The initial commit of iSCSI-SCST
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Wed, 15 Aug 2007 09:28:54 +0000 (09:28 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Wed, 15 Aug 2007 09:28:54 +0000 (09:28 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@162 d57e44dd-8a1f-0410-8b47-8ef2f437770f

60 files changed:
Makefile
iscsi-scst/COPYING [new file with mode: 0644]
iscsi-scst/ChangeLog [new file with mode: 0644]
iscsi-scst/ChangeLog-IET [new file with mode: 0644]
iscsi-scst/Makefile [new file with mode: 0644]
iscsi-scst/README [new file with mode: 0644]
iscsi-scst/README-IET [new file with mode: 0644]
iscsi-scst/ToDo [new file with mode: 0644]
iscsi-scst/doc/manpages/iscsi-scst-adm.8 [new file with mode: 0644]
iscsi-scst/doc/manpages/iscsi-scstd.8 [new file with mode: 0644]
iscsi-scst/doc/manpages/iscsi-scstd.conf.5 [new file with mode: 0644]
iscsi-scst/etc/initd/initd [new file with mode: 0644]
iscsi-scst/etc/initd/initd.debian [new file with mode: 0644]
iscsi-scst/etc/initd/initd.gentoo [new file with mode: 0644]
iscsi-scst/etc/initd/initd.redhat [new file with mode: 0644]
iscsi-scst/etc/initiators.allow [new file with mode: 0644]
iscsi-scst/etc/initiators.deny [new file with mode: 0644]
iscsi-scst/etc/iscsi-scstd.conf [new file with mode: 0644]
iscsi-scst/include/iscsi_u.h [new file with mode: 0644]
iscsi-scst/kernel/Makefile [new file with mode: 0644]
iscsi-scst/kernel/config.c [new file with mode: 0644]
iscsi-scst/kernel/conn.c [new file with mode: 0644]
iscsi-scst/kernel/digest.c [new file with mode: 0644]
iscsi-scst/kernel/digest.h [new file with mode: 0644]
iscsi-scst/kernel/event.c [new file with mode: 0644]
iscsi-scst/kernel/iscsi.c [new file with mode: 0644]
iscsi-scst/kernel/iscsi.h [new file with mode: 0644]
iscsi-scst/kernel/iscsi_dbg.h [new file with mode: 0644]
iscsi-scst/kernel/iscsi_hdr.h [new file with mode: 0644]
iscsi-scst/kernel/nthread.c [new file with mode: 0644]
iscsi-scst/kernel/param.c [new file with mode: 0644]
iscsi-scst/kernel/patches/put_page_callback-2.6.16.patch [new file with mode: 0644]
iscsi-scst/kernel/patches/put_page_callback-2.6.18.patch [new file with mode: 0644]
iscsi-scst/kernel/patches/put_page_callback-2.6.21.patch [new file with mode: 0644]
iscsi-scst/kernel/patches/put_page_callback-2.6.22.patch [new file with mode: 0644]
iscsi-scst/kernel/session.c [new file with mode: 0644]
iscsi-scst/kernel/target.c [new file with mode: 0644]
iscsi-scst/usr/Makefile [new file with mode: 0644]
iscsi-scst/usr/chap.c [new file with mode: 0644]
iscsi-scst/usr/config.h [new file with mode: 0644]
iscsi-scst/usr/conn.c [new file with mode: 0644]
iscsi-scst/usr/ctldev.c [new file with mode: 0644]
iscsi-scst/usr/event.c [new file with mode: 0644]
iscsi-scst/usr/iscsi_adm.c [new file with mode: 0644]
iscsi-scst/usr/iscsi_adm.h [new file with mode: 0644]
iscsi-scst/usr/iscsi_hdr.h [new file with mode: 0644]
iscsi-scst/usr/iscsi_scstd.c [new file with mode: 0644]
iscsi-scst/usr/iscsid.c [new file with mode: 0644]
iscsi-scst/usr/iscsid.h [new file with mode: 0644]
iscsi-scst/usr/isns.c [new file with mode: 0644]
iscsi-scst/usr/isns_proto.h [new file with mode: 0644]
iscsi-scst/usr/log.c [new file with mode: 0644]
iscsi-scst/usr/message.c [new file with mode: 0644]
iscsi-scst/usr/misc.h [new file with mode: 0644]
iscsi-scst/usr/param.c [new file with mode: 0644]
iscsi-scst/usr/param.h [new file with mode: 0644]
iscsi-scst/usr/plain.c [new file with mode: 0644]
iscsi-scst/usr/session.c [new file with mode: 0644]
iscsi-scst/usr/target.c [new file with mode: 0644]
iscsi-scst/usr/types.h [new file with mode: 0644]

index a2d1d8e..ef113ea 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,22 +24,28 @@ QLA_DIR=qla2x00t/qla2x00-target
 LSI_DIR=mpt
 USR_DIR=usr/fileio
 
+ISCSI_DIR=iscsi-scst
+#ISCSI_DISTDIR=../../../../iscsi_scst_inst
+
 all:
        cd $(SCST_DIR) && $(MAKE) $@
        @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
 #      @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
+       @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
        @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
 
 install: 
        cd $(SCST_DIR) && $(MAKE) $@
        @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
 #      @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
+       @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) DISTDIR=$(ISCSI_DISTDIR) $@; fi
        @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
 
 uninstall: 
        cd $(SCST_DIR) && $(MAKE) $@
        @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
        @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
+       @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
        @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
 
 clean: 
@@ -47,6 +53,7 @@ clean:
        @if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi
        @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
        @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
+       @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
        @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
 
 extraclean: 
@@ -54,6 +61,7 @@ extraclean:
        @if [ -d $(QLA_INI_DIR) ]; then cd $(QLA_INI_DIR) && $(MAKE) $@; fi
        @if [ -d $(QLA_DIR) ]; then cd $(QLA_DIR) && $(MAKE) $@; fi
        @if [ -d $(LSI_DIR) ]; then cd $(LSI_DIR) && $(MAKE) $@; fi
+       @if [ -d $(ISCSI_DIR) ]; then cd $(ISCSI_DIR) && $(MAKE) $@; fi
        @if [ -d $(USR_DIR) ]; then cd $(USR_DIR) && $(MAKE) $@; fi
 
 scst: 
@@ -88,6 +96,21 @@ qla_extraclean:
        cd $(QLA_INI_DIR)/.. && $(MAKE) extraclean
        cd $(QLA_DIR) && $(MAKE) extraclean
 
+iscsi:
+       cd $(ISCSI_DIR) && $(MAKE)
+
+iscsi_install:
+       cd $(ISCSI_DIR) && $(MAKE) install
+
+iscsi_uninstall:
+       cd $(ISCSI_DIR) && $(MAKE) uninstall
+
+iscsi_clean: 
+       cd $(ISCSI_DIR) && $(MAKE) clean
+
+iscsi_extraclean:
+       cd $(ISCSI_DIR) && $(MAKE) extraclean
+
 lsi:
        cd $(LSI_DIR) && $(MAKE)
 
@@ -137,6 +160,12 @@ help:
        @echo "         qla_install     : 2.6 qla target: install"
        @echo "         qla_uninstall   : 2.6 qla target: uninstall"
        @echo ""
+       @echo "         iscsi             : make new ISCSI target"
+       @echo "         iscsi_clean       : ISCSI target: clean "
+       @echo "         iscsi_extraclean  : ISCSI target: clean + clean dependencies"
+       @echo "         iscsi_install     : ISCSI target: install"
+       @echo "         iscsi_uninstall   : ISCSI target: uninstall"
+       @echo ""
        @echo "         lsi             : make lsi target"
        @echo "         lsi_clean       : lsi target: clean "
        @echo "         lsi_extraclean  : lsi target: clean + clean dependencies"
@@ -154,5 +183,6 @@ help:
 .PHONY: all install uninstall clean extraclean help \
        qla qla_install qla_uninstall qla_clean qla_extraclean \
        lsi lsi_install lsi_uninstall lsi_clean lsi_extraclean \
+       iscsi iscsi_install iscsi_uninstall iscsi_clean iscsi_extraclean \
        scst scst_install scst_uninstall scst_clean scst_extraclean \
        usr usr_install usr_uninstall usr_clean usr_extraclean
diff --git a/iscsi-scst/COPYING b/iscsi-scst/COPYING
new file mode 100644 (file)
index 0000000..afd5a94
--- /dev/null
@@ -0,0 +1,341 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/iscsi-scst/ChangeLog b/iscsi-scst/ChangeLog
new file mode 100644 (file)
index 0000000..70f5ba1
--- /dev/null
@@ -0,0 +1,38 @@
+Summary of changes in iSCSI-SCST since it was IET
+-------------------------------------------------
+
+ - Commands processing flow changed to work with SCST. Significant
+   cleanup was done. Modules and config files were renamed to allow to
+   run with IET on the same host.
+
+ - In IET the iSCSI negotiation isn't fully iSCSI RFC confirmed: it doesn't
+   support ranges of values and, more important, violates RFC in case
+   when in the IET config file some value for some parameter is set and
+   a remote initiator doesn't initiate the negotiation for this
+   parameter or declare its value. According to RFC, in this case IET
+   shall use the RFC-specified default value, but it uses config file
+   specified one instead. Looks like the implementation confuses IET
+   config file default values and iSCSI RFC ones. The default values
+   handling was fixed. But support for ranges remains unfixed (see ToDo
+   file).
+
+ - All shutdown/restart problems (resource leaks, hangups, etc.), especially
+   under load, were fixed.
+
+ - Full duplex network IO implemented.
+
+ - Threading model reimplemented.
+
+ - Digests calculation made multi-threaded.
+
+ - TX data digest errors handling iSCSI RFC violation was fixed. Another one
+   (SNACK handling) remains unfixed.
+
+ - Ability to send data with sense in a single response added.
+
+ - Ability to handle initiators misbehavior a bit improved: several BUG()'s
+   were relaced by the proper handling. Hovewer, there is a plenty of work in
+   this area left, IET is known to have a lot of weaknesses here.
+
+ - A lot of other bugfixes and code cleanups.
+
diff --git a/iscsi-scst/ChangeLog-IET b/iscsi-scst/ChangeLog-IET
new file mode 100644 (file)
index 0000000..a982743
--- /dev/null
@@ -0,0 +1,435 @@
+Summary of changes from v0.4.14 to v0.4.15
+=================================
+
+Juhani Rautiainen
+ o Add RELEASE/RESERVE support
+
+Ross S. W. Walker
+ o Improve the build system to support several kernel versions
+ o Add block-io support
+
+
+Summary of changes from v0.4.13 to v0.4.14
+=================================
+
+Arne Redlich
+  o Kill unused "state" in struct iscsi_cmnd.
+  o Fixed fileio_sync() to propagate error to the caller (and initiator).
+  o Don't attempt to show target/session params if communication with ietd
+  fails.
+  o Fixes to ietadm parameters handling.
+
+FUJITA Tomonori
+  o rewritten iSNS code, many iSNS fixes.
+  o added iSNS SCN support.
+  o IPv6 fixes to userspace.
+  
+Ming Zhang
+  o Fix the READ_* commands error handling bug.
+  o fix the mode sense response.
+  o wrong #endif sequence in misc.h
+
+Richard Bollinger
+  o add a patch to ietd.c that allows defunct sessions to go away.
+  o add write-back cache and read-only support.
+
+Frederic Temporelli
+  o Fix for the combination of 32-bit userland and 64-bit kernel on mips.
+
+Henry Liu
+  o corrected many task management functions, prevent crashing on
+  LUN RESET, TARGET WARM RESET.
+
+K Chapman
+  o Fixed a typo in check_segment_length().
+
+Emmanuel Florac
+  o Add ietadm manpage.
+
+
+Summary of changes from v0.4.12 to v0.4.13
+=================================
+Arne Redlich
+  o patch to avoid digest calculation for PDUs whose data has been skipped 
+  already for various reasons.
+  o Correct a bug managing non-default MaxRxDSL.
+  o added to ietadm ability to show target parameters.
+  o add on the workaround to AIX initiator MaxCmdSN bug.
+
+FUJITA Tomonori
+  o added to ietadm ability to show the iSCSI parameters for an established 
+  session.
+
+Ming Zhang
+  o Fixed this bug : ietd should manage the iscsi name in a case insensitive
+  way to conform to the RFC.
+  o workaround to AIX initiator MaxCmdSN bug.
+  o Fixed socket() return value judgment.
+  
+Bastiaan Bakker
+  o add 'condrestart' command to the RedHat initscript.
+
+Robert Whitehead
+  o correct the bug that prevents iet to start if there isn't 
+  an /etc/ietd.conf file.
+  
+Summary of changes from v0.4.11 to v0.4.12
+=================================
+
+Arne Redlich
+  o Fix MaxRecvDataSegmentLength handling.
+  o Fix login parameter handling.
+  o Update man pages.
+
+Bastiaan Bakker
+  o Add features to specify the listen address and port.
+  o Fix setuid and setgid bugs in ietd daemon.
+
+FUJITA Tomonori
+  o Add IPv6 support.
+
+Junjiro Okajima
+  o Fix a bug about getting parameters from kernel space.
+
+Krzysztof Blaszkowski
+  o Fix for requests with unaligned to 4 length.
+
+
+Summary of changes from v0.4.10 to v0.4.11
+=================================
+
+FUJITA Tomonori
+  o Fix Task Management Function bugs.
+
+Ming Zhang
+  o Update man pages.
+
+
+Summary of changes from v0.4.9 to v0.4.10
+=================================
+
+Arne Redlich
+  o Fix 0x83 inquiry output.
+  o Fix iSCSI parameter handling bugs.
+
+FUJITA Tomonori
+  o Add the access control based on initiator address
+    and target name patterns.
+
+Junjiro Okajima
+  o Fix parameter checking bugs.
+
+Ming Zhang
+  o Add the nullio mode (only useful for performance evaluation).
+
+
+Summary of changes from v0.4.8 to v0.4.9
+=================================
+
+FUJITA Tomonori
+  o Fix parameter negotiation handling bugs.
+
+Wang Zhenyu
+  o Fix digest negotiation handling bugs.
+
+
+Summary of changes from v0.4.7 to v0.4.8
+=================================
+
+FUJITA Tomonori
+  o Fix unsolicited data handling bugs.
+  o Rewrite parameter handling code.
+  o Rewrite ietadm tool.
+  o Improve dynamic configuration support.
+  o Cleanups on the kernel-user interface.
+  o Improve wrong PDU handling.
+  o Implement a framework to handle multiple configuration methods.
+  o Implement basic access control support.
+
+
+Summary of changes from v0.4.6 to v0.4.7
+=================================
+
+Florian Zierer
+  o Add the startup script for Gentoo.
+
+FUJITA Tomonori
+  o Rewrite parameter handling code.
+  o Fix task management code bug.
+  o Fix 0x83 inquiry output (Thanks to Christophe Varoqui).
+
+Ming Zhang
+  o Acquire T10 ID.
+  o Fix parameter handling bugs.
+  o Some user-space cleanups.
+
+Philipp Hug
+  o Fix ietd.8 man typo.
+
+
+Summary of changes from v0.4.5 to v0.4.6
+=================================
+
+FUJITA Tomonori
+  o Replace the makeshift event notification code with netlink.
+  o Add task management code except for ACA and reassign stuff.
+  o Fix r2t lun bug (Thanks to Ming Zhang).
+
+
+Summary of changes from v0.4.4 to v0.4.5
+=================================
+
+FUJITA Tomonori
+  o Rewrite the iSCSI command handling code.
+  o Rewrite the I/O data handling code.
+  o Fix worker thread.
+  o Several cleanups.
+
+
+Summary of changes from v0.4.3 to v0.4.4
+=================================
+
+Krzysztof Blaszkowski
+  o Fix an out-of-memory bug.
+
+
+Summary of changes from v0.4.2 to v0.4.3
+=================================
+
+Arne Redlich
+  o Fix header digest bug.
+  o Fix unexpected closed connection bug.
+  o Fix iSCSI parameter bug.
+
+FUJITA Tomonori
+  o Fix network thread.
+
+
+Summary of changes from v0.4.1 to v0.4.2
+=================================
+
+FUJITA Tomonori
+  o Fix network thread.
+  o Fix MaxOutstandingR2T handling.
+
+Ming Zhang
+  o Add large volume support (over 2TB).
+
+
+Summary of changes from v0.4.0 to v0.4.1
+=================================
+
+Arne Redlich
+  o Add mutual CHAP support. Note that you need to replace "User"
+    with "IncomingUser" in ietd.conf.
+
+FUJITA Tomonori
+  o Fix InitialR2T=No support.
+  o Fix INQUIRY command handling.
+  o Fix network and worker thread.
+  o Start to split SCSI stuff.
+  o Rewrite the R2T handling code.
+  o Several cleanups.
+
+
+Summary of changes from v0.3.8 to v0.4.0
+=================================
+       
+Arne Redlich
+  o iSNS bug fix.
+
+FUJITA Tomonori
+  o Move to 2.6 kernels.
+  o Rewrite the kernel thread performing network I/O.
+  o Add header and data digests (Thanks to Arne Redlich).
+
+Ming Zhang
+  o Add mode sense page 0x3 and 0x4 support (Thanks to K Chapman).
+  o iSNS bug fix.
+
+
+Summary of changes from v0.3.7 to v0.3.8
+=================================
+       
+Arne Redlich
+  o Fix ietadm global option bug.
+
+FUJITA Tomonori
+  o Fix TCP option bugs (Thanks to Chuck Berg).
+  o Fix REPORT LUN (handling lots of LUs).
+
+
+Summary of changes from v0.3.6 to v0.3.7
+=================================
+       
+Arne Redlich
+  o Fix target_alloc_pages().
+
+FUJITA Tomonori
+  o Fix REPORT LUN bug.
+
+
+Summary of changes from v0.3.5 to v0.3.6
+=================================
+       
+Arne Redlich
+  o Fix bugs about rejecting PDUs.
+
+FUJITA Tomonori
+  o Cleanups on target_cmnd structure.
+  o Kill highmem stuff.
+  o Fix REPORT LUN (handling lots of LUs).
+
+
+Summary of changes from v0.3.4 to v0.3.5
+=================================
+       
+Arne Redlich
+  o Fix ietd security hole.
+  o Fix REPORT LUN bug.
+  o FIX NOOP_OUT padding bug.
+
+FUJITA Tomonori
+  o Rewrite event notification code.
+
+Libor Vanek
+  o Add max_sessions option.
+  o Fix command parsing bug.
+
+Ming Zhang
+  o Cleanups for 64-bit architectures.
+
+
+Summary of changes from v0.3.3 to v0.3.4
+=================================
+
+FUJITA Tomonori
+  o Improve dynamic configuration support (adding targets and users).
+
+
+Summary of changes from v0.3.2 to v0.3.3
+=================================
+
+FUJITA Tomonori
+  o Fix Makefile for the startup script.
+
+
+Summary of changes from v0.3.1 to v0.3.2
+=================================
+
+Ali Lehmann
+  o Add a new startup script for Debian.
+
+FUJITA Tomonori
+  o Fix the istd's handling of connections in out-of-memory situations.
+  o Fix bugs in regular file support.
+  o Fix `ietadm --mode del all`.
+
+Libor Vanek
+  o Add a new startup script for RedHat.
+
+Ming Zhang
+  o Add uid/gid option to ietd daemon.
+  o Fix a access freed-memory bug in kernel/daemon.c.
+
+
+Summary of changes from v0.3.0 to v0.3.1
+=================================
+
+FUJITA Tomonori
+  o Fix memory leaks in ietd daemon (Thanks to Ming).
+  o Fix bugs about REPORT_LUNS commands (Thanks to Ming).
+  o Fix a bug about Target Task Tag.
+  o Add regular file support to fileio mode.
+
+
+Summary of changes from v0.2.6 to v0.3.0
+=================================
+
+Ali Lehmann
+  o Update ietd.8 man page.
+
+FUJITA Tomonori
+  o Fix shutdown code.
+  o Fix istd kernel thread bugs.
+  o Replace procfs interface with ioctl.
+  o Add dynamic configuration support.
+  o Update README and the boot script.
+
+Ming Zhang
+  o Add config option to ietd daemon.
+
+
+Summary of changes from v0.2.5 to v0.2.6
+=================================
+
+Ali Lehmann
+  o Add ietd.8 and ietd.conf.5 man pages.
+
+FUJITA Tomonori
+  o Update README, Makefile, and the boot script.
+
+
+Summary of changes from v0.2.4 to v0.2.5
+=================================
+
+FUJITA Tomonori
+  o Update README.
+
+
+Summary of changes from v0.2.3 to v0.2.4
+=================================
+
+Ming Zhang
+  o Add a preliminary iSNS support.
+  o Fix merge mistakes that I made at the previous release.
+
+
+Summary of changes from v0.2.2 to v0.2.3
+=================================
+
+Ming Zhang
+  o Improve INQUIRY, REQUEST_SENSE, and MODE_SENSE command supports
+  o Add fake RESERVE* and RELEASE* command supports
+
+
+Summary of changes from v0.2.1 to v0.2.2
+=================================
+
+FUJITA Tomonori
+  o Improve the write performance of the file IO mode
+
+Ming Zhang
+  o Fix unupdated pg_cnt when allocating a new tcmnd
+  o Several cleanups
+
+
+Summary of changes from v0.2.0 to v0.2.1
+=================================
+
+FUJITA Tomonori
+  o Fix a bug that makes the target use CPU unnecessarily
+  o Add a feature that enable you to pass options to an IO mode
+
+
+Summary of changes from v0.1.0 to v0.2.0
+=================================
+
+FUJITA Tomonori
+  o Rewrite read and write kernel threads which perform network IO
+  o Fix race issues in the proc interface
+  o Fix shutdown code
+
+Ming Zhang
+  o Fix memory leaks in file and block IO modes
+
+
+Summary of changes from the ardis v20040211 to v0.1.0
+=================================
+
+FUJITA Tomonori
+  o Remove a kernel patch. Multiple threads execute I/O operations
+  o Replace IO functions with the kernel starndard functions
+  o Add multiple IO modes feature
+  o Fix several race issues
+  o Fix several out-of-memory situation bugs
diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile
new file mode 100644 (file)
index 0000000..0053745
--- /dev/null
@@ -0,0 +1,76 @@
+#
+# Makefile for the Linux kernel device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+SCST_DIR := $(shell pwd)/../scst/src
+SUBDIRS := $(shell pwd)
+
+ifeq ($(KVER),)
+  ifeq ($(KDIR),)
+    KVER = $(shell uname -r)
+    KDIR ?= /lib/modules/$(KVER)/build
+  else
+    KVER = $$KERNELRELEASE
+  endif
+else
+  KDIR ?= /lib/modules/$(KVER)/build
+endif
+
+all: progs mods
+
+mods: Modules.symvers Module.symvers
+       $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/kernel modules
+
+progs:
+       $(MAKE) -C usr
+
+install: all kernel/iscsi-scst.ko usr/iscsi-scstd usr/iscsi-scst-adm
+       @install -vD usr/iscsi-scstd $(DISTDIR)/usr/local/sbin/iscsi-scstd
+       @install -vD usr/iscsi-scst-adm $(DISTDIR)/usr/local/sbin/iscsi-scst-adm
+       if [ -f /etc/debian_version ]; then \
+               install -vD -m 755 etc/initd/initd.debian $(DISTDIR)/etc/init.d/iscsi-scst; \
+       elif [ -f /etc/redhat-release ]; then \
+               install -vD -m 755 etc/initd/initd.redhat $(DISTDIR)/etc/init.d/iscsi-scst; \
+       elif [ -f /etc/gentoo-release ]; then \
+               install -vD -m 755 etc/initd/initd.gentoo $(DISTDIR)/etc/init.d/iscsi-scst; \
+       elif [ -f /etc/slackware-version ]; then \
+               install -vD -m 755 etc/initd/initd $(DISTDIR)/etc/rc.d/iscsi-scst; \
+       else \
+               install -vD -m 755 etc/initd/initd $(DISTDIR)/etc/init.d/iscsi-scst; \
+       fi
+       @eval `sed -n 's/#define UTS_RELEASE /KERNELRELEASE=/p' $(KDIR)/include/linux/version.h $(KDIR)/include/linux/utsrelease.h 2>/dev/null`; \
+       install -vD -m 644 kernel/iscsi-scst.ko \
+       $(DISTDIR)$(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra/iscsi-scst.ko
+       -/sbin/depmod -aq $(KVER)
+
+SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null)
+ifneq ($(SCST_MOD_VERS),)
+Modules.symvers: $(SCST_DIR)/Modules.symvers
+       echo $(SCST_MOD_VERS)
+       cp $(SCST_DIR)/Modules.symvers kernel/
+else
+.PHONY: Modules.symvers
+endif
+
+# It's renamed in 2.6.18
+SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Module.symvers 2>/dev/null)
+ifneq ($(SCST_MOD_VERS),)
+Module.symvers: $(SCST_DIR)/Module.symvers
+       cp $(SCST_DIR)/Module.symvers kernel/
+else
+.PHONY: Module.symvers
+endif
+
+clean:
+       $(MAKE) -C usr clean
+       $(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd)/kernel clean
+       rm -f kernel/Modules.symvers kernel/Module.symvers
+
+extraclean: clean
+
+.PHONY: all mods progs install clean extraclean
diff --git a/iscsi-scst/README b/iscsi-scst/README
new file mode 100644 (file)
index 0000000..dffa1d8
--- /dev/null
@@ -0,0 +1,115 @@
+iSCSI SCST target driver
+========================
+
+Version 0.9.6/XXXX, XX XXX 200X
+-------------------------------
+
+This driver is a forked with all respects version of iSCSI Enterprise
+Target (IET) (http://iscsitarget.sourceforge.net/) with updates to work
+over SCST as well as with many improvements and bugfixes (see ChangeLog
+file). The reason of fork is that the necessary changes are intrusive
+and with the current IET merge policy, where only simple bugfix-like
+patches, which doesn't touch the core code, could be merged, it is very
+unlikely that they will be merged in the main IET trunk.
+
+To let it be installed and work at the same host together with IET
+simultaneously all the driver's modules and files were renamed:
+
+ * ietd.conf -> iscsi-scstd.conf
+ * ietadm -> iscsi-scst-adm
+ * ietd -> iscsi-scstd
+ * iscsi-target -> iscsi-scst
+ * iscsi-target.ko -> iscsi-scst.ko
+
+This version is compatible with SCST version 0.9.6 and higher.
+
+Tested on 2.6.21.1 kernel, but it should also work on other versions,
+starting from 2.6.16.x.
+
+Installation
+------------
+
+Basically as in README-IET, where file names are changed as specified
+above.
+
+To use full power of TCP zero-copy transmit functions, especially
+dealing with user space supplied via scst_user module memory, iSCSI-SCST
+needs to be notified when Linux networking finished data transmission.
+Patch put_page_callback.patch provides such functionality. The
+corresponding version of it should be applied on your kernel. This is
+highly recommended, but not required. If it isn't applied, iSCSI-SCST
+will work in the performance degraded mode, when for data transmission:
+
+ - For in-kernel allocated memory (scst_vdisk and pass-through
+   handlers) usage of SGV cache on transmit path (READ-type commands)
+   will be disabled. The performance hit will be not big, performance
+   will still remain better, than for IET, because SGV cache will remain
+   used on receive path and IET doesn't have such feature.
+
+ - For user space allocated memory (scst_user handler) all transmitted
+   data will be additionally copied into temporary TCP buffers. The
+   performance hit will be quite noticiable.
+  
+If you need your own version of put_page_callback.patch for your custom
+kernel, for which there is no prepared version, you can create it
+yourself. For that it is only necessary:
+
+1. Apply the closest version of put_page_callback.patch on your kernel.
+Resolve only failed hanks from include/ and net/core/utils.c, ignore
+other failures.
+
+2. Grep net/ and replace everywhere, except in net/sunrpc/svc.c and
+net/core/pktgen.c, put_page() by net_put_page() and get_page() by
+net_get_page().
+
+That's all.
+
+Usage
+-----
+
+ISCSI parameters like iSNS, CHAP and target parameters are configured in
+iscsi-scstd.conf. All LUN information is configured using the regular
+SCST interface. The LUN information in iscsi-scstd.conf will be ignored.
+This is because now responsibilities are divided (as it should be)
+between the target driver (iSCSI-SCST) and the SCST core. The target
+driver is responsible for handling targets and their parameters, SCST
+core is responsible for handling backstorage.
+
+If you need to configure different LUs for different targets you should
+create for each target group "Default_target_name", where target name
+means name of the target, for example:
+"Default_iqn.2007-05.com.example:storage.disk1.sys1.xyz", and add there
+all necessary LUNs. Check SCST README file for details.
+
+Compilation options
+-------------------
+
+There are the following compilation options, that could be commented
+in/out in the kernel's module Makefile:
+
+ - DEBUG - turns on some debugging code, including some logging. Makes
+   the driver considerably bigger and slower, producing large amount of
+   log data.
+
+ - TRACING - turns on ability to log events. Makes the driver considerably
+   bigger and lead to some performance loss.
+
+ - EXTRACHECKS - adds extra validity checks in the various places.
+
+ - DEBUG_DIGEST_FAILURES - simulates digest failures in random places.
+
+Credits
+-------
+
+Thanks to:
+
+ * IET developers for IET
+
+ * Ming Zhang <blackmagic02881@gmail.com> for fixes
+
+ * Krzysztof Blaszkowski <kb@sysmikro.com.pl> for many fixes
+
+ * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> for comments and help in
+   debugging
+Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
diff --git a/iscsi-scst/README-IET b/iscsi-scst/README-IET
new file mode 100644 (file)
index 0000000..11009ab
--- /dev/null
@@ -0,0 +1,105 @@
+Introduction
+-------------
+iSCSI Enterprise Target is for building an iSCSI storage system on
+Linux. It is aimed at developing an iSCSI target satisfying enterprise
+requirements.
+
+We borrow code from an Ardis iSCSI target (with respect to the GPL).
+
+
+Installation
+-------------
+The iSCSI target requires a host running the Linux operating system
+with a kernel version of 2.6.19 (2.6.14 - 2.6.18 kernels using
+backward compatibility patches, see below) or newer. You need to
+enable "Cryptographic API" under "Cryptographic options" in the kernel
+config.  You also need to enable "CRC32c CRC algorithm" if you use
+header or data digests. They are the kernel options, CONFIG_CRYPTO and
+CONFIG_CRYPTO_CRC32C, respectively. The user-space code requires
+OpenSSL library (http://www.openssl.org/).
+
+The iSCSI target consists of kernel modules and a daemon. Since IET
+is generally targetted at the latest stable mainline kernel, you might
+need to apply a backward compatibility patch to compile it against
+older kernel versions:
+
+       patch -p0 < patches/compat-2.6.14-2.6.18.patch
+
+Compilation of the kernel modules require the path to the kernel
+sources:
+
+       make KSRC=<kernel-src>
+
+The path can also be set by editing the main Makefile. If KSRC is
+omitted, make program will try to locate the kernel sources for
+current running kernel.  Be sure to check whether it finds the right
+kernel sources.
+
+This will build the modules, the daemon, and the control tool. To
+install both, use:
+
+       make KSRC=<kernel-src> install
+
+The kernel modules will be install in the module directory of the
+kernel. The daemon and the control tool will be installed as ietd and
+ietadm under /usr/sbin. The boot script will be installed as
+iscsi-targt under /etc/init.d.
+
+If you use Linux distribution that does not have /etc/init.d, the
+boot script will not be installed. So you need to install it to an
+appropriate directory manually.
+
+
+Configuration
+-------------
+The daemon is configured via the configuration file /etc/ietd.conf.
+See the man page and the example file for the current syntax.
+
+The ietadm utility is for managing IET software dynamically. You can
+change the configurations of running targets. See the help message.
+
+The access control based on initiator address and target name patterns
+is configured via two configuration files (/etc/initiators.allow and
+/etc/initiators.deny). These files work like tcpd files
+(/etc/hosts.allow and /etc/hosts.deny). This feature enables you to
+hide a particular targets from some initiators. See the example files
+for the supported expressions. You can change the configuration
+dynamically. The modifications to the files become effective
+immediately.
+
+
+Starting
+-------------
+The target is not started automatically. So execute:
+
+       /etc/init.d/iscsi-target start
+
+Note that you must edit the configuration file before starting the
+target.
+
+
+Stopping
+-------------
+Execute:
+
+       /etc/init.d/iscsi-target stop
+
+Contact
+-------------
+Please send bug reports, comments, feature requests etc. to our
+mailing list <iscsitarget-devel@lists.sourceforge.net>.
+
+
+Developer Notes
+----------------
+The central resource for IET development is the
+<iscsitarget-devel@lists.sourceforge.net> mailing list.
+
+Our subversion repository can be found at: svn://svn.berlios.de/iscsitarget
+
+When submitting patches, please diff against the code in our repository's
+trunk and adhere otherwise to the same rules that apply to Linux kernel
+development, in particular the Linux kernel coding style
+($KSRC/Documentation/CodingStyle) and the rules for submitting patches
+($KSRC/Documentation/SubmittingPatches), i.e. please send patches inline as
+plain text.
diff --git a/iscsi-scst/ToDo b/iscsi-scst/ToDo
new file mode 100644 (file)
index 0000000..7b5fa12
--- /dev/null
@@ -0,0 +1,32 @@
+ - Reimplement sessions parameters negotiation and storage. In IET
+   the negotiation isn't iSCSI RFC confirmed: it doesn't support ranges
+   of values and, more important, violates RFC in case when in the IET
+   config file some value for some parameter is set and a remote
+   initiator doesn't initiate the negotiation for this parameter or
+   declare its value. According to RFC, in this case IET shall use the
+   RFC-specified default value, but it will use config file specified
+   one instead. Looks like the implementation confuses IET config file
+   default values and iSCSI ones. The default values handling was fixed
+   in very dirty and hackish way, but ranges support remains unfixed.
+   Storage of set by user parameters should be reimplemented, so they
+   will be kept in iscsi-scstd, not in the kernel, as it is currently
+   done in IET's code. Using kernel to *only* store parameters is quite
+   questionable decision, especially considering that it leads to some
+   code duplication between kernel and user space, so remove all
+   parameters storage code from kernel. Remove target_param, leave only
+   one type of parameters, but separate processing of iSCSI RFC
+   parameters from local ones (currently they are mixed up). Also mixing
+   up conceptions of "key" and "param" in param_*() and struct
+   iscsi_key_ops functions makes the code hardly manageable, particularly
+   for adding support for ranges in the negotiation keys, so it needs to
+   be fixed as well.
+
+ - Fix SNACK command handling. Currently it violates iSCSI RFC.
+
+ - Consider better integration with TCP internals on receive path to
+   improve performance.
+
+ - The target shouldn't crash/hang/etc. on initiators' misbehavior as
+   IET likes to do.
+
+ - Minor "ToDo"'s spread in the code.
diff --git a/iscsi-scst/doc/manpages/iscsi-scst-adm.8 b/iscsi-scst/doc/manpages/iscsi-scst-adm.8
new file mode 100644 (file)
index 0000000..4d30f85
--- /dev/null
@@ -0,0 +1,240 @@
+.\" Automatically generated by Pod::Man 2.09 (Pod::Simple 3.04)
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings.  \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote.  | will give a
+.\" real vertical bar.  \*(C+ will give a nicer C++.  Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available.  \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+.    ds -- \(*W-
+.    ds PI pi
+.    if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.    if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\"  diablo 12 pitch
+.    ds L" ""
+.    ds R" ""
+.    ds C` ""
+.    ds C' ""
+'br\}
+.el\{\
+.    ds -- \|\(em\|
+.    ds PI \(*p
+.    ds L" ``
+.    ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD.  Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+.    de IX
+.    tm Index:\\$1\t\\n%\t"\\$2"
+..
+.    nr % 0
+.    rr F
+.\}
+.\"
+.\" For nroff, turn off justification.  Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear.  Run.  Save yourself.  No user-serviceable parts.
+.    \" fudge factors for nroff and troff
+.if n \{\
+.    ds #H 0
+.    ds #V .8m
+.    ds #F .3m
+.    ds #[ \f1
+.    ds #] \fP
+.\}
+.if t \{\
+.    ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+.    ds #V .6m
+.    ds #F 0
+.    ds #[ \&
+.    ds #] \&
+.\}
+.    \" simple accents for nroff and troff
+.if n \{\
+.    ds ' \&
+.    ds ` \&
+.    ds ^ \&
+.    ds , \&
+.    ds ~ ~
+.    ds /
+.\}
+.if t \{\
+.    ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+.    ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+.    ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+.    ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+.    ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+.    ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+.    \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+.    \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+.    \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+.    ds : e
+.    ds 8 ss
+.    ds o a
+.    ds d- d\h'-1'\(ga
+.    ds D- D\h'-1'\(hy
+.    ds th \o'bp'
+.    ds Th \o'LP'
+.    ds ae ae
+.    ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "ISCSI_SCST_ADM 1"
+.TH ISCSI_SCST_ADM 8 "2007-05" "iSCSI SCST Target admin" "User Manuals"
+.SH "NAME"
+iscsi-scst-adm \- iSCSI SCST Target Administration Utility.
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBiscsi-scst-adm \-\-op [operation] \-\-tid=[id] [\-\-sid [id]] [\-\-params [key=value,...]]\fR
+.SH "DESCRIPTION"
+.IX Header "DESCRIPTION"
+iscsi-scst-adm is used to monitor and modify in real-time the iSCSI SCST Target 
+targets.
+.SH "USAGE"
+.IX Header "USAGE"
+You'll have to get target and sessions ids from /proc/scsi_tgt/iscsi.
+Some usage examples can be :
+.PP
+\&\fBiscsi-scst-adm \-\-op show \-\-tid=1\fR
+.PP
+display status of target 1 (see /proc/scsi_tgt/iscsi to get the matching target name)
+.PP
+\&\fBiscsi-scst-adm \-\-op new \-\-tid=2\fR
+.PP
+create dynamically a new target, numbered 2. \s-1CAUTION\s0 : the target will disappear if you restart iscsi-scstd, you'll have to edit /etc/iscsi-scstd.conf to make it permanent!
+.SH "ERROR MESSAGES"
+.IX Header "ERROR MESSAGES"
+iscsi-scst-adm misses error messages. Look carefully the \s-1STDERR\s0 output : in case of error
+it will send a 3 number error code, ending with \-1, for instance :
+.PP
+iscsi-scstd_request 203 3 \-1
+.SH "OPTIONS"
+.IX Header "OPTIONS"
+\&\fB\-\-op new \-\-tid=[id] \-\-params Name=[name]\fR
+.PP
+add a new target with [id]. [id] must not be zero.
+.PP
+\&\fB\-\-op delete \-\-tid=[id]\fR
+.PP
+delete specific target with [id]. The target must
+have no active sessions.
+.PP
+\&\fB\-\-op show \-\-tid=[id]\fR
+.PP
+show target parameters of target with [id].
+.PP
+\&\fB\-\-op show \-\-tid=[id] \-\-sid=[sid]\fR
+.PP
+show iSCSI parameters in effect for session [sid]. If
+[sid] is \*(L"0\*(R" (zero), the configured parameters
+will be displayed.
+.PP
+\&\fB\-\-op delete \-\-tid=[id] \-\-sid=[sid] \-\-cid=[cid]\fR
+.PP
+delete specific connection with [cid] in a session
+with [sid] that the target with [id] has.
+If the session has no connections after
+the operation, the session will be deleted
+automatically.
+.PP
+\&\fB\-\-op delete\fR
+.PP
+stop all activity.
+.PP
+\&\fB\-\-op update \-\-tid=[id] \-\-params=key1=value1,key2=value2,...\fR
+.PP
+change iSCSI \s-1SCST\s0 target parameters of specific
+target with [id]. You can use parameters in iscsi-scstd.conf
+as a key.
+.PP
+\&\fB\-\-op new \-\-tid=[id] \-\-user \-\-params=[user]=[name],Password=[pass]\fR
+.PP
+add a new account with [pass] for specific target.
+[user] could be [IncomingUser] or [OutgoingUser].
+If you don't specify a target (omit \-\-tid option),
+you add a new account for discovery sessions.
+.PP
+\&\fB\-\-op delete \-\-tid=[id] \-\-user \-\-params=[user]=[name]\fR
+.PP
+delete specific account having [name] of specific
+target. [user] could be [IncomingUser] or
+[OutgoingUser].
+If you don't specify a target (omit \-\-tid option),
+you delete the account for discovery sessions.
+.PP
+\&\fB\-\-version\fR
+.PP
+display version and exit
+.PP
+\&\fB\-\-help\fR
+.PP
+display a list of available options and exits
+.SH "KNOWN ISSUES"
+.IX Header "KNOWN ISSUES"
+.IP "\(bu" 4
+iscsi-scst-adm doesn't return any human-readable error message, only error codes.
+.IP "\(bu" 4
+iscsi-scst-adm doesn't modify or read the /etc/iscsi-scstd.conf iscsi-scstd configuration file.
+.IP "\(bu" 4
+iscsi-scst-adm can't use target names or aliases, only the tid found in /proc/scsi_tgt/iscsi.
+.IP "\(bu" 4
+/proc/scsi_tgt/iscsi may list inactive sessions if the initiator doesn't logout properly.
+.PP
+Report bugs to <iscsitarget\-devel@sourceforge.net>.
+.SH "FILES"
+.IX Header "FILES"
+/proc/scsi_tgt/iscsi
+.SH "SEE ALSO"
+.IX Header "SEE ALSO"
+\&\fIiscsi-scstd\fR\|(8),\fIiscsi-scstd.conf\fR\|(5)
+.\"man page written by Emmanuel Florac <eflorac@intellique.com"
+.\"distributed under GPL v2 licence"
diff --git a/iscsi-scst/doc/manpages/iscsi-scstd.8 b/iscsi-scst/doc/manpages/iscsi-scstd.8
new file mode 100644 (file)
index 0000000..0ce805e
--- /dev/null
@@ -0,0 +1,308 @@
+.\" Process this file with
+.\" groff -man -Tascii iscsi-scstd.8
+.\"
+.TH "iSCSI SCST Target Daemon" 8 "May 2007" Linux "User Manuals"
+.SH NAME
+iscsi-scstd \- iSCSI SCST Target Daemon
+.SH SYNOPSIS
+.B iscsi-scstd
+.RB [\| \-c
+.IR configfile \|]
+.RB [\| \-d
+.IR debuglevel \|]
+.RB [\| \-f \|]
+.RB [\| \-g
+.IR GID \|]
+.RB [\| \-h \|]
+.RB [\| \-a
+.IR address \|]
+.RB [\| \-p
+.IR port \|]
+.RB [\| \-s
+.IR IP \|]
+.RB [\| \-u
+.IR UID \|]
+.SH DESCRIPTION
+The
+.B iscsi-scstd
+program implements the user level part of iSCSI SCST Target software for building an iSCSI storage system on Linux.
+.SH OPTIONS
+.TP
+.BI \-c\  filename ,\ \-\-config= filename
+Specify configuration file, default is
+.I /etc/iscsi-scstd.conf
+.TP
+.BI \-d\  debuglevel ,\ \-\-debug= debuglevel
+Turns on debugging. Logging goes to
+.I /var/log/syslog
+via
+.BR syslog (1).
+.TP
+.BI \-f,\  \-\-foreground
+Act as a normal application which uses a console.
+.TP
+.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.
+.TP
+.BI \-p\  port ,\ \-\-port= port
+Specify on which port the server should listen, default is 3260.
+.TP
+.BI \-h,\  \-\-help
+Display help message on command line options.
+.TP
+.BI \-s\  IP ,\ \-\-isns= IP
+isns server's IP address
+.TP
+.BI \-u\  UID ,\ \-\-uid= UID
+Specify running user id, default is current uid.
+.SH FILES
+.I /etc/iscsi-scstd.conf
+.RS
+The system wide configuration file. See        
+.BR iscsi-scstd.conf (5)
+for further details.
+.RE
+.SH DIAGNOSTICS
+This may be logged to stderr:
+
+.B Warnings
+
+"Dropping key (%s=%s)"
+.RS
+Value(s): key, value
+.RE
+
+"unable to set fd flags (%s)!"
+.RS
+Value(s): strerror(errno)
+.RE
+
+"unable to get fd flags (%s)!"
+.RS
+Value(s): strerror(errno)
+.RE
+
+"unable to set SO_REUSEADDR on server socket (%s)!"
+.RS
+Value(s): strerror(errno)
+.RE
+
+"iscsi_ctrl: failed to scan '%s'"
+.RS
+Value(s): p
+.RE
+
+"target %u not found?"
+.RS
+Value(s): target_id
+.RE
+
+"session %#Lx not found?"
+.RS
+Value(s): session_id
+.RE
+
+"session_remove: session %#Lx not found?"
+.RS
+Value(s): session->sid.id64
+.RE
+
+"need to shutdown target %s"
+.RS
+Value(s): target->name
+.RE
+
+.B Errors
+
+"unable to create server socket (%s)!"
+.RS
+Value(s): strerror(errno)
+.RE
+
+"unable to bind server socket (%s)!"
+.RS
+Value(s): strerror(errno)
+.RE
+
+"unable to listen to server socket (%s)!"
+.RS
+Value(s): strerror(errno)
+.RE
+
+"error reading /proc/iscsi/iscsi (%d)"
+.RS
+Value(s): errno
+.RE
+
+"unable to create pid file"
+
+"starting daemon failed"
+
+"unable to lock pid file"
+
+"unable to open %s! (%s)"
+.RS
+Value(s): PROC_DEVICEDIR, strerror(errno)
+.RE
+
+"unable to open %s! (%s)"
+.RS
+Value(s): PROC_TARGETDIR, strerror(errno)
+.RE
+
+.B Debug Messages Level 0
+
+"unknown user %s"
+.RS
+Value(s): value
+.RE
+
+"login by %s failed"
+.RS
+Value(s): user->name
+.RE
+
+"succesfull login by %s"
+.RS
+Value(s): user->name
+.RE
+
+"connection request from %s"
+.RS
+Value(s): inet_ntoa(name.sin_addr)
+.RE
+
+"connection closed"
+
+.RS
+Value(s): target->id
+.RE
+
+.B Debug Messages Level 1
+
+"conn_take_fd: %u %u %u %Lx %d"
+.RS
+Value(s): conn->cid, conn->stat_sn, conn->exp_stat_sn, conn->sid.id64, fd
+.RE
+
+"exp_cmd_sn: %d,%d"
+.RS
+Value(s): conn->exp_cmd_sn, req->cmd_sn
+.RE
+
+"Login request (security negotiation): %d"
+.RS
+Value(s): conn->state
+.RE
+
+"Login request (operational negotiation): %d"
+.RS
+Value(s): conn->state
+.RE
+
+"Text request: %d"
+.RS
+Value(s): conn->state
+.RE
+
+"close conn %u session %Lx target %u"
+.RS
+Value(s): conn_id, session_id, target_id
+.RE
+
+"session_find_name: %s,%#Lx"
+.RS
+Value(s): iname, sid.id64
+.RE
+
+"session_find_id: %#Lx"
+.RS
+Value(s): sid
+.RE
+
+"session_create: %#Lx"
+.RS
+Value(s): session->sid.id64
+.RE
+
+"session_close: %#Lx"
+.RS
+Value(s): session->sid.id64
+.RE
+
+"session_remove: %#Lx"
+.RS
+Value(s): session->sid.id64
+.RE
+
+"active target %d: %s"
+.RS
+Value(s): id, name
+.RE
+
+"removing target %d"
+.RS
+Value(s): id
+.RE
+
+"creaing target %u: %s"
+.RS
+Value(s): target_next_id, p
+.RE
+
+
+"target_find_name: %s"
+.RS
+Value(s): name
+.RE
+
+"target_find_id: %u"
+.RS
+Value(s): id
+.RE
+
+"target_remove: %u,%s"
+.RS
+Value(s): target->id, target->name
+.RE
+
+.B Debug Messages Level 2
+
+"%s %.16s"
+.RS
+Value(s): line, buf
+.RE
+
+"BHS: (%p)"
+.RS
+Value(s): buf
+.RE
+
+"AHS: (%p)"
+.RS
+Value(s): buf
+.RE
+
+"Data: (%p)"
+.RS
+Value(s): buf
+.RE
+
+.SH KNOWN ISSUES
+Task attributes is incomplete (all tasks are treated as if they have the SIMPLE attribute.)
+
+SCSI task management is incomplete.
+
+NOP-In is incomplete.
+
+The target never sends a NOP-In of its own accord.
+
+Header and Data Digest in a discovery session are not implemented.
+
+Out-of-memory situation leads to the system crash (There are still some out-of-memory bugs.)
+
+.SH "SEE ALSO"
+.BR iscsi-scstd.conf (5)
diff --git a/iscsi-scst/doc/manpages/iscsi-scstd.conf.5 b/iscsi-scst/doc/manpages/iscsi-scstd.conf.5
new file mode 100644 (file)
index 0000000..b9768aa
--- /dev/null
@@ -0,0 +1,210 @@
+.\" Process this file with
+.\" groff -man -Tascii iscsi-scstd.conf.5
+.\" 
+.TH "ISCSI_SCSTD.CONF" "5" "May 2007" "A. Lehmann, M. Zhang and A. Redlich" "File formats"
+.SH "NAME"
+/etc/iscsi-scstd.conf \- configuration for iSCSI SCST Target Daemon
+.SH "SYNOPSIS"
+/etc/iscsi-scstd.conf
+.SH "DESCRIPTION"
+/etc/iscsi-scstd.conf contains configuration information for the
+.B iscsi-scstd (8)
+command. This is the place, where you configure your iSCSI targets and daemon parameters.
+.P
+Only lines starting with `#' are ignored. Putting '#' in the middle of a line is disallowed. A line may be extended across multiple lines by making the last character a backslash.
+.P
+The "Yes" and "No" for parameter values are case sensitive. The parameter names are case insensitive.
+.P
+The file consists of a global part and zero or more "Target" stanzas. Everything until the first target definition belongs to the global configuration. 
+
+Here is an example:
+
+IncomingUser joe secret
+.br 
+OutgoingUser jack secret2
+
+Target iqn.2007\-05.com.example:storage.disk2.sys1.xyz
+    IncomingUser jim othersecret
+    OutgoingUser james yetanothersecret
+    Alias Test
+    HeaderDigest None
+    DataDigest None
+    MaxConnections 1
+    InitialR2T No
+    ImmediateData Yes
+    MaxRecvDataSegmentLength 1048576
+    MaxXmitDataSegmentLength 1048576
+    MaxBurstLength 1048576
+    FirstBurstLength 1048576
+    DefaultTime2Wait 2
+    DefaultTime2Retain 20
+    MaxOutstandingR2T 20
+    DataPDUInOrder Yes
+    DataSequenceInOrder Yes
+    ErrorRecoveryLevel 0
+.P
+Stanzas start with the word "Target" and the target name. This name must be a globally unique name, as defined by the iSCSI standard : the "iSCSI Qualified Name". The daemon brings the targets up in the order listed.
+.SH "GLOBAL OPTIONS"
+Global Options are case sensitive.
+.TP 
+.B [IncomingUser <username> <password>]
+The 
+.I <username>
+and 
+.I <password>
+used during discovery sessions to authenticate iSCSI initiators. Several of those can be specified for discovery. If no
+.B IncomingUser
+is specified, any initiator is allowed to open a discovery session.
+.RS
+HINT: RFC 3720 requires
+.I <password>
+to be 12 characters long. This is enforced e.g. by MS Initiator.
+.RE
+.TP 
+.B [OutgoingUser <username> <password>]
+The 
+.I <username>
+and 
+.I <password>
+used during discovery sessions to authenticate the target to initiators. Only one outgoing 
+.I <username>/<password>
+combination may be specified.
+.RS
+HINT: RFC 3720 requires
+.I <password>
+to be 12 characters long. This is enforced e.g. by MS Initiator.
+.RE
+.TP 
+.B Target iqn.<yyyy\-mm>.<tld.domain.some.host>[:<identifier>]
+A target definition and the target name. The targets name (the
+.B iSCSI Qualified Name
+) must be a globally unique name (as defined by the iSCSI standard) and has to start with
+.I iqn
+followed by a single dot. The EUI\-64 form is not supported. 
+.I <yyyy\-mm>
+is the date (year and month) at which the domain is valid. This has to be followed by a single dot and the reversed domain name.
+The optional 
+.I <identifier>
+\- which is freely selectable \- has to be separated by a single colon. For further details please check the iSCSI spec.
+
+Here is an example:
+
+Target iqn.2007\-05.com.example.host:storage.disk2.sys1.xyz
+.SH "TARGET OPTIONS"
+Target options are also case sensitive.
+.TP 
+.B [IncomingUser <username> <password>]
+The
+.I <username>
+and
+.I <password>
+used to authenticate the iSCSI initiators to this target. It may be different from the username and password in section GLOBAL OPTIONS, which is used for discovery. If you omit the
+.B IncomingUser
+Option, connections are allowed without authentication. A
+.I <password>
+has to be provided, if there is a 
+.I <username>
+given. Specifying several different
+.B IncomingUser
+accounts is supported.
+.TP 
+.B [OutgoingUser <username> <password>]
+The
+.I <username>
+and
+.I <password>
+used to authenticate this iSCSI target to initiators. Only one
+.B 
+OutgoingUser
+per target is supported. It may be different from the username and password in section GLOBAL OPTIONS, which is used for discovery. A
+.I <password>
+has to be provided, if there is a 
+.I <username>
+given.
+.TP 
+.B [Alias <aliasname>]
+This assigns an optional 
+.I <aliasname>
+to the target.
+.TP 
+.B [HeaderDigest <CRC32C|None>]
+Optional. If set to "CRC32C" and the initiator is configured accordingly, the integrity of an iSCSI PDU's header segments will be protected by a CRC32C checksum. The default is "None". Note that header digests are not supported during discovery sessions.
+.TP 
+.B [DataDigest <CRC32C|None>]
+Optional. If set to "CRC32C" and the initiator is configured accordingly, the integrity of an iSCSI PDU's data segment will be protected by a CRC32C checksum. The default is "None". Note that data digests are not supported during discovery sessions.
+.TP 
+.B [MaxConnections <value>]
+Optional. Has to be set to "1" (in words: one), which is also the default.
+.TP 
+.B [InitialR2T <Yes|No>]
+Optional. If set to "Yes", the initiator has to wait for the target to solicit SCSI data before sending it. Setting it to "No" (default) allows the initiator to send a burst of
+.B FirstBurstLength
+bytes unsolicited right after and/or (depending on the setting of
+.B ImmediateData
+) together with the command. Thus setting it to "No" may improve performance.
+.TP 
+.B [ImmediateData <Yes|No>]
+Optional. This allows the initiator to append unsolicited data to a command. To achieve better performance, this should be set to "Yes". Which is the default.
+.TP 
+.B [MaxRecvDataSegmentLength <value>]
+Optional. Sets the maximum data segment length that can be received. The 
+.I <value>
+should be set to multiples of PAGE_SIZE. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is the highes possible for current platform (1 or 2 MB).
+.TP 
+.B [MaxXmitDataSegmentLength <value>]
+Optional. Sets the maximum data segment length that can be sent. The
+.I <value>
+actually used is the minimum of
+.B MaxXmitDataSegmentLength
+and the
+.B MaxRecvDataSegmentLength
+announced by the initiator. The 
+.I <value>
+should be set to multiples of PAGE_SIZE. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is the highes possible for current platform (1 or 2 MB).
+.TP 
+.B [MaxBurstLength <value>]
+Optional. Sets the maximum amount of either unsolicited or solicited data the initiator may send in a single burst. Any amount of data exceeding this value must be explicitly solicited by the target. The 
+.I <value>
+should be set to multiples of PAGE_SIZE. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is the highes possible for current platform (1 or 2 MB).
+.TP 
+.B [FirstBurstLength <value>]
+Optional. Sets the amount of unsolicited data the initiator may transmit in the first burst of a transfer either with and/or right after the command, depending on the settings of
+.B InitialR2T
+and
+.B ImmediateData
+.
+.I <value>
+should be set to multiples of PAGE_SIZE. Configuring too large values may lead to problems allocating sufficient memory, which in turn may lead to SCSI commands timing out at the initiator host. The default value is the highes possible for current platform (1 or 2 MB).
+.TP 
+.B [DefaultTime2Wait <value>]
+Currently not supported.
+.TP 
+.B [DefaultTime2Retain <value>]
+Currently not supported.
+.TP 
+.B [MaxOutstandingR2T <value>]
+Optional. Controls the maximum number of data transfers the target may request at once, each of up to
+.B MaxBurstLength
+bytes. The default is 20.
+.TP 
+.B [DataPDUInOrder <Yes|No>]
+Optional. Has to be set to "Yes" \- which is also the default.
+.TP 
+.B [DataSequenceInOrder <Yes|No>]
+Optional. Has to be set to "Yes" \- which is also the default.
+.TP 
+.B [ErrorRecoveryLevel <value>]
+Optional. Has to be set to "0" (in words: zero), which is also the default.
+.TP 
+.B [QueuedCommands <value>]
+Optional. This parameter defines a window of commands an initiator may send and that will be buffered by the target. Depending on your hardware and your (expected) workload, the
+.I value
+may be carefully adjusted. The default value of 32 should be sufficient for most purposes.
+.SH "KNOWN BUGS/LIMITATIONS"
+Currently (as of 0.4.11) not all iSCSI target parameters are used. Header and data digests are not supported during discovery sessions.
+.SH "SEE ALSO"
+.B iscsi-scstd (8)
+.TP 
+You should have a look at
+.B RFC 3720
+for all the glory details.
diff --git a/iscsi-scst/etc/initd/initd b/iscsi-scst/etc/initd/initd
new file mode 100644 (file)
index 0000000..5ff1a2a
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/bash
+#
+# Start the iSCSI Enterprise Target.
+#
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+MEM_SIZE=1048576
+
+configure_memsize()
+{
+    if [ -e /proc/sys/net/core/wmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/wmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
+    fi
+
+    if [ -e  /proc/sys/net/ipv4/tcp_rmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
+    fi
+}
+
+start_server()
+{
+#      configure_memsize
+       modprobe -q crc32c
+       modprobe iscsi-scst
+       /usr/local/sbin/iscsi-scstd
+}
+       
+stop_server()
+{
+       killall iscsi-scstd
+       rmmod -w iscsi-scst
+}
+
+case "$1" in
+       start)
+               start_server
+               ;;
+       stop)
+               stop_server
+               ;;
+       *)
+               echo "Usage: {start|stop}" >&2
+               exit 1
+               ;;
+esac
+
+exit 0
diff --git a/iscsi-scst/etc/initd/initd.debian b/iscsi-scst/etc/initd/initd.debian
new file mode 100644 (file)
index 0000000..e3d4f00
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/sh
+#
+# chkconfig: - 39 35
+# description: Starts and stops the iSCSI target
+# debianized start-stop script
+
+PID_FILE=/var/run/iscsi-scst.pid
+CONFIG_FILE=/etc/iscsi-scstd.conf
+DAEMON=/usr/local/sbin/iscsi-scstd
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+
+# Don't touch this "memsize thingy" unless you are blessed
+# with knowledge about it.
+MEM_SIZE=1048576
+
+configure_memsize()
+{
+    if [ -e /proc/sys/net/core/wmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/wmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
+    fi
+
+    if [ -e  /proc/sys/net/ipv4/tcp_rmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
+    fi
+}
+
+RETVAL=0
+
+iscsi_scstd_start()
+{
+       echo -n "Starting iSCSI enterprise target service: "
+#      configure_memsize
+       modprobe -q crc32c
+       modprobe iscsi-scst
+       start-stop-daemon --start --exec $DAEMON --quiet
+       RETVAL=$?
+       if [ $RETVAL == "0" ]; then
+           echo "succeeded."
+       else
+           echo "failed."
+       fi          
+}
+       
+iscsi_scstd_stop()
+{
+       echo -n "Stopping iSCSI enterprise target service: "
+       start-stop-daemon --stop --quiet --exec $DAEMON --pidfile $PID_FILE
+       RETVAL=$?
+       if [ $RETVAL == "0" ]; then
+           echo "succeeded."
+       else
+           echo "failed."
+       fi
+       # ugly, but pid file is not removed ba iscsi-scstd
+       rm -f $PID_FILE
+
+       echo -n "Removing iSCSI enterprise target modules: "
+       rmmod -w iscsi-scst
+       RETVAL=$?
+       modprobe -r crc32c 2>/dev/null
+       if [ $RETVAL == "0" ]; then
+           echo "succeeded."
+       else
+           echo "failed."
+           exit 1
+       fi
+}
+
+case "$1" in
+  start)
+        iscsi_scstd_start
+        ;;
+  stop)
+        iscsi_scstd_stop
+        ;;
+  restart)
+        iscsi_scstd_stop
+       sleep 1
+       iscsi_scstd_start
+        ;;
+  status)
+       PID=`pidof iscsi-scstd`
+       if [ $PID ]; then
+               echo "iSCSI enterprise target is running at pid $PID"
+       else
+               echo "no iSCSI enterprise target found!"
+               exit 1
+       fi      
+        ;;
+  dump)
+       DUMP=`tempfile -p iscsi-scstd`
+       RETVAL=$?
+       if [ $RETVAL != "0" ]; then
+           echo "Failed to create dump file $DUMP"
+           exit 1
+       fi
+        iscsi-scst-adm --mode dump --all >$DUMP
+       RETVAL=$?
+       if [ $RETVAL != "0" ]; then
+           echo "Error dumping config from daemon"
+           rm $DUMP
+           exit 1
+       fi
+       mv -u $DUMP $CONFIG_FILE
+       echo "Config dumped to $CONFIG_FILE"
+        ;;
+  *)
+        echo $"Usage: $0 {start|stop|restart|status|dump}"
+        exit 1
+esac
+
+exit 0
diff --git a/iscsi-scst/etc/initd/initd.gentoo b/iscsi-scst/etc/initd/initd.gentoo
new file mode 100644 (file)
index 0000000..590eb01
--- /dev/null
@@ -0,0 +1,71 @@
+#!/sbin/runscript
+#
+# Start the iSCSI Enterprise Target.
+#
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+MEM_SIZE=1048576
+
+depend()
+{
+        use net
+}
+
+configure_memsize()
+{
+    if [ -e /proc/sys/net/core/wmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/wmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
+    fi
+
+    if [ -e  /proc/sys/net/ipv4/tcp_rmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
+    fi
+}
+
+start_server()
+{
+#        configure_memsize
+        modprobe -q crc32c
+        modprobe iscsi-scst
+        /usr/local/sbin/iscsi-scstd
+}
+
+stop_server()
+{
+        killall iscsi-scstd
+        rmmod -w iscsi-scst
+}
+
+start()
+{
+        ebegin "Starting iscsi"
+        start_server
+        eend 0
+}
+
+stop()
+{
+        ebegin "Stopping iscsi"
+        stop_server
+        eend 0
+}
diff --git a/iscsi-scst/etc/initd/initd.redhat b/iscsi-scst/etc/initd/initd.redhat
new file mode 100644 (file)
index 0000000..b46d401
--- /dev/null
@@ -0,0 +1,130 @@
+#!/bin/sh
+#
+# chkconfig: - 39 35
+# description: Starts and stops the iSCSI target
+#
+# pidfile: /var/run/iscsi-scstd.pid
+# config:  /etc/iscsi-scstd.conf
+
+# Source function library.
+if [ -f /etc/init.d/functions ] ; then
+  . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+  . /etc/rc.d/init.d/functions
+else
+  exit 0
+fi
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+MEM_SIZE=1048576
+
+configure_memsize()
+{
+    if [ -e /proc/sys/net/core/wmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_max ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_max
+    fi
+
+    if [ -e /proc/sys/net/core/wmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/wmem_default
+    fi
+
+    if [ -e /proc/sys/net/core/rmem_default ]; then
+        echo ${MEM_SIZE} > /proc/sys/net/core/rmem_default
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_mem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_mem
+    fi
+
+    if [ -e  /proc/sys/net/ipv4/tcp_rmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_rmem
+    fi
+
+    if [ -e /proc/sys/net/ipv4/tcp_wmem ]; then
+        echo "${MEM_SIZE} ${MEM_SIZE} ${MEM_SIZE}" > /proc/sys/net/ipv4/tcp_wmem
+    fi
+}
+
+RETVAL=0
+
+start()
+{
+       echo -n "Starting iSCSI target service: "
+#      configure_memsize
+       modprobe -q crc32c
+       modprobe iscsi-scst
+       daemon /usr/local/sbin/iscsi-scstd
+       # -d 0xFFFF
+       RETVAL=$?
+       echo
+       return $RETVAL
+}
+       
+stop()
+{
+       echo -n "Stopping iSCSI target service: "
+#      iscsi-scst-adm --op delete
+       killall iscsi-scstd
+       rmmod -w iscsi-scst
+       RETVAL=$?
+       modprobe -r crc32c 2>/dev/null
+       if [ $RETVAL == "0" ]; then
+               echo_success
+       else
+               echo_failure
+       fi
+       echo
+       return $RETVAL
+}
+
+restart()
+{
+        stop
+        start
+}
+
+condrestart()
+{
+       PID=`pidofproc iscsi-scstd`
+       if [ $PID ]; then
+               restart
+       fi
+}
+
+status()
+{
+       PID=`pidofproc iscsi-scstd`
+       if [ ! $PID ]; then
+               echo "iSCSI target stopped"
+               exit 1
+       else
+               echo "iscsi-scstd (pid $PID) is running..."
+       fi      
+}
+
+case "$1" in
+  start)
+        start
+        ;;
+  stop)
+        stop
+        ;;
+  restart)
+        restart
+        ;;
+  condrestart)
+        condrestart
+        ;;
+  status)
+        status
+        ;;
+  *)
+        echo $"Usage: $0 {start|stop|restart|status}"
+        exit 1
+esac
+
+exit 0
diff --git a/iscsi-scst/etc/initiators.allow b/iscsi-scst/etc/initiators.allow
new file mode 100644 (file)
index 0000000..0cef10d
--- /dev/null
@@ -0,0 +1,7 @@
+# Some examples
+#
+#iqn.2001-04.com.example:storage.disk1.sys1.xyz 192.168.22.2, 192.168.3.8
+#iqn.2001-04.com.example:storage.disk1.sys2.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], [3ffe:505:2:1::]/64
+#iqn.2001-04.com.example:storage.disk1.sys3.xyz ALL
+#iqn.2001-04.com.example:storage.disk1.sys4.xyz 192.168.22.3
+#ALL 192.168.22.4
diff --git a/iscsi-scst/etc/initiators.deny b/iscsi-scst/etc/initiators.deny
new file mode 100644 (file)
index 0000000..72fdf0b
--- /dev/null
@@ -0,0 +1,14 @@
+# The semantics are:
+# * By default, every initiator can see and connect to all targets.
+#  
+# * Deny for some or every initiator access to one or all targets,
+#   by adding a line to this file.
+# 
+# * Then allow some named initiators access to selected targets,
+#   by adding lines to initiators.allow
+#
+# Some examples
+#iqn.2001-04.com.example:storage.disk1.sys1.xyz ALL
+#iqn.2001-04.com.example:storage.disk1.sys2.xyz 192.168.12.2, 192.168.3.0/24, 192.167.1.16/28
+#iqn.2001-04.com.example:storage.disk1.sys4.xyz [3ffe:302:11:1:211:43ff:fe31:5ae2], [3ffe:505:2:1::]/64
+#ALL ALL
diff --git a/iscsi-scst/etc/iscsi-scstd.conf b/iscsi-scst/etc/iscsi-scstd.conf
new file mode 100644 (file)
index 0000000..6488e93
--- /dev/null
@@ -0,0 +1,55 @@
+# Example iscsi target configuration
+#
+# Everything until the first target definition belongs
+# to the global configuration.
+# Right now this is only the user configuration used
+# during discovery sessions. "IncomingUser" specifies credentials the
+# initiator has to provide - several of these are supported. If mutual
+# CHAP shall be employed, "OutgoingUser" specifies the user/pass
+# combination the target will provide - only one is supported.
+# Leave them alone (keep them commented out) if you don't want to use
+# authentication for discovery sessions.
+
+#iSNSServer 192.168.1.16
+#iSNSAccessControl No
+
+#IncomingUser joe secret
+#OutgoingUser jack 12charsecret
+
+# Targets definitions start with "Target" and the target name.
+# The target name must be a globally unique name, the iSCSI
+# standard defines the "iSCSI Qualified Name" as follows:
+#
+# iqn.yyyy-mm.<reversed domain name>[:identifier]
+#
+# "yyyy-mm" is the date at which the domain is valid and the identifier
+# is freely selectable. For further details please check the iSCSI spec.
+
+Target iqn.2007-05.com.example:storage.disk2.sys1.xyz
+       # Users, who can access this target. The same rules as for discovery
+       # users apply here.
+       # Leave them alone if you don't want to use authentication.
+       #IncomingUser joe secret
+       #OutgoingUser jim 12charpasswd
+       # Alias name for this target
+       # Alias Test
+       # various iSCSI parameters
+       # (not all are used right now, see also iSCSI spec for details)
+       #MaxConnections         1
+       #InitialR2T             No
+       #ImmediateData          Yes
+       #MaxRecvDataSegmentLength 1048576
+       #MaxXmitDataSegmentLength 1048576
+       #MaxBurstLength         1048576
+       #FirstBurstLength       1048576
+       #DefaultTime2Wait       2
+       #DefaultTime2Retain     20
+       #MaxOutstandingR2T      20
+       #DataPDUInOrder         Yes
+       #DataSequenceInOrder    Yes
+       #ErrorRecoveryLevel     0
+       #HeaderDigest           CRC32C,None
+       #DataDigest             CRC32C,None
+       # various target parameters
+       #QueuedCommands         32
+
diff --git a/iscsi-scst/include/iscsi_u.h b/iscsi-scst/include/iscsi_u.h
new file mode 100644 (file)
index 0000000..4f88378
--- /dev/null
@@ -0,0 +1,137 @@
+#ifndef _ISCSI_U_H
+#define _ISCSI_U_H
+
+#ifndef __KERNEL__
+#include <sys/uio.h>
+#endif
+
+#define ISCSI_VERSION_STRING   "0.9.6/0.4.15r133"
+
+/* The maximum length of 223 bytes in the RFC. */
+#define ISCSI_NAME_LEN 256
+
+#define ISCSI_LISTEN_PORT      3260
+
+#define SCSI_ID_LEN    24
+
+#ifndef aligned_u64
+#define aligned_u64 unsigned long long __attribute__((aligned(8)))
+#endif
+
+struct target_info {
+       u32 tid;
+       char name[ISCSI_NAME_LEN];
+};
+
+struct session_info {
+       u32 tid;
+
+       aligned_u64 sid;
+       char initiator_name[ISCSI_NAME_LEN];
+       char user_name[ISCSI_NAME_LEN];
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+};
+
+#define DIGEST_ALL     (DIGEST_NONE | DIGEST_CRC32C)
+#define DIGEST_NONE            (1 << 0)
+#define DIGEST_CRC32C           (1 << 1)
+
+struct conn_info {
+       u32 tid;
+       aligned_u64 sid;
+
+       u32 cid;
+       u32 stat_sn;
+       u32 exp_stat_sn;
+       int header_digest;
+       int data_digest;
+       int fd;
+};
+
+enum {
+       key_initial_r2t,
+       key_immediate_data,
+       key_max_connections,
+       key_max_recv_data_length,
+       key_max_xmit_data_length,
+       key_max_burst_length,
+       key_first_burst_length,
+       key_default_wait_time,
+       key_default_retain_time,
+       key_max_outstanding_r2t,
+       key_data_pdu_inorder,
+       key_data_sequence_inorder,
+       key_error_recovery_level,
+       key_header_digest,
+       key_data_digest,
+       key_ofmarker,
+       key_ifmarker,
+       key_ofmarkint,
+       key_ifmarkint,
+       session_key_last,
+};
+
+enum {
+       key_queued_cmnds,
+       target_key_last,
+};
+
+enum {
+       key_session,
+       key_target,
+};
+
+struct iscsi_param_info {
+       u32 tid;
+       aligned_u64 sid;
+
+       u32 param_type;
+       u32 partial;
+
+       u32 session_param[session_key_last];
+       u32 target_param[target_key_last];
+};
+
+enum iscsi_event_state {
+       E_CONN_CLOSE,
+};
+
+struct iscsi_event {
+       u32 tid;
+       aligned_u64 sid;
+       u32 cid;
+       u32 state;
+};
+
+#define        DEFAULT_NR_QUEUED_CMNDS 32
+#define        MIN_NR_QUEUED_CMNDS     1
+#define        MAX_NR_QUEUED_CMNDS     256
+
+#define MAX_DATA_SEG_LEN       (4096/sizeof(struct iovec)*4096)
+
+#define NETLINK_ISCSI_SCST     25
+
+#define ADD_TARGET _IOW('i', 0, struct target_info)
+#define DEL_TARGET _IOW('i', 1, struct target_info)
+#define ADD_SESSION _IOW('i', 2, struct session_info)
+#define DEL_SESSION _IOW('i', 3, struct session_info)
+#define GET_SESSION_INFO _IOWR('i', 4, struct session_info)
+#define ADD_CONN _IOW('i', 5, struct conn_info)
+#define DEL_CONN _IOW('i', 6, struct conn_info)
+#define GET_CONN_INFO _IOWR('i', 7, struct conn_info)
+#define ISCSI_PARAM_SET _IOW('i', 8, struct iscsi_param_info)
+#define ISCSI_PARAM_GET _IOWR('i', 9, struct iscsi_param_info)
+
+static inline int iscsi_is_key_declarative(int key)
+{
+       switch(key)
+       {
+       case key_max_xmit_data_length:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+#endif
diff --git a/iscsi-scst/kernel/Makefile b/iscsi-scst/kernel/Makefile
new file mode 100644 (file)
index 0000000..e701c77
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Makefile for the Linux kernel device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+#SCST_INC_DIR := /usr/local/include/scst
+SCST_INC_DIR := $(SUBDIRS)/../../scst/include
+
+EXTRA_CFLAGS += -I$(src)/../include -I$(SCST_INC_DIR)
+# -Wextra -Wno-unused-parameter
+
+EXTRA_CFLAGS += -DEXTRACHECKS
+#EXTRA_CFLAGS += -DTRACING
+EXTRA_CFLAGS += -DDEBUG -g
+
+#EXTRA_CFLAGS += -DDEBUG_DIGEST_FAILURES
+
+obj-m          += iscsi-scst.o
+iscsi-scst-objs        := iscsi.o nthread.o config.o digest.o \
+       conn.o session.o target.o event.o param.o
+
diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c
new file mode 100644 (file)
index 0000000..59ad8cd
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include <linux/proc_fs.h>
+
+#include "iscsi.h"
+
+#define ISCSI_PROC_VERSION_NAME                "version"
+
+#if defined(DEBUG) || defined(TRACING)
+
+#define ISCSI_PROC_LOG_ENTRY_NAME      "trace_level"
+
+#include <linux/proc_fs.h>
+
+static struct scst_proc_log iscsi_proc_local_trace_tbl[] =
+{
+    { TRACE_D_READ,            "d_read" },
+    { TRACE_D_WRITE,           "d_write" },
+    { TRACE_CONN_OC,           "conn" },
+    { TRACE_D_IOV,             "iov" },
+    { TRACE_D_DUMP_PDU,                "pdu" },
+    { 0,                       NULL }
+};
+
+static int iscsi_log_info_show(struct seq_file *seq, void *v)
+{
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       res = scst_proc_log_entry_read(seq, trace_flag,
+               iscsi_proc_local_trace_tbl);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static int iscsi_proc_log_entry_write(struct file *file, const char __user *buf,
+                                        size_t length, loff_t *off)
+{
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
+               ISCSI_DEFAULT_LOG_FLAGS, iscsi_proc_local_trace_tbl);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+#endif
+
+static int iscsi_version_info_show(struct seq_file *seq, void *v)
+{
+       TRACE_ENTRY();
+
+       seq_printf(seq, "%s\n", ISCSI_VERSION_STRING);
+
+#ifdef EXTRACHECKS
+       seq_printf(seq, "EXTRACHECKS\n");
+#endif
+
+#ifdef TRACING
+       seq_printf(seq, "TRACING\n");
+#endif
+
+#ifdef DEBUG
+       seq_printf(seq, "DEBUG\n");
+#endif
+
+#ifdef DEBUG_DIGEST_FAILURES
+       seq_printf(seq, "DEBUG_DIGEST_FAILURES\n");
+#endif
+
+       TRACE_EXIT();
+       return 0;
+}
+
+static struct scst_proc_data iscsi_version_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = iscsi_version_info_show,
+};
+
+#if defined(DEBUG) || defined(TRACING)
+static struct scst_proc_data iscsi_log_proc_data = {
+       SCST_DEF_RW_SEQ_OP(iscsi_proc_log_entry_write)
+       .show = iscsi_log_info_show,
+};
+#endif
+
+static __init int iscsi_proc_log_entry_build(struct scst_tgt_template *templ)
+{
+       int res = 0;
+       struct proc_dir_entry *p, *root;
+
+       TRACE_ENTRY();
+
+       root = scst_proc_get_tgt_root(templ);
+       if (root) {
+               p = scst_create_proc_entry(root, ISCSI_PROC_VERSION_NAME,
+                                        &iscsi_version_proc_data);
+               if (p == NULL) {
+                       PRINT_ERROR_PR("Not enough memory to register "
+                            "target driver %s entry %s in /proc",
+                             templ->name, ISCSI_PROC_VERSION_NAME);
+                       res = -ENOMEM;
+                       goto out;
+               }
+
+#if defined(DEBUG) || defined(TRACING)
+               /* create the proc file entry for the device */
+               iscsi_log_proc_data.data = (void *)templ->name;
+               p = scst_create_proc_entry(root, ISCSI_PROC_LOG_ENTRY_NAME,
+                                       &iscsi_log_proc_data);
+               if (p == NULL) {
+                       PRINT_ERROR_PR("Not enough memory to register "
+                            "target driver %s entry %s in /proc",
+                             templ->name, ISCSI_PROC_LOG_ENTRY_NAME);
+                       res = -ENOMEM;
+                       goto out_remove_ver;
+               }
+#endif
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+#if defined(DEBUG) || defined(TRACING)
+out_remove_ver:
+       remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
+       goto out;
+#endif
+}
+
+static void iscsi_proc_log_entry_clean(struct scst_tgt_template *templ)
+{
+       struct proc_dir_entry *root;
+
+       TRACE_ENTRY();
+
+       root = scst_proc_get_tgt_root(templ);
+       if (root) {
+#if defined(DEBUG) || defined(TRACING)
+               remove_proc_entry(ISCSI_PROC_LOG_ENTRY_NAME, root);
+#endif
+               remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
+       }
+
+       TRACE_EXIT();
+       return;
+}
+
+struct proc_entries {
+       const char *name;
+       struct file_operations *fops;
+};
+
+static struct proc_entries iscsi_proc_entries[] =
+{
+       {"session", &session_seq_fops},
+};
+
+static struct proc_dir_entry *proc_iscsi_dir;
+
+void iscsi_procfs_exit(void)
+{
+       int i;
+
+       if (!proc_iscsi_dir)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++)
+               remove_proc_entry(iscsi_proc_entries[i].name, proc_iscsi_dir);
+
+       iscsi_proc_log_entry_clean(&iscsi_template);
+}
+
+int __init iscsi_procfs_init(void)
+{
+       int i, err = 0;
+       struct proc_dir_entry *ent;
+
+       proc_iscsi_dir = scst_proc_get_tgt_root(&iscsi_template);
+       if (proc_iscsi_dir == NULL) {
+               err = -ESRCH;
+               goto out;
+       }
+
+       proc_iscsi_dir->owner = THIS_MODULE;
+
+       err = iscsi_proc_log_entry_build(&iscsi_template);
+       if (err < 0)
+               goto out;
+
+       for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++) {
+               ent = create_proc_entry(iscsi_proc_entries[i].name, 0, proc_iscsi_dir);
+               if (ent)
+                       ent->proc_fops = iscsi_proc_entries[i].fops;
+               else {
+                       err = -ENOMEM;
+                       goto err;
+               }
+       }
+
+out:
+       return err;
+
+err:
+       if (proc_iscsi_dir)
+               iscsi_procfs_exit();
+       goto out;
+}
+
+/* target_mutex supposed to be locked */
+static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
+{
+       int err;
+       struct iscsi_session *session;
+       struct conn_info info;
+       struct iscsi_conn *conn;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       session = session_lookup(target, info.sid);
+       if (!session)
+               return -ENOENT;
+       conn = conn_lookup(session, info.cid);
+
+       info.cid = conn->cid;
+       info.stat_sn = conn->stat_sn;
+       info.exp_stat_sn = conn->exp_stat_sn;
+
+       if (copy_to_user((void *) ptr, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+/* target_mutex supposed to be locked */
+static int add_conn(struct iscsi_target *target, unsigned long ptr)
+{
+       int err;
+       struct iscsi_session *session;
+       struct conn_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       if (!(session = session_lookup(target, info.sid)))
+               return -ENOENT;
+
+       return conn_add(session, &info);
+}
+
+/* target_mutex supposed to be locked */
+static int del_conn(struct iscsi_target *target, unsigned long ptr)
+{
+       int err;
+       struct iscsi_session *session;
+       struct conn_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       if (!(session = session_lookup(target, info.sid)))
+               return -ENOENT;
+
+       return conn_del(session, &info);
+}
+
+/* target_mutex supposed to be locked */
+static int get_session_info(struct iscsi_target *target, unsigned long ptr)
+{
+       int err;
+       struct iscsi_session *session;
+       struct session_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       session = session_lookup(target, info.sid);
+
+       if (!session)
+               return -ENOENT;
+
+       info.exp_cmd_sn = session->exp_cmd_sn;
+       info.max_cmd_sn = session->max_cmd_sn;
+
+       if (copy_to_user((void *) ptr, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+/* target_mutex supposed to be locked */
+static int add_session(struct iscsi_target *target, unsigned long ptr)
+{
+       int err;
+       struct session_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       info.initiator_name[ISCSI_NAME_LEN-1] = '\0';
+       info.user_name[ISCSI_NAME_LEN-1] = '\0';
+
+       return session_add(target, &info);
+}
+
+/* target_mutex supposed to be locked */
+static int del_session(struct iscsi_target *target, unsigned long ptr)
+{
+       int err;
+       struct session_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       return session_del(target, info.sid);
+}
+
+/* target_mutex supposed to be locked */
+static int iscsi_param_config(struct iscsi_target *target, unsigned long ptr, int set)
+{
+       int err;
+       struct iscsi_param_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               goto out;
+
+       if ((err = iscsi_param_set(target, &info, set)) < 0)
+               goto out;
+
+       if (!set)
+               err = copy_to_user((void *) ptr, &info, sizeof(info));
+
+out:
+       return err;
+}
+
+/* target_mgmt_mutex supposed to be locked */
+static int add_target(unsigned long ptr)
+{
+       int err;
+       struct target_info info;
+
+       if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
+               return err;
+
+       if (!(err = target_add(&info)))
+               err = copy_to_user((void *) ptr, &info, sizeof(info));
+
+       return err;
+}
+
+static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct iscsi_target *target = NULL;
+       long err;
+       u32 id;
+
+       if ((err = get_user(id, (u32 *) arg)) != 0)
+               goto out;
+
+       if ((err = mutex_lock_interruptible(&target_mgmt_mutex)) < 0)
+               goto out;
+
+       if (cmd == DEL_TARGET) {
+               err = target_del(id);
+               goto out_unlock;
+       }
+
+       target = target_lookup_by_id(id);
+
+       if (cmd == ADD_TARGET)
+               if (target) {
+                       err = -EEXIST;
+                       PRINT_ERROR_PR("Target %u already exist!", id);
+                       goto out_unlock;
+               }
+
+       switch (cmd) {
+       case ADD_TARGET:
+               err = add_target(arg);
+               goto out_unlock;
+       }
+
+       if (!target) {
+               PRINT_ERROR_PR("can't find the target %u", id);
+               err = -EINVAL;
+               goto out_unlock;
+       }
+
+       mutex_lock(&target->target_mutex);
+
+       switch (cmd) {
+       case ADD_SESSION:
+               err = add_session(target, arg);
+               break;
+
+       case DEL_SESSION:
+               err = del_session(target, arg);
+               break;
+
+       case GET_SESSION_INFO:
+               err = get_session_info(target, arg);
+               break;
+
+       case ISCSI_PARAM_SET:
+               err = iscsi_param_config(target, arg, 1);
+               break;
+
+       case ISCSI_PARAM_GET:
+               err = iscsi_param_config(target, arg, 0);
+               break;
+
+       case ADD_CONN:
+               err = add_conn(target, arg);
+               break;
+
+       case DEL_CONN:
+               err = del_conn(target, arg);
+               break;
+
+       case GET_CONN_INFO:
+               err = get_conn_info(target, arg);
+               break;
+       
+       default:
+               PRINT_ERROR_PR("invalid ioctl cmd %x", cmd);
+               err = -EINVAL;
+       }
+
+       mutex_unlock(&target->target_mutex);
+
+out_unlock:
+       mutex_unlock(&target_mgmt_mutex);
+
+out:
+       return err;
+}
+
+static int release(struct inode *inode, struct file *filp)
+{
+       target_del_all();
+       return 0;
+}
+
+struct file_operations ctr_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = ioctl,
+       .compat_ioctl   = ioctl,
+       .release        = release,
+};
+
+#ifdef DEBUG
+void iscsi_dump_iov(struct msghdr *msg)
+{
+       if (trace_flag & TRACE_D_IOV) {
+               int i;
+               printk("%p, %d\n", msg->msg_iov, msg->msg_iovlen);
+               for (i = 0; i < min_t(size_t, msg->msg_iovlen, 
+                               ISCSI_CONN_IOV_MAX); i++) {
+                       printk("%d: %p,%d\n", i, msg->msg_iov[i].iov_base,
+                               msg->msg_iov[i].iov_len);
+               }
+       }
+}
+
+static void iscsi_dump_char(int ch)
+{
+       static unsigned char text[16];
+       static int i = 0;
+
+       if (ch < 0) {
+               while ((i % 16) != 0) {
+                       printk("   ");
+                       text[i] = ' ';
+                       i++;
+                       if ((i % 16) == 0)
+                               printk(" | %.16s |\n", text);
+                       else if ((i % 4) == 0)
+                               printk(" |");
+               }
+               i = 0;
+               return;
+       }
+
+       text[i] = (ch < 0x20 || (ch >= 0x80 && ch <= 0xa0)) ? ' ' : ch;
+       printk(" %02x", ch);
+       i++;
+       if ((i % 16) == 0) {
+               printk(" | %.16s |\n", text);
+               i = 0;
+       } else if ((i % 4) == 0)
+               printk(" |");
+}
+
+void iscsi_dump_pdu(struct iscsi_pdu *pdu)
+{
+       if (trace_flag & TRACE_D_DUMP_PDU) {
+               unsigned char *buf;
+               int i;
+
+               buf = (void *)&pdu->bhs;
+               printk("BHS: (%p,%d)\n", buf, sizeof(pdu->bhs));
+               for (i = 0; i < sizeof(pdu->bhs); i++)
+                       iscsi_dump_char(*buf++);
+               iscsi_dump_char(-1);
+
+               buf = (void *)pdu->ahs;
+               printk("AHS: (%p,%d)\n", buf, pdu->ahssize);
+               for (i = 0; i < pdu->ahssize; i++)
+                       iscsi_dump_char(*buf++);
+               iscsi_dump_char(-1);
+
+               printk("Data: (%d)\n", pdu->datasize);
+       }
+}
+#endif /* DEBUG */
diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c
new file mode 100644 (file)
index 0000000..b776bc0
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <linux/file.h>
+#include <linux/ip.h>
+#include <net/tcp.h>
+
+#include "iscsi.h"
+#include "digest.h"
+
+static void print_conn_state(char *p, size_t size, struct iscsi_conn *conn)
+{
+       int printed = 0;
+
+       if (conn->closing) {
+               snprintf(p, size, "%s", "closing");
+               return;
+       }
+
+       switch(conn->rd_state) {
+       case ISCSI_CONN_RD_STATE_PROCESSING:
+               snprintf(p, size, "%s", "read_processing ");
+               printed = 1;
+               break;
+       case ISCSI_CONN_RD_STATE_IN_LIST:
+               snprintf(p, size, "%s", "in_read_list ");
+               printed = 1;
+               break;
+       }
+
+       switch(conn->wr_state) {
+       case ISCSI_CONN_WR_STATE_PROCESSING:
+               snprintf(p, size, "%s", "write_processing ");
+               printed = 1;
+               break;
+       case ISCSI_CONN_WR_STATE_IN_LIST:
+               snprintf(p, size, "%s", "in_write_list ");
+               printed = 1;
+               break;
+       case ISCSI_CONN_WR_STATE_SPACE_WAIT:
+               snprintf(p, size, "%s", "space_waiting ");
+               printed = 1;
+               break;
+       }
+
+       if (!printed)
+               snprintf(p, size, "%s", "established idle ");
+}
+
+static void print_digest_state(char *p, size_t size, unsigned long flags)
+{
+       if (DIGEST_NONE & flags)
+               snprintf(p, size, "%s", "none");
+       else if (DIGEST_CRC32C & flags)
+               snprintf(p, size, "%s", "crc32c");
+       else
+               snprintf(p, size, "%s", "unknown");
+}
+
+/* target_mutex supposed to be locked */
+void conn_info_show(struct seq_file *seq, struct iscsi_session *session)
+{
+       struct iscsi_conn *conn;
+       struct sock *sk;
+       char buf[64];
+
+       list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
+               sk = conn->sock->sk;
+               switch (sk->sk_family) {
+               case AF_INET:
+                       snprintf(buf, sizeof(buf),
+                                "%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr));
+                       break;
+               case AF_INET6:
+                       snprintf(buf, sizeof(buf),
+                                "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
+                                NIP6(inet6_sk(sk)->daddr));
+                       break;
+               default:
+                       break;
+               }
+               seq_printf(seq, "\t\tcid:%u ip:%s ", conn->cid, buf);
+               print_conn_state(buf, sizeof(buf), conn);
+               seq_printf(seq, "state:%s ", buf);
+               print_digest_state(buf, sizeof(buf), conn->hdigest_type);
+               seq_printf(seq, "hd:%s ", buf);
+               print_digest_state(buf, sizeof(buf), conn->ddigest_type);
+               seq_printf(seq, "dd:%s\n", buf);
+       }
+}
+
+/* target_mutex supposed to be locked */
+struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid)
+{
+       struct iscsi_conn *conn;
+
+       list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
+               if (conn->cid == cid)
+                       return conn;
+       }
+       return NULL;
+}
+
+static void iscsi_make_conn_rd_active(struct iscsi_conn *conn)
+{
+       TRACE_ENTRY();
+
+       spin_lock_bh(&iscsi_rd_lock);
+
+       TRACE_DBG("conn %p, rd_state %x, rd_data_ready %d", conn,
+               conn->rd_state, conn->rd_data_ready);
+
+       conn->rd_data_ready = 1;
+
+       if (conn->rd_state == ISCSI_CONN_RD_STATE_IDLE) {
+               list_add_tail(&conn->rd_list_entry, &iscsi_rd_list);
+               conn->rd_state = ISCSI_CONN_RD_STATE_IN_LIST;
+               wake_up(&iscsi_rd_waitQ);
+       }
+
+       spin_unlock_bh(&iscsi_rd_lock);
+
+       TRACE_EXIT();
+       return;
+}
+
+void iscsi_make_conn_wr_active(struct iscsi_conn *conn)
+{
+       TRACE_ENTRY();
+
+       spin_lock_bh(&iscsi_wr_lock);
+
+       TRACE_DBG("conn %p, wr_state %x, wr_space_ready %d", conn,
+               conn->wr_state, conn->wr_space_ready);
+
+       if (conn->wr_state == ISCSI_CONN_WR_STATE_IDLE) {
+               list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
+               conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
+               wake_up(&iscsi_wr_waitQ);
+       }
+
+       spin_unlock_bh(&iscsi_wr_lock);
+
+       TRACE_EXIT();
+       return;
+}
+
+void mark_conn_closed(struct iscsi_conn *conn)
+{
+       spin_lock_bh(&iscsi_rd_lock);
+       conn->closing = 1;
+       spin_unlock_bh(&iscsi_rd_lock);
+
+       iscsi_make_conn_rd_active(conn);
+}
+
+static void iscsi_state_change(struct sock *sk)
+{
+       struct iscsi_conn *conn = sk->sk_user_data;
+
+       TRACE_ENTRY();
+
+       if (sk->sk_state != TCP_ESTABLISHED) {
+               if (!conn->closing) {
+                       PRINT_ERROR_PR("Connection with initiator %s (%p) "
+                               "unexpectedly closed!",
+                               conn->session->initiator_name, conn);
+                       mark_conn_closed(conn);
+               }
+       } else
+               iscsi_make_conn_rd_active(conn);
+
+       conn->old_state_change(sk);
+
+       TRACE_EXIT();
+       return;
+}
+
+static void iscsi_data_ready(struct sock *sk, int len)
+{
+       struct iscsi_conn *conn = sk->sk_user_data;
+
+       TRACE_ENTRY();
+
+       iscsi_make_conn_rd_active(conn);
+
+       conn->old_data_ready(sk, len);
+
+       TRACE_EXIT();
+       return;
+}
+
+static void iscsi_write_space_ready(struct sock *sk)
+{
+       struct iscsi_conn *conn = sk->sk_user_data;
+
+       TRACE_ENTRY();
+
+       TRACE_DBG("Write space ready for conn %p", conn);
+
+       spin_lock_bh(&iscsi_wr_lock);
+       conn->wr_space_ready = 1;
+       if ((conn->wr_state == ISCSI_CONN_WR_STATE_SPACE_WAIT)) {
+               list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
+               conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
+               wake_up(&iscsi_wr_waitQ);
+       }
+       spin_unlock_bh(&iscsi_wr_lock);
+
+       conn->old_write_space(sk);
+
+       TRACE_EXIT();
+       return;
+}
+
+static int iscsi_socket_bind(struct iscsi_conn *conn)
+{
+       int res = 0;
+       int opt = 1;
+       mm_segment_t oldfs;
+       struct iscsi_session *session = conn->session;
+
+       TRACE_DBG("%llu", (unsigned long long) session->sid);
+
+       conn->sock = SOCKET_I(conn->file->f_dentry->d_inode);
+
+       if (conn->sock->ops->sendpage == NULL) {
+               PRINT_ERROR_PR("Socket for sid %llu doesn't support sendpage()",
+                       session->sid);
+               res = -EINVAL;
+               goto out;
+       }
+
+#if 0
+       conn->sock->sk->sk_allocation = GFP_NOIO;
+#endif
+       conn->sock->sk->sk_user_data = conn;
+
+       write_lock_bh(&conn->sock->sk->sk_callback_lock);
+
+       conn->old_state_change = conn->sock->sk->sk_state_change;
+       conn->sock->sk->sk_state_change = iscsi_state_change;
+
+       conn->old_data_ready = conn->sock->sk->sk_data_ready;
+       conn->sock->sk->sk_data_ready = iscsi_data_ready;
+
+       conn->old_write_space = conn->sock->sk->sk_write_space;
+       conn->sock->sk->sk_write_space = iscsi_write_space_ready;
+
+       write_unlock_bh(&conn->sock->sk->sk_callback_lock);
+
+       oldfs = get_fs();
+       set_fs(get_ds());
+       conn->sock->ops->setsockopt(conn->sock, SOL_TCP, TCP_NODELAY,
+               (void *)&opt, sizeof(opt));
+       set_fs(oldfs);
+
+out:
+       return res;
+}
+
+/* target_mutex supposed to be locked */
+int conn_free(struct iscsi_conn *conn)
+{
+       TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn,
+               conn->session, (unsigned long long)conn->session->sid,
+               conn->cid);
+
+       sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0);
+       sBUG_ON(!list_empty(&conn->cmd_list));
+       sBUG_ON(!list_empty(&conn->write_list));
+
+       list_del(&conn->conn_list_entry);
+
+       fput(conn->file);
+       conn->file = NULL;
+       conn->sock = NULL;
+
+       free_page((unsigned long)conn->read_iov);
+
+       kfree(conn);
+
+       return 0;
+}
+
+/* target_mutex supposed to be locked */
+static int iscsi_conn_alloc(struct iscsi_session *session, struct conn_info *info)
+{
+       struct iscsi_conn *conn;
+       int res = 0;
+
+       TRACE_MGMT_DBG("Creating connection for sid %#Lx, cid %u",
+               (unsigned long long)session->sid, info->cid);
+
+       conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+       if (!conn) {
+               res = -ENOMEM;
+               goto out_err;
+       }
+
+       /* Changing it, change ISCSI_CONN_IOV_MAX as well !! */
+       conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL);
+       if (conn->read_iov == NULL) {
+               res = -ENOMEM;
+               goto out_err_free_conn;
+       }
+
+       atomic_set(&conn->conn_ref_cnt, 0);
+       conn->session = session;
+       conn->cid = info->cid;
+       conn->stat_sn = info->stat_sn;
+       conn->exp_stat_sn = info->exp_stat_sn;
+       conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
+       conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
+
+       conn->hdigest_type = info->header_digest;
+       conn->ddigest_type = info->data_digest;
+       res = digest_init(conn);
+       if (res != 0)
+               goto out_err_free1;
+
+       conn->target = session->target;
+       spin_lock_init(&conn->cmd_list_lock);
+       INIT_LIST_HEAD(&conn->cmd_list);
+       spin_lock_init(&conn->write_list_lock);
+       INIT_LIST_HEAD(&conn->write_list);
+
+       conn->file = fget(info->fd);
+
+       res = iscsi_socket_bind(conn);
+       if (res != 0)
+               goto out_err_free2;
+
+       list_add(&conn->conn_list_entry, &session->conn_list);
+
+out:
+       return res;
+
+out_err_free2:
+       fput(conn->file);
+
+out_err_free1:
+       free_page((unsigned long)conn->read_iov);
+
+out_err_free_conn:
+       kfree(conn);
+
+out_err:
+       goto out;
+}
+
+/* target_mutex supposed to be locked */
+int conn_add(struct iscsi_session *session, struct conn_info *info)
+{
+       struct iscsi_conn *conn;
+       int err = -EEXIST;
+
+       conn = conn_lookup(session, info->cid);
+       if (conn)
+               return err;
+
+       return iscsi_conn_alloc(session, info);
+}
+
+/* target_mutex supposed to be locked */
+int conn_del(struct iscsi_session *session, struct conn_info *info)
+{
+       struct iscsi_conn *conn;
+       int err = -EEXIST;
+
+       conn = conn_lookup(session, info->cid);
+       if (!conn)
+               return err;
+
+       PRINT_INFO_PR("Deleting connection with initiator %s (%p)",
+               conn->session->initiator_name, conn);
+
+       mark_conn_closed(conn);
+
+       return 0;
+}
diff --git a/iscsi-scst/kernel/digest.c b/iscsi-scst/kernel/digest.c
new file mode 100644 (file)
index 0000000..26848c8
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * iSCSI digest handling.
+ * (C) 2004 - 2006 Xiranet Communications GmbH <arne.redlich@xiranet.com>
+ * This code is licensed under the GPL.
+ */
+#include <asm/types.h>
+#include <asm/scatterlist.h>
+
+#include "iscsi.h"
+#include "digest.h"
+#include <linux/crc32c.h>
+
+void digest_alg_available(unsigned int *val)
+{
+#if CONFIG_LIBCRC32C_MODULE || CONFIG_LIBCRC32C
+       int crc32c = 1;
+#else
+       int crc32c = 0;
+#endif
+
+       if ((*val & DIGEST_CRC32C) && !crc32c) {
+               PRINT_ERROR_PR("%s", "CRC32C digest algorithm not available "
+                       "in kernel");
+               *val |= ~DIGEST_CRC32C;
+       }
+}
+
+/**
+ * initialize support for digest calculation.
+ *
+ * digest_init -
+ * @conn: ptr to connection to make use of digests
+ *
+ * @return: 0 on success, < 0 on error
+ */
+int digest_init(struct iscsi_conn *conn)
+{
+       if (!(conn->hdigest_type & DIGEST_ALL))
+               conn->hdigest_type = DIGEST_NONE;
+
+       if (!(conn->ddigest_type & DIGEST_ALL))
+               conn->ddigest_type = DIGEST_NONE;
+
+       return 0;
+}
+
+/* Copied from linux-iscsi initiator and slightly adjusted */
+#define SETSG(sg, p, l) do {                                   \
+       (sg).page = virt_to_page((p));                          \
+       (sg).offset = ((unsigned long)(p) & ~PAGE_MASK);        \
+       (sg).length = (l);                                      \
+} while (0)
+
+static u32 evaluate_crc32_from_sg(struct scatterlist *sg, int total,
+       int pad_bytes)
+{
+       u32 crc = ~0;
+
+#ifdef DEBUG_DIGEST_FAILURES
+       if (((scst_random() % 100000) == 752)) {
+               PRINT_INFO_PR("%s", "Simulating digest failure");
+               return 0;
+       }
+#endif
+
+#if CONFIG_LIBCRC32C_MODULE || CONFIG_LIBCRC32C
+       while (total > 0) {
+               int d = min(min(total, (int)(sg->length)),
+                       (int)(PAGE_SIZE - sg->offset));
+
+               crc = crc32c(crc, page_address(sg->page) + sg->offset, d);
+               total -= d;
+               sg++;
+       }
+
+       if (pad_bytes) {
+               u32 padding = 0;
+               /*
+                * Digest includes also padding for aligned pdu length, hopefully
+                * it is always filled with 0s in pdu (according to crypto/crc32c.c
+                */
+               crc = crc32c(crc, (u8 *)&padding, pad_bytes);
+       }
+#endif
+
+       return ~cpu_to_le32(crc);
+}
+
+static u32 digest_header(struct iscsi_pdu *pdu)
+{
+       struct scatterlist sg[2];
+       unsigned int nbytes = sizeof(struct iscsi_hdr);
+
+       SETSG(sg[0], &pdu->bhs, nbytes);
+       if (pdu->ahssize) {
+               SETSG(sg[1], pdu->ahs, pdu->ahssize);
+               nbytes += pdu->ahssize;
+       }
+       return evaluate_crc32_from_sg(sg, nbytes, 0);
+}
+
+static u32 digest_data(struct iscsi_cmnd *req, u32 osize, u32 offset)
+{
+       struct scatterlist *sg = req->sg;
+       int idx, count;
+       struct scatterlist saved_sg;
+       u32 size = (osize + 3) & ~3;
+       u32 crc;
+
+       offset += sg[0].offset;
+       idx = offset >> PAGE_SHIFT;
+       offset &= ~PAGE_MASK;
+       
+       count = get_pgcnt(size, offset);
+       sBUG_ON(idx + count > get_pgcnt(req->bufflen, 0));
+       sBUG_ON(count > ISCSI_CONN_IOV_MAX);
+
+       saved_sg = sg[idx];
+       sg[idx].offset = offset;
+       sg[idx].length -= offset - saved_sg.offset;
+
+       crc = evaluate_crc32_from_sg(sg + idx, osize, size - osize);
+
+       sg[idx] = saved_sg;
+       return crc;
+}
+
+int digest_rx_header(struct iscsi_cmnd *cmnd)
+{
+       u32 crc;
+
+       crc = digest_header(&cmnd->pdu);
+       if (crc != cmnd->hdigest) {
+               PRINT_ERROR_PR("%s", "RX header digest failed");
+               return -EIO;
+       } else
+               TRACE_DBG("RX header digest OK for cmd %p", cmnd);
+
+       return 0;
+}
+
+void digest_tx_header(struct iscsi_cmnd *cmnd)
+{
+       cmnd->hdigest = digest_header(&cmnd->pdu);
+       TRACE_DBG("TX header digest for cmd %p: %x", cmnd, cmnd->hdigest);
+}
+
+int digest_rx_data(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_cmnd *req;
+       struct iscsi_data_out_hdr *req_hdr;
+       u32 offset, crc;
+       int res = 0;
+
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_SCSI_DATA_OUT:
+               req = cmnd->cmd_req;
+               req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
+               offset = be32_to_cpu(req_hdr->buffer_offset);
+               break;
+
+       case ISCSI_OP_SCSI_REJECT:
+       case ISCSI_OP_PDU_REJECT:
+       case ISCSI_OP_DATA_REJECT:
+               goto out;
+
+       default:
+               req = cmnd;
+               offset = 0;
+       }
+
+       crc = digest_data(req, cmnd->pdu.datasize, offset);
+
+       if (crc != cmnd->ddigest) {
+               PRINT_ERROR_PR("%s", "RX data digest failed");
+               res = -EIO;
+       } else
+               TRACE_DBG("RX data digest OK for cmd %p", cmnd);
+
+out:
+       return res;
+}
+
+void digest_tx_data(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_data_in_hdr *hdr;
+       u32 offset;
+
+       TRACE_DBG("%s:%d req %p, own_sg %d, sg %p, sgcnt %d cmnd %p, "
+               "own_sg %d, sg %p, sgcnt %d", __FUNCTION__, __LINE__,
+               cmnd->parent_req, cmnd->parent_req->own_sg,
+               cmnd->parent_req->sg, cmnd->parent_req->sg_cnt,
+               cmnd, cmnd->own_sg, cmnd->sg, cmnd->sg_cnt);
+
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_SCSI_DATA_IN:
+               hdr = (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs;
+               offset = be32_to_cpu(hdr->buffer_offset);
+               break;
+       default:
+               offset = 0;
+       }
+
+       /* 
+        * cmnd is used here regardless of its sg comes from parent or was
+        * allocated for this cmnd only, see cmnd_send_pdu()
+        */
+       cmnd->ddigest = digest_data(cmnd, cmnd->pdu.datasize, offset);
+       TRACE_DBG("TX data digest for cmd %p: %x (offset %d, opcode %x)", cmnd,
+               cmnd->ddigest, offset, cmnd_opcode(cmnd));
+}
diff --git a/iscsi-scst/kernel/digest.h b/iscsi-scst/kernel/digest.h
new file mode 100644 (file)
index 0000000..349a608
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * iSCSI digest handling.
+ * (C) 2004 Xiranet Communications GmbH <arne.redlich@xiranet.com>
+ * This code is licensed under the GPL.
+ */
+
+#ifndef __ISCSI_DIGEST_H__
+#define __ISCSI_DIGEST_H__
+
+extern void digest_alg_available(unsigned int *val);
+extern int digest_init(struct iscsi_conn *conn);
+
+extern int digest_rx_header(struct iscsi_cmnd *cmnd);
+extern int digest_rx_data(struct iscsi_cmnd *cmnd);
+
+extern void digest_tx_header(struct iscsi_cmnd *cmnd);
+extern void digest_tx_data(struct iscsi_cmnd *cmnd);
+
+#endif /* __ISCSI_DIGEST_H__ */
diff --git a/iscsi-scst/kernel/event.c b/iscsi-scst/kernel/event.c
new file mode 100644 (file)
index 0000000..2b0edc2
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Event notification code.
+ * (C) 2005 FUJITA Tomonori <tomof@acm.org>
+ * This code is licenced under the GPL.
+ *
+ * Some functions are based on audit code.
+ */
+
+#include <net/tcp.h>
+#include "iscsi_u.h"
+#include "iscsi.h"
+
+static struct sock *nl;
+static u32 iscsid_pid;
+
+static int event_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+{
+       u32 uid, pid, seq;
+       char *data;
+
+       pid  = NETLINK_CREDS(skb)->pid;
+       uid  = NETLINK_CREDS(skb)->uid;
+       seq  = nlh->nlmsg_seq;
+       data = NLMSG_DATA(nlh);
+
+       iscsid_pid = pid;
+
+       return 0;
+}
+
+static int event_recv_skb(struct sk_buff *skb)
+{
+       int err;
+       struct nlmsghdr *nlh;
+       u32 rlen;
+
+       while (skb->len >= NLMSG_SPACE(0)) {
+               nlh = (struct nlmsghdr *)skb->data;
+               if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+                       return 0;
+               rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+               if (rlen > skb->len)
+                       rlen = skb->len;
+               if ((err = event_recv_msg(skb, nlh))) {
+                       netlink_ack(skb, nlh, -err);
+               } else if (nlh->nlmsg_flags & NLM_F_ACK)
+                       netlink_ack(skb, nlh, 0);
+               skb_pull(skb, rlen);
+       }
+       return 0;
+}
+
+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);
+       }
+}
+
+static int notify(void *data, int len, int gfp_mask)
+{
+       struct sk_buff *skb;
+       struct nlmsghdr *nlh;
+       static u32 seq = 0;
+
+       if (!(skb = alloc_skb(NLMSG_SPACE(len), gfp_mask)))
+               return -ENOMEM;
+
+       nlh = __nlmsg_put(skb, iscsid_pid, seq++, NLMSG_DONE, len - sizeof(*nlh), 0);
+
+       memcpy(NLMSG_DATA(nlh), data, len);
+
+       return netlink_unicast(nl, skb, iscsid_pid, 0);
+}
+
+int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic)
+{
+       int err;
+       struct iscsi_event event;
+
+       event.tid = tid;
+       event.sid = sid;
+       event.cid = cid;
+       event.state = state;
+
+       err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_event)), 0);
+
+       return err;
+}
+
+int __init event_init(void)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
+       nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv, THIS_MODULE);
+#else
+       nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv, NULL,
+               THIS_MODULE);
+#endif
+       if (!nl) {
+               PRINT_ERROR_PR("%s", "netlink_kernel_create() failed");
+               return -ENOMEM;
+       } else
+               return 0;
+}
+
+void event_exit(void)
+{
+       if (nl)
+               sock_release(nl->sk_socket);
+}
diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c
new file mode 100644 (file)
index 0000000..b152f8d
--- /dev/null
@@ -0,0 +1,2425 @@
+/*
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <linux/module.h>
+#include <linux/hash.h>
+#include <linux/kthread.h>
+#include <net/tcp.h>
+#include <scsi/scsi.h>
+
+#include "iscsi.h"
+#include "digest.h"
+
+#ifndef NET_PAGE_CALLBACKS_DEFINED
+#warning Patch put_page_callback.patch not applied on your kernel. ISCSI-SCST \
+       will run in the performance degraded mode. Refer README file for \
+       details.
+#endif
+
+#define ISCSI_INIT_WRITE_WAKE          0x1
+#define ISCSI_INIT_WRITE_REMOVE_HASH   0x2
+
+static int ctr_major;
+static char ctr_name[] = "iscsi-scst-ctl";
+static int iscsi_template_registered;
+
+#if defined(DEBUG) || defined(TRACING)
+unsigned long iscsi_trace_flag = ISCSI_DEFAULT_LOG_FLAGS;
+#endif
+
+static struct kmem_cache *iscsi_cmnd_cache;
+
+spinlock_t iscsi_rd_lock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(iscsi_rd_list);
+DECLARE_WAIT_QUEUE_HEAD(iscsi_rd_waitQ);
+
+spinlock_t iscsi_wr_lock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(iscsi_wr_list);
+DECLARE_WAIT_QUEUE_HEAD(iscsi_wr_waitQ);
+
+static char dummy_data[1024];
+
+struct iscsi_thread_t {
+       struct task_struct *thr;
+       struct list_head threads_list_entry;
+};
+
+static LIST_HEAD(iscsi_threads_list);
+
+static void cmnd_remove_hash(struct iscsi_cmnd *cmnd);
+static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status);
+static void cmnd_prepare_skip_pdu(struct iscsi_cmnd *cmnd);
+
+static inline u32 cmnd_write_size(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd);
+
+       if (hdr->flags & ISCSI_CMD_WRITE)
+               return be32_to_cpu(hdr->data_length);
+       return 0;
+}
+
+static inline u32 cmnd_read_size(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd);
+
+       if (hdr->flags & ISCSI_CMD_READ) {
+               struct iscsi_rlength_ahdr *ahdr =
+                       (struct iscsi_rlength_ahdr *)cmnd->pdu.ahs;
+
+               if (!(hdr->flags & ISCSI_CMD_WRITE))
+                       return be32_to_cpu(hdr->data_length);
+               if (ahdr && ahdr->ahstype == ISCSI_AHSTYPE_RLENGTH)
+                       return be32_to_cpu(ahdr->read_length);
+       }
+       return 0;
+}
+
+static inline void iscsi_restart_cmnd(struct iscsi_cmnd *cmnd)
+{
+       cmnd->scst_state = ISCSI_CMD_STATE_RESTARTED;
+       scst_restart_cmd(cmnd->scst_cmd, SCST_PREPROCESS_STATUS_SUCCESS,
+               SCST_CONTEXT_THREAD);
+}
+
+struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, struct iscsi_cmnd *parent)
+{
+       struct iscsi_cmnd *cmnd;
+
+       /* ToDo: __GFP_NOFAIL?? */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17)
+       cmnd = kmem_cache_alloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL);
+       memset(cmnd, 0, sizeof(*cmnd));
+#else
+       cmnd = kmem_cache_zalloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL);
+#endif
+
+       atomic_set(&cmnd->ref_cnt, 1);
+       cmnd->scst_state = ISCSI_CMD_STATE_NEW;
+       cmnd->conn = conn;
+       cmnd->parent_req = parent;
+       init_waitqueue_head(&cmnd->scst_waitQ);
+
+       if (parent == NULL) {
+               atomic_inc(&conn->conn_ref_cnt);
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+               atomic_set(&cmnd->net_ref_cnt, 0);
+#endif         
+               cmnd->target = conn->target;
+               spin_lock_init(&cmnd->rsp_cmd_lock);
+               INIT_LIST_HEAD(&cmnd->rsp_cmd_list);
+               INIT_LIST_HEAD(&cmnd->rx_ddigest_cmd_list);
+
+               spin_lock_bh(&conn->cmd_list_lock);
+               list_add_tail(&cmnd->cmd_list_entry, &conn->cmd_list);
+               spin_unlock_bh(&conn->cmd_list_lock);
+       }
+
+       TRACE_DBG("conn %p, parent %p, cmnd %p", conn, parent, cmnd);
+       return cmnd;
+}
+
+/* Frees a command. Also frees the additional header. */
+void cmnd_free(struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("%p", cmnd);
+
+       /* Catch users from cmd_list or rsp_cmd_list */
+       EXTRACHECKS_BUG_ON(atomic_read(&cmnd->ref_cnt) != 0);
+
+       kfree(cmnd->pdu.ahs);
+
+       if (unlikely(cmnd->on_write_list)) {
+               struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
+
+               PRINT_ERROR_PR("cmnd %p still on some list?, %x, %x, %x, %x, %x, %x, %x",
+                       cmnd, req->opcode, req->scb[0], req->flags, req->itt,
+                       be32_to_cpu(req->data_length),
+                       req->cmd_sn, be32_to_cpu(cmnd->pdu.datasize));
+
+               if (cmnd->parent_req) {
+                       struct iscsi_scsi_cmd_hdr *req =
+                                       cmnd_hdr(cmnd->parent_req);
+                       PRINT_ERROR_PR("%p %x %u", req, req->opcode, req->scb[0]);
+               }
+               sBUG();
+       }
+
+       kmem_cache_free(iscsi_cmnd_cache, cmnd);
+       return;
+}
+
+void cmnd_done(struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("%p", cmnd);
+
+       if (unlikely(cmnd->tmfabort)) {
+               TRACE_MGMT_DBG("Done aborted cmd %p (scst cmd %p, state %d)",
+                       cmnd, cmnd->scst_cmd, cmnd->scst_state);
+       }
+
+       if (cmnd->parent_req == NULL) {
+               struct iscsi_conn *conn = cmnd->conn;
+               TRACE_DBG("Deleting req %p from conn %p", cmnd, conn);
+               spin_lock_bh(&conn->cmd_list_lock);
+               list_del(&cmnd->cmd_list_entry);
+               spin_unlock_bh(&conn->cmd_list_lock);
+
+               smp_mb__before_atomic_dec();
+               atomic_dec(&conn->conn_ref_cnt);
+
+               EXTRACHECKS_BUG_ON(!list_empty(&cmnd->rsp_cmd_list));
+               EXTRACHECKS_BUG_ON(!list_empty(&cmnd->rx_ddigest_cmd_list));
+
+               /* Order between above and below code is important! */
+
+               if (cmnd->scst_cmd) {
+                       switch(cmnd->scst_state) {
+                       case ISCSI_CMD_STATE_AFTER_PREPROC:
+                               TRACE_DBG("%s", "AFTER_PREPROC");
+                               cmnd->scst_state = ISCSI_CMD_STATE_RESTARTED;
+                               scst_restart_cmd(cmnd->scst_cmd,
+                                       SCST_PREPROCESS_STATUS_ERROR_FATAL,
+                                       SCST_CONTEXT_THREAD);
+                               break;
+                       case ISCSI_CMD_STATE_PROCESSED:
+                               TRACE_DBG("%s", "PROCESSED");
+                               scst_tgt_cmd_done(cmnd->scst_cmd);
+                               break;
+                       default:
+                               PRINT_ERROR_PR("Unexpected cmnd scst state %d",
+                                       cmnd->scst_state);
+                               sBUG();
+                               break;
+                       }
+               }
+       } else {
+               EXTRACHECKS_BUG_ON(cmnd->scst_cmd != NULL);
+
+               spin_lock_bh(&cmnd->parent_req->rsp_cmd_lock);
+               TRACE_DBG("Deleting rsp %p from parent %p", cmnd,
+                       cmnd->parent_req);
+               list_del(&cmnd->rsp_cmd_list_entry);
+               spin_unlock_bh(&cmnd->parent_req->rsp_cmd_lock);
+
+               cmnd_put(cmnd->parent_req);
+       }
+
+       /* Order between above and below code is important! */
+
+       if (cmnd->own_sg) {
+               TRACE_DBG("%s", "own_sg");
+               scst_free(cmnd->sg, cmnd->sg_cnt);
+#ifdef DEBUG
+               cmnd->own_sg = 0;
+               cmnd->sg = NULL;
+               cmnd->sg_cnt = -1;
+#endif
+       }
+
+       cmnd_free(cmnd);
+       return;
+}
+
+void req_cmnd_release_force(struct iscsi_cmnd *req, int flags)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_conn *conn = req->conn;
+
+       TRACE_ENTRY();
+
+       TRACE_DBG("%p", req);
+
+       if (flags & ISCSI_FORCE_RELEASE_WRITE) {
+               spin_lock(&conn->write_list_lock);
+               while(!list_empty(&conn->write_list)) {
+                       rsp = list_entry(conn->write_list.next,
+                               struct iscsi_cmnd, write_list_entry);
+                       cmd_del_from_write_list(rsp);
+                       spin_unlock(&conn->write_list_lock);
+
+                       cmnd_put(rsp);
+
+                       spin_lock(&conn->write_list_lock);
+               }
+               spin_unlock(&conn->write_list_lock);
+       }
+
+again:
+       spin_lock_bh(&req->rsp_cmd_lock);
+       list_for_each_entry(rsp, &req->rsp_cmd_list, rsp_cmd_list_entry) {
+               int f;
+
+               if (rsp->on_write_list || rsp->write_processing_started ||
+                   rsp->force_cleanup_done)
+                       continue;
+
+               spin_unlock_bh(&req->rsp_cmd_lock);
+
+               /* 
+                * Recheck is necessary to not take write_list_lock under
+                * rsp_cmd_lock.
+                */
+               spin_lock(&conn->write_list_lock);
+               f = rsp->on_write_list || rsp->write_processing_started ||
+                       rsp->force_cleanup_done;
+               spin_unlock(&conn->write_list_lock);
+               if (f)
+                       goto again;
+
+               rsp->force_cleanup_done = 1;
+               cmnd_put(rsp);
+
+               goto again;
+       }
+       spin_unlock_bh(&req->rsp_cmd_lock);
+
+       req_cmnd_release(req);
+
+       TRACE_EXIT();
+       return;
+}
+
+void req_cmnd_release(struct iscsi_cmnd *req)
+{
+       struct iscsi_cmnd *c, *t;
+
+       TRACE_ENTRY();
+
+       TRACE_DBG("%p", req);
+
+#ifdef EXTRACHECKS
+       sBUG_ON(req->release_called);
+       req->release_called = 1;
+#endif
+
+       if (unlikely(req->tmfabort)) {
+               TRACE_MGMT_DBG("Release aborted req cmd %p (scst cmd %p, "
+                       "state %d)", req, req->scst_cmd, req->scst_state);
+       }
+
+       sBUG_ON(req->parent_req != NULL);
+
+       list_for_each_entry_safe(c, t, &req->rx_ddigest_cmd_list,
+                               rx_ddigest_cmd_list_entry) {
+               TRACE_DBG("Deleting RX ddigest cmd %p from digest "
+                       "list of req %p", c, req);
+               list_del(&c->rx_ddigest_cmd_list_entry);
+               cmnd_put(c);
+       }
+
+       if (req->hashed)
+               cmnd_remove_hash(req);
+
+       cmnd_put(req);
+
+       TRACE_EXIT();
+       return;
+}
+
+void rsp_cmnd_release(struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("%p", cmnd);
+
+#ifdef EXTRACHECKS
+       sBUG_ON(cmnd->release_called);
+       cmnd->release_called = 1;
+#endif
+
+       sBUG_ON(cmnd->hashed);
+       sBUG_ON(cmnd->parent_req == NULL);
+
+       if (unlikely(cmnd->tmfabort)) {
+               TRACE_MGMT_DBG("Release aborted rsp cmd %p (parent req %p, "
+                       "scst cmd %p, state %d)", cmnd, cmnd->parent_req,
+                       cmnd->parent_req->scst_cmd,
+                       cmnd->parent_req->scst_state);
+       }
+
+       cmnd_put(cmnd);
+       return;
+}
+
+/**
+ * create a new command used as response.
+ *
+ * iscsi_cmnd_create_rsp_cmnd - 
+ * @cmnd: ptr to request command
+ *
+ * @return    ptr to response command or NULL
+ */
+static struct iscsi_cmnd *iscsi_cmnd_create_rsp_cmnd(struct iscsi_cmnd *parent)
+{
+       struct iscsi_cmnd *rsp = cmnd_alloc(parent->conn, parent);
+
+       spin_lock_bh(&parent->rsp_cmd_lock);
+       TRACE_DBG("Adding rsp %p to parent %p", rsp, parent);
+       list_add_tail(&rsp->rsp_cmd_list_entry, &parent->rsp_cmd_list);
+       spin_unlock_bh(&parent->rsp_cmd_lock);
+       cmnd_get(parent);
+       return rsp;
+}
+
+static inline struct iscsi_cmnd *get_rsp_cmnd(struct iscsi_cmnd *req)
+{
+       struct iscsi_cmnd *res;
+
+       /* Currently this lock isn't needed, but just in case.. */
+       spin_lock_bh(&req->rsp_cmd_lock);
+       res = list_entry(req->rsp_cmd_list.prev, struct iscsi_cmnd,
+               rsp_cmd_list_entry);
+       spin_unlock_bh(&req->rsp_cmd_lock);
+
+       return res;
+}
+
+static void iscsi_cmnds_init_write(struct list_head *send, int flags)
+{
+       struct iscsi_cmnd *cmnd = list_entry(send->next, struct iscsi_cmnd, 
+                                               write_list_entry);
+       struct iscsi_conn *conn = cmnd->conn;
+       struct list_head *pos, *next;
+
+       /*
+        * If we don't remove hashed req cmd from the hash list here, before
+        * submitting it for transmittion, we will have a race, when for
+        * some reason cmd's release is delayed after transmittion and
+        * initiator sends cmd with the same ITT => this command will be
+        * erroneously rejected as a duplicate.
+        */
+       if ((flags & ISCSI_INIT_WRITE_REMOVE_HASH) && cmnd->parent_req->hashed &&
+           (cmnd->parent_req->outstanding_r2t == 0))
+               cmnd_remove_hash(cmnd->parent_req);
+
+       list_for_each_safe(pos, next, send) {
+               cmnd = list_entry(pos, struct iscsi_cmnd, write_list_entry);
+
+               TRACE_DBG("%p:%x", cmnd, cmnd_opcode(cmnd));
+
+               sBUG_ON(conn != cmnd->conn);
+
+               if (!(conn->ddigest_type & DIGEST_NONE) &&
+                   (cmnd->pdu.datasize != 0))
+                       digest_tx_data(cmnd);
+
+               list_del(&cmnd->write_list_entry);
+
+               spin_lock(&conn->write_list_lock);
+               cmd_add_on_write_list(conn, cmnd);
+               spin_unlock(&conn->write_list_lock);
+       }
+       
+
+       if (flags & ISCSI_INIT_WRITE_WAKE)
+               iscsi_make_conn_wr_active(conn);
+}
+
+static void iscsi_cmnd_init_write(struct iscsi_cmnd *cmnd, int flags)
+{
+       LIST_HEAD(head);
+
+       if (unlikely(cmnd->on_write_list)) {
+               PRINT_ERROR_PR("cmd already on write list (%x %x %x %x %u %u "
+                       "%u %u %u %u %u %d %d",
+                       cmnd_itt(cmnd), cmnd_ttt(cmnd), cmnd_opcode(cmnd),
+                       cmnd_scsicode(cmnd), cmnd->r2t_sn,
+                       cmnd->r2t_length, cmnd->is_unsolicited_data,
+                       cmnd->target_task_tag, cmnd->outstanding_r2t,
+                       cmnd->hdigest, cmnd->ddigest,
+                       list_empty(&cmnd->rsp_cmd_list), cmnd->hashed);
+               sBUG();
+       }
+       list_add(&cmnd->write_list_entry, &head);
+       iscsi_cmnds_init_write(&head, flags);
+}
+
+static void iscsi_set_datasize(struct iscsi_cmnd *cmnd, u32 offset, u32 size)
+{
+       cmnd->pdu.datasize = size;
+
+       if (cmnd->pdu.datasize & 3) {
+               int idx = (offset + cmnd->pdu.datasize) >> PAGE_SHIFT;
+               u8 *p = (u8 *)page_address(cmnd->sg[idx].page) + 
+                       ((offset + cmnd->pdu.datasize) & ~PAGE_MASK);
+               int i = 4 - (cmnd->pdu.datasize & 3);
+               while (i--) 
+                   *p++ = 0;
+       }
+}
+
+static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
+       struct iscsi_data_in_hdr *rsp_hdr;
+       u32 pdusize, expsize, scsisize, size, offset, sn;
+       LIST_HEAD(send);
+
+       TRACE_DBG("req %p", req);
+       pdusize = req->conn->session->sess_param.max_xmit_data_length;
+       expsize = cmnd_read_size(req);
+       size = min(expsize, (u32)req->bufflen);
+       offset = 0;
+       sn = 0;
+
+       while (1) {
+               rsp = iscsi_cmnd_create_rsp_cmnd(req);
+               TRACE_DBG("rsp %p", rsp);
+               rsp->sg = req->sg;
+               rsp->bufflen = req->bufflen;
+               rsp_hdr = (struct iscsi_data_in_hdr *)&rsp->pdu.bhs;
+
+               rsp_hdr->opcode = ISCSI_OP_SCSI_DATA_IN;
+               rsp_hdr->itt = req_hdr->itt;
+               rsp_hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+               rsp_hdr->buffer_offset = cpu_to_be32(offset);
+               rsp_hdr->data_sn = cpu_to_be32(sn);
+
+               if (size <= pdusize) {
+                       iscsi_set_datasize(rsp, offset, size);
+                       if (send_status) {
+                               TRACE_DBG("status %x", status);
+                               rsp_hdr->flags =
+                                       ISCSI_FLG_FINAL | ISCSI_FLG_STATUS;
+                               rsp_hdr->cmd_status = status;
+                       }
+                       scsisize = req->bufflen;
+                       if (scsisize < expsize) {
+                               rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                               size = expsize - scsisize;
+                       } else if (scsisize > expsize) {
+                               rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
+                               size = scsisize - expsize;
+                       } else
+                               size = 0;
+                       rsp_hdr->residual_count = cpu_to_be32(size);
+                       list_add_tail(&rsp->write_list_entry, &send);
+                       break;
+               }
+
+               iscsi_set_datasize(rsp, offset, pdusize);
+
+               size -= pdusize;
+               offset += pdusize;
+               sn++;
+
+               list_add_tail(&rsp->write_list_entry, &send);
+       }
+       iscsi_cmnds_init_write(&send, ISCSI_INIT_WRITE_REMOVE_HASH);
+}
+
+static struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req, int status,
+       const u8 *sense_buf, int sense_len)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_scsi_rsp_hdr *rsp_hdr;
+       struct iscsi_sense_data *sense;
+       struct scatterlist *sg;
+
+       rsp = iscsi_cmnd_create_rsp_cmnd(req);
+       TRACE_DBG("%p", rsp);
+
+       rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
+       rsp_hdr->opcode = ISCSI_OP_SCSI_RSP;
+       rsp_hdr->flags = ISCSI_FLG_FINAL;
+       rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED;
+       rsp_hdr->cmd_status = status;
+       rsp_hdr->itt = cmnd_hdr(req)->itt;
+
+       if (status == SAM_STAT_CHECK_CONDITION) {
+               TRACE_DBG("%s", "CHECK_CONDITION");
+               /* ToDo: __GFP_NOFAIL ?? */
+               sg = rsp->sg = scst_alloc(PAGE_SIZE, GFP_KERNEL|__GFP_NOFAIL, 0,
+                                       &rsp->sg_cnt);
+               if (sg == NULL) {
+                       /* ToDo(); */
+               }
+               rsp->own_sg = 1;
+               sense = (struct iscsi_sense_data *)page_address(sg[0].page);
+               sense->length = cpu_to_be16(sense_len);
+               memcpy(sense->data, sense_buf, sense_len);
+               rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + sense_len;
+               rsp->bufflen = (rsp->pdu.datasize + 3) & -4;
+               if (rsp->bufflen - rsp->pdu.datasize) {
+                   int i = rsp->pdu.datasize;
+                   u8 *p = (u8 *)sense + i;
+                   
+                   while (i < rsp->bufflen) {
+                       *p ++ = 0;
+                       i++;
+                   }
+               }
+       } else {
+               rsp->pdu.datasize = 0;
+               rsp->bufflen = 0;
+       }
+
+       return rsp;
+}
+
+static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req,
+       u8 sense_key, u8 asc, u8 ascq)
+{
+       u8 sense[14];
+       memset(sense, 0, sizeof(sense));
+       sense[0] = 0xf0;
+       sense[2] = sense_key;
+       sense[7] = 6;   // Additional sense length
+       sense[12] = asc;
+       sense[13] = ascq;
+       return create_status_rsp(req, SAM_STAT_CHECK_CONDITION, sense,
+               sizeof(sense));
+}
+
+static void iscsi_cmnd_reject(struct iscsi_cmnd *req, int reason)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_reject_hdr *rsp_hdr;
+       struct scatterlist *sg;
+       char *addr;
+
+       TRACE_MGMT_DBG("Reject: req %p, reason %x", req, reason);
+
+       rsp = iscsi_cmnd_create_rsp_cmnd(req);
+       rsp_hdr = (struct iscsi_reject_hdr *)&rsp->pdu.bhs;
+
+       rsp_hdr->opcode = ISCSI_OP_REJECT;
+       rsp_hdr->ffffffff = ISCSI_RESERVED_TAG;
+       rsp_hdr->reason = reason;
+
+       /* ToDo: __GFP_NOFAIL ?? */
+       sg = rsp->sg = scst_alloc(PAGE_SIZE, GFP_KERNEL|__GFP_NOFAIL, 0,
+                               &rsp->sg_cnt);
+       if (sg == NULL) {
+               /* ToDo(); */
+       }
+       rsp->own_sg = 1;
+       addr = page_address(sg[0].page);
+       clear_page(addr);
+       memcpy(addr, &req->pdu.bhs, sizeof(struct iscsi_hdr));
+       rsp->bufflen = rsp->pdu.datasize = sizeof(struct iscsi_hdr);
+       cmnd_prepare_skip_pdu(req);
+
+       req->pdu.bhs.opcode = ISCSI_OP_PDU_REJECT;
+}
+
+static u32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       struct iscsi_session *sess = conn->session;
+       u32 res;
+
+       spin_lock(&sess->sn_lock);
+
+       if (set_stat_sn)
+               cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn++);
+       cmnd->pdu.bhs.exp_sn = cpu_to_be32(sess->exp_cmd_sn);
+       cmnd->pdu.bhs.max_sn = cpu_to_be32(sess->exp_cmd_sn + sess->max_queued_cmnds);
+
+       res = cpu_to_be32(conn->stat_sn);
+
+       spin_unlock(&sess->sn_lock);
+       return res;
+}
+
+/* Called under sn_lock */
+static void __update_stat_sn(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       u32 exp_stat_sn;
+
+       cmnd->pdu.bhs.exp_sn = exp_stat_sn = be32_to_cpu(cmnd->pdu.bhs.exp_sn);
+       TRACE_DBG("%x,%x", cmnd_opcode(cmnd), exp_stat_sn);
+       if ((int)(exp_stat_sn - conn->exp_stat_sn) > 0 &&
+           (int)(exp_stat_sn - conn->stat_sn) <= 0) {
+               // free pdu resources
+               cmnd->conn->exp_stat_sn = exp_stat_sn;
+       }
+}
+
+static inline void update_stat_sn(struct iscsi_cmnd *cmnd)
+{
+       spin_lock(&cmnd->conn->session->sn_lock);
+       __update_stat_sn(cmnd);
+       spin_unlock(&cmnd->conn->session->sn_lock);
+}
+
+/* Called under sn_lock */
+static int check_cmd_sn(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_session *session = cmnd->conn->session;
+       u32 cmd_sn;
+
+       cmnd->pdu.bhs.sn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.sn);
+       TRACE_DBG("%d(%d)", cmd_sn, session->exp_cmd_sn);
+       if ((s32)(cmd_sn - session->exp_cmd_sn) >= 0)
+               return 0;
+       PRINT_ERROR_PR("sequence error (%x,%x)", cmd_sn, session->exp_cmd_sn);
+       return -ISCSI_REASON_PROTOCOL_ERROR;
+}
+
+static inline struct iscsi_cmnd *__cmnd_find_hash(struct iscsi_session *session,
+       u32 itt, u32 ttt)
+{
+       struct list_head *head;
+       struct iscsi_cmnd *cmnd;
+
+       head = &session->cmnd_hash[cmnd_hashfn(itt)];
+
+       list_for_each_entry(cmnd, head, hash_list_entry) {
+               if (cmnd->pdu.bhs.itt == itt) {
+                       if ((ttt != ISCSI_RESERVED_TAG) && (ttt != cmnd->target_task_tag))
+                               continue;
+                       return cmnd;
+               }
+       }
+       return NULL;
+}
+
+static struct iscsi_cmnd *cmnd_find_hash(struct iscsi_session *session,
+       u32 itt, u32 ttt)
+{
+       struct iscsi_cmnd *cmnd;
+
+       spin_lock(&session->cmnd_hash_lock);
+       cmnd = __cmnd_find_hash(session, itt, ttt);
+       spin_unlock(&session->cmnd_hash_lock);
+
+       return cmnd;
+}
+
+static struct iscsi_cmnd *cmnd_find_hash_get(struct iscsi_session *session,
+       u32 itt, u32 ttt)
+{
+       struct iscsi_cmnd *cmnd;
+
+       spin_lock(&session->cmnd_hash_lock);
+       cmnd = __cmnd_find_hash(session, itt, ttt);
+       cmnd_get(cmnd);
+       spin_unlock(&session->cmnd_hash_lock);
+
+       return cmnd;
+}
+
+static int cmnd_insert_hash(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_session *session = cmnd->conn->session;
+       struct iscsi_cmnd *tmp;
+       struct list_head *head;
+       int err = 0;
+       u32 itt = cmnd->pdu.bhs.itt;
+
+       TRACE_DBG("%p:%x", cmnd, itt);
+       if (itt == ISCSI_RESERVED_TAG) {
+               err = -ISCSI_REASON_PROTOCOL_ERROR;
+               goto out;
+       }
+
+       spin_lock(&session->cmnd_hash_lock);
+
+       head = &session->cmnd_hash[cmnd_hashfn(cmnd->pdu.bhs.itt)];
+
+       tmp = __cmnd_find_hash(session, itt, ISCSI_RESERVED_TAG);
+       if (!tmp) {
+               list_add_tail(&cmnd->hash_list_entry, head);
+               cmnd->hashed = 1;
+       } else
+               err = -ISCSI_REASON_TASK_IN_PROGRESS;
+
+       spin_unlock(&session->cmnd_hash_lock);
+
+       if (!err) {
+               spin_lock(&session->sn_lock);
+               __update_stat_sn(cmnd);
+               err = check_cmd_sn(cmnd);
+               spin_unlock(&session->sn_lock);
+       }
+
+out:
+       return err;
+}
+
+static void cmnd_remove_hash(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_session *session = cmnd->conn->session;
+       struct iscsi_cmnd *tmp;
+
+       spin_lock(&session->cmnd_hash_lock);
+
+       tmp = __cmnd_find_hash(session, cmnd->pdu.bhs.itt, ISCSI_RESERVED_TAG);
+
+       if (tmp && tmp == cmnd) {
+               list_del(&cmnd->hash_list_entry);
+               cmnd->hashed = 0;
+       } else {
+               PRINT_ERROR_PR("%p:%x not found", cmnd, cmnd_itt(cmnd));
+       }
+
+       spin_unlock(&session->cmnd_hash_lock);
+}
+
+static void cmnd_prepare_skip_pdu(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       struct scatterlist *sg = cmnd->sg;
+       char *addr;
+       u32 size;
+       int i;
+
+       TRACE_MGMT_DBG("Skipping (%p, %x %x %x %u, %p, scst state %d)", cmnd,
+               cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd_hdr(cmnd)->scb[0],
+               cmnd->pdu.datasize, cmnd->scst_cmd, cmnd->scst_state);
+
+       iscsi_extracheck_is_rd_thread(conn);
+
+       if (!(size = cmnd->pdu.datasize))
+               return;
+
+       if (sg == NULL) {
+               /* ToDo: __GFP_NOFAIL ?? */
+               sg = cmnd->sg = scst_alloc(PAGE_SIZE, GFP_KERNEL|__GFP_NOFAIL,
+                                       0, &cmnd->sg_cnt);
+               if (sg == NULL) {
+                       /* ToDo(); */
+               }
+               cmnd->own_sg = 1;
+               cmnd->bufflen = PAGE_SIZE;
+       }
+
+       addr = page_address(sg[0].page);
+       sBUG_ON(addr == NULL);
+       size = (size + 3) & -4;
+       conn->read_size = size;
+       for (i = 0; size > PAGE_SIZE; i++, size -= cmnd->bufflen) {
+               sBUG_ON(i >= ISCSI_CONN_IOV_MAX);
+               conn->read_iov[i].iov_base = addr;
+               conn->read_iov[i].iov_len = cmnd->bufflen;
+       }
+       conn->read_iov[i].iov_base = addr;
+       conn->read_iov[i].iov_len = size;
+       conn->read_msg.msg_iov = conn->read_iov;
+       conn->read_msg.msg_iovlen = ++i;
+}
+
+static void cmnd_prepare_skip_pdu_set_resid(struct iscsi_cmnd *req)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_scsi_rsp_hdr *rsp_hdr;
+       u32 size;
+
+       TRACE_DBG("%p", req);
+
+       rsp = get_rsp_cmnd(req);
+       rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
+       if (cmnd_opcode(rsp) != ISCSI_OP_SCSI_RSP) {
+               PRINT_ERROR_PR("unexpected response command %u", cmnd_opcode(rsp));
+               return;
+       }
+
+       size = cmnd_write_size(req);
+       if (size) {
+               rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+               rsp_hdr->residual_count = cpu_to_be32(size);
+       }
+       size = cmnd_read_size(req);
+       if (size) {
+               if (cmnd_hdr(req)->flags & ISCSI_CMD_WRITE) {
+                       rsp_hdr->flags |= ISCSI_FLG_BIRESIDUAL_UNDERFLOW;
+                       rsp_hdr->bi_residual_count = cpu_to_be32(size);
+               } else {
+                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                       rsp_hdr->residual_count = cpu_to_be32(size);
+               }
+       }
+       req->pdu.bhs.opcode =
+               (req->pdu.bhs.opcode & ~ISCSI_OPCODE_MASK) | ISCSI_OP_SCSI_REJECT;
+
+       cmnd_prepare_skip_pdu(req);
+}
+
+static int cmnd_prepare_recv_pdu(struct iscsi_conn *conn,
+       struct iscsi_cmnd *cmd, u32 offset, u32 size)
+{
+       struct scatterlist *sg = cmd->sg;
+       int bufflen = cmd->bufflen;
+       int idx, i;
+       char *addr;
+       int res = 0;
+
+       TRACE_DBG("%p %u,%u", cmd->sg, offset, size);
+
+       iscsi_extracheck_is_rd_thread(conn);
+
+       if ((offset >= bufflen) ||
+           (offset + size > bufflen)) {
+               PRINT_ERROR_PR("Wrong ltn (%u %u %u)", offset, size, bufflen);
+               mark_conn_closed(conn);
+               res = -EIO;
+               goto out;
+       }
+
+       offset += sg[0].offset;
+       idx = offset >> PAGE_SHIFT;
+       offset &= ~PAGE_MASK;
+
+       conn->read_msg.msg_iov = conn->read_iov;
+       conn->read_size = size = (size + 3) & -4;
+
+       i = 0;
+       while (1) {
+               sBUG_ON(sg[idx].page == NULL);
+               addr = page_address(sg[idx].page);
+               sBUG_ON(addr == NULL);
+               conn->read_iov[i].iov_base =  addr + offset;
+               if (offset + size <= PAGE_SIZE) {
+                       TRACE_DBG("idx=%d, offset=%u, size=%d, addr=%p",
+                               idx, offset, size, addr);
+                       conn->read_iov[i].iov_len = size;
+                       conn->read_msg.msg_iovlen = ++i;
+                       break;
+               }
+               conn->read_iov[i].iov_len = PAGE_SIZE - offset;
+               TRACE_DBG("idx=%d, offset=%u, size=%d, iov_len=%d, addr=%p",
+                       idx, offset, size, conn->read_iov[i].iov_len, addr);
+               size -= conn->read_iov[i].iov_len;
+               offset = 0;
+               if (++i >= ISCSI_CONN_IOV_MAX) {
+                       PRINT_ERROR_PR("Initiator %s violated negotiated "
+                               "parameters by sending too much data (size "
+                               "left %d)", conn->session->initiator_name, size);
+                       mark_conn_closed(conn);
+                       res = -EINVAL;
+                       break;
+               }
+               idx++;
+       }
+       TRACE_DBG("msg_iov=%p, msg_iovlen=%d",
+               conn->read_msg.msg_iov, conn->read_msg.msg_iovlen);
+
+out:
+       return res;
+}
+
+static void send_r2t(struct iscsi_cmnd *req)
+{
+       struct iscsi_session *session = req->conn->session;
+       struct iscsi_cmnd *rsp;
+       struct iscsi_r2t_hdr *rsp_hdr;
+       u32 length, offset, burst;
+       LIST_HEAD(send);
+
+       /*
+        * There is no race with data_out_start() and __cmnd_abort(), since
+        * all functions called from single read thread
+        */
+       iscsi_extracheck_is_rd_thread(req->conn);
+
+       length = req->r2t_length;
+       burst = session->sess_param.max_burst_length;
+       offset = be32_to_cpu(cmnd_hdr(req)->data_length) - length;
+
+       do {
+               rsp = iscsi_cmnd_create_rsp_cmnd(req);
+               rsp->pdu.bhs.ttt = req->target_task_tag;
+               rsp_hdr = (struct iscsi_r2t_hdr *)&rsp->pdu.bhs;
+               rsp_hdr->opcode = ISCSI_OP_R2T;
+               rsp_hdr->flags = ISCSI_FLG_FINAL;
+               memcpy(rsp_hdr->lun, cmnd_hdr(req)->lun, 8);
+               rsp_hdr->itt = cmnd_hdr(req)->itt;
+               rsp_hdr->r2t_sn = cpu_to_be32(req->r2t_sn++);
+               rsp_hdr->buffer_offset = cpu_to_be32(offset);
+               if (length > burst) {
+                       rsp_hdr->data_length = cpu_to_be32(burst);
+                       length -= burst;
+                       offset += burst;
+               } else {
+                       rsp_hdr->data_length = cpu_to_be32(length);
+                       length = 0;
+               }
+
+               TRACE(TRACE_D_WRITE, "%x %u %u %u %u", cmnd_itt(req),
+                       be32_to_cpu(rsp_hdr->data_length),
+                       be32_to_cpu(rsp_hdr->buffer_offset),
+                       be32_to_cpu(rsp_hdr->r2t_sn), req->outstanding_r2t);
+
+               list_add_tail(&rsp->write_list_entry, &send);
+
+               if (++req->outstanding_r2t >= session->sess_param.max_outstanding_r2t)
+                       break;
+
+       } while (length);
+
+       iscsi_cmnds_init_write(&send, ISCSI_INIT_WRITE_WAKE);
+
+       req->data_waiting = 1;
+}
+
+static int iscsi_pre_exec(struct scst_cmd *scst_cmd)
+{
+       int res = SCST_PREPROCESS_STATUS_SUCCESS;
+       struct iscsi_cmnd *req = (struct iscsi_cmnd*)
+               scst_cmd_get_tgt_priv(scst_cmd);
+       struct iscsi_cmnd *c, *t;
+
+       TRACE_ENTRY();
+
+       EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd));
+
+       /* If data digest isn't used this list will be empty */
+       list_for_each_entry_safe(c, t, &req->rx_ddigest_cmd_list,
+                               rx_ddigest_cmd_list_entry) {
+               TRACE_DBG("Checking digest of RX ddigest cmd %p", c);
+               if (digest_rx_data(c) != 0) {
+                       scst_set_cmd_error(scst_cmd,
+                               SCST_LOAD_SENSE(iscsi_sense_crc_error));
+                       res = SCST_PREPROCESS_STATUS_ERROR_SENSE_SET;
+                       /*
+                        * The rest of rx_ddigest_cmd_list will be freed 
+                        * in req_cmnd_release()
+                        */
+                       goto out;
+               }
+               TRACE_DBG("Deleting RX digest cmd %p from digest list", c);
+               list_del(&c->rx_ddigest_cmd_list_entry);
+               cmnd_put(c);
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+
+static void scsi_cmnd_exec(struct iscsi_cmnd *cmnd)
+{
+       if (cmnd->r2t_length) {
+               if (!cmnd->is_unsolicited_data)
+                       send_r2t(cmnd);
+       } else {
+               /*
+                * There is no race with send_r2t() and __cmnd_abort(),
+                * since all functions called from single read thread
+                */
+               cmnd->data_waiting = 0;
+               iscsi_restart_cmnd(cmnd);
+       }
+}
+
+static int noop_out_start(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       u32 size, tmp;
+       int i, err = 0;
+
+       TRACE_DBG("%p", cmnd);
+
+       iscsi_extracheck_is_rd_thread(conn);
+
+       if (cmnd_ttt(cmnd) != cpu_to_be32(ISCSI_RESERVED_TAG)) {
+               /*
+                * We don't request a NOP-Out by sending a NOP-In.
+                * See 10.18.2 in the draft 20.
+                */
+               PRINT_ERROR_PR("initiator bug %x", cmnd_itt(cmnd));
+               err = -ISCSI_REASON_PROTOCOL_ERROR;
+               goto out;
+       }
+
+       if (cmnd_itt(cmnd) == cpu_to_be32(ISCSI_RESERVED_TAG)) {
+               if (!(cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE))
+                       PRINT_ERROR_PR("%s","initiator bug!");
+               spin_lock(&conn->session->sn_lock);
+               __update_stat_sn(cmnd);
+               err = check_cmd_sn(cmnd);
+               spin_unlock(&conn->session->sn_lock);
+               if (err)
+                       goto out;
+       } else if ((err = cmnd_insert_hash(cmnd)) < 0) {
+               PRINT_ERROR_PR("Can't insert in hash: ignore this request %x",
+                       cmnd_itt(cmnd));
+               goto out;
+       }
+
+       if ((size = cmnd->pdu.datasize)) {
+               size = (size + 3) & -4;
+               conn->read_msg.msg_iov = conn->read_iov;
+               if (cmnd->pdu.bhs.itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
+                       struct scatterlist *sg;
+
+                       /* ToDo: __GFP_NOFAIL ?? */
+                       cmnd->sg = sg = scst_alloc(size,
+                               GFP_KERNEL|__GFP_NOFAIL, 0, &cmnd->sg_cnt);
+                       if (sg == NULL) {
+                               /* ToDo(); */
+                       }
+                       if (cmnd->sg_cnt > ISCSI_CONN_IOV_MAX) {
+                               /* ToDo(); */
+                       }
+                       cmnd->own_sg = 1;
+                       cmnd->bufflen = size;
+
+                       for (i = 0; i < cmnd->sg_cnt; i++) {
+                               conn->read_iov[i].iov_base =
+                                       page_address(sg[i].page);
+                               tmp = min_t(u32, size, PAGE_SIZE);
+                               conn->read_iov[i].iov_len = tmp;
+                               conn->read_size += tmp;
+                               size -= tmp;
+                       }
+               } else {
+                       /*
+                        * There are no problems with the safety from concurrent
+                        * accesses to dummy_data, since for ISCSI_RESERVED_TAG
+                        * the data only read and then discarded.
+                        */
+                       for (i = 0; i < ISCSI_CONN_IOV_MAX; i++) {
+                               conn->read_iov[i].iov_base = dummy_data;
+                               tmp = min_t(u32, size, sizeof(dummy_data));
+                               conn->read_iov[i].iov_len = tmp;
+                               conn->read_size += tmp;
+                               size -= tmp;
+                       }
+               }
+               sBUG_ON(size == 0);
+               conn->read_msg.msg_iovlen = i;
+               TRACE_DBG("msg_iov=%p, msg_iovlen=%d", conn->read_msg.msg_iov,
+                       conn->read_msg.msg_iovlen);
+       }
+out:
+       return err;
+}
+
+static inline u32 get_next_ttt(struct iscsi_conn *conn)
+{
+       u32 ttt;
+       struct iscsi_session *session = conn->session;
+
+       iscsi_extracheck_is_rd_thread(conn);
+
+       if (session->next_ttt == ISCSI_RESERVED_TAG)
+               session->next_ttt++;
+       ttt = session->next_ttt++;
+
+       return cpu_to_be32(ttt);
+}
+
+static int scsi_cmnd_start(struct iscsi_cmnd *req)
+{
+       struct iscsi_conn *conn = req->conn;
+       struct iscsi_session *session = conn->session;
+       struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
+       struct scst_cmd *scst_cmd;
+       scst_data_direction dir;
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       TRACE_DBG("scsi command: %02x", req_hdr->scb[0]);
+
+       scst_cmd = scst_rx_cmd(session->scst_sess,
+               (uint8_t*)req_hdr->lun, sizeof(req_hdr->lun),
+               req_hdr->scb, sizeof(req_hdr->scb), SCST_NON_ATOMIC);
+       if (scst_cmd == NULL) {
+               create_status_rsp(req, SAM_STAT_BUSY, NULL, 0);
+               cmnd_prepare_skip_pdu_set_resid(req);
+               goto out;
+       }
+
+       req->scst_cmd = scst_cmd;
+       scst_cmd_set_tag(scst_cmd, req_hdr->itt);
+       scst_cmd_set_tgt_priv(scst_cmd, req);
+#ifndef NET_PAGE_CALLBACKS_DEFINED
+       scst_cmd_set_data_buf_tgt_alloc(scst_cmd);
+#endif
+
+       if (req_hdr->flags & ISCSI_CMD_READ)
+               dir = SCST_DATA_READ;
+       else if (req_hdr->flags & ISCSI_CMD_WRITE)
+               dir = SCST_DATA_WRITE;
+       else
+               dir = SCST_DATA_NONE;
+       scst_cmd_set_expected(scst_cmd, dir, be32_to_cpu(req_hdr->data_length));
+
+       switch(req_hdr->flags & ISCSI_CMD_ATTR_MASK) {
+       case ISCSI_CMD_SIMPLE:
+               scst_cmd->queue_type = SCST_CMD_QUEUE_SIMPLE;
+               break;
+       case ISCSI_CMD_HEAD_OF_QUEUE:
+               scst_cmd->queue_type = SCST_CMD_QUEUE_HEAD_OF_QUEUE;
+               break;
+       case ISCSI_CMD_ORDERED:
+               scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
+               break;
+       case ISCSI_CMD_ACA:
+               scst_cmd->queue_type = SCST_CMD_QUEUE_ACA;
+               break;
+       case ISCSI_CMD_UNTAGGED:
+               scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED;
+               break;
+       default:
+               PRINT_ERROR_PR("Unknown task code %x, use ORDERED instead",
+                       req_hdr->flags & ISCSI_CMD_ATTR_MASK);
+               scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
+               break;
+       }
+
+       TRACE_DBG("START Command (tag %d, queue_type %d)",
+               req_hdr->itt, scst_cmd->queue_type);
+       req->scst_state = ISCSI_CMD_STATE_RX_CMD;
+       scst_cmd_init_stage1_done(scst_cmd, SCST_CONTEXT_DIRECT, 0);
+
+       wait_event(req->scst_waitQ, (req->scst_state != ISCSI_CMD_STATE_RX_CMD));
+
+       if (unlikely(req->scst_state != ISCSI_CMD_STATE_AFTER_PREPROC)) {
+               TRACE_DBG("req %p is in %x state", req, req->scst_state);
+               if (req->scst_state == ISCSI_CMD_STATE_PROCESSED) {
+                       /* Response is already prepared */
+                       cmnd_prepare_skip_pdu_set_resid(req);
+                       goto out;
+               }
+               if (unlikely(req->tmfabort)) {
+                       TRACE_MGMT_DBG("req %p (scst_cmd %p) aborted", req,
+                               req->scst_cmd);
+                       cmnd_prepare_skip_pdu(req);
+                       goto out;
+               }
+               sBUG();
+       }
+
+       dir = scst_cmd_get_data_direction(scst_cmd);
+       if (dir != SCST_DATA_WRITE) {
+               if (!(req_hdr->flags & ISCSI_CMD_FINAL) || req->pdu.datasize) {
+                       PRINT_ERROR_PR("Unexpected unsolicited data (ITT %x "
+                               "CDB %x", cmnd_itt(req), req_hdr->scb[0]);
+                       create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc);
+                       cmnd_prepare_skip_pdu_set_resid(req);
+                       goto out;
+               }
+       }
+
+       if (dir == SCST_DATA_WRITE) {
+               req->is_unsolicited_data = !(req_hdr->flags & ISCSI_CMD_FINAL);
+               req->r2t_length = be32_to_cpu(req_hdr->data_length) - req->pdu.datasize;
+       }
+       req->target_task_tag = get_next_ttt(conn);
+       req->sg = scst_cmd_get_sg(scst_cmd);
+       req->bufflen = scst_cmd_get_bufflen(scst_cmd);
+       if (req->r2t_length > req->bufflen) {
+               PRINT_ERROR_PR("req->r2t_length %d > req->bufflen %d",
+                       req->r2t_length, req->bufflen);
+               req->r2t_length = req->bufflen;
+       }
+
+       TRACE_DBG("req=%p, dir=%d, is_unsolicited_data=%d, "
+               "r2t_length=%d, bufflen=%d", req, dir,
+               req->is_unsolicited_data, req->r2t_length, req->bufflen);
+
+       if (!session->sess_param.immediate_data &&
+           req->pdu.datasize) {
+               PRINT_ERROR_PR("Initiator %s violated negotiated paremeters: "
+                       "forbidden immediate data sent (ITT %x, op  %x)",
+                       session->initiator_name, cmnd_itt(req), req_hdr->scb[0]);
+               res = -EINVAL;
+               goto out;
+       }
+
+       if (session->sess_param.initial_r2t &&
+           !(req_hdr->flags & ISCSI_CMD_FINAL)) {
+               PRINT_ERROR_PR("Initiator %s violated negotiated paremeters: "
+                       "initial R2T is required (ITT %x, op  %x)",
+                       session->initiator_name, cmnd_itt(req), req_hdr->scb[0]);
+               res = -EINVAL;
+               goto out;
+       }
+
+       if (req->pdu.datasize) {
+               if (dir != SCST_DATA_WRITE) {
+                       PRINT_ERROR_PR("pdu.datasize(%d) >0, but dir(%x) isn't WRITE",
+                               req->pdu.datasize, dir);
+                       create_sense_rsp(req, ABORTED_COMMAND, 0xc, 0xc);
+                       cmnd_prepare_skip_pdu_set_resid(req);
+               } else
+                       res = cmnd_prepare_recv_pdu(conn, req, 0, req->pdu.datasize);
+       }
+out:
+       /* Aborted commands will be freed in cmnd_rx_end() */
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static int data_out_start(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_data_out_hdr *req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
+       struct iscsi_cmnd *req = NULL;
+       u32 offset = be32_to_cpu(req_hdr->buffer_offset);
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       /*
+        * There is no race with send_r2t() and __cmnd_abort(), since
+        * all functions called from single read thread
+        */
+       iscsi_extracheck_is_rd_thread(cmnd->conn);
+
+       update_stat_sn(cmnd);
+
+       cmnd->cmd_req = req = cmnd_find_hash(conn->session, req_hdr->itt,
+                                       req_hdr->ttt);
+       if (!req) {
+               PRINT_ERROR_PR("unable to find scsi task %x %x",
+                       cmnd_itt(cmnd), cmnd_ttt(cmnd));
+               goto skip_pdu;
+       }
+
+       if (req->r2t_length < cmnd->pdu.datasize) {
+               PRINT_ERROR_PR("Invalid data len %x %u %u", cmnd_itt(req),
+                       cmnd->pdu.datasize, req->r2t_length);
+               mark_conn_closed(conn);
+               res = -EINVAL;
+               goto out;
+       }
+
+       if (req->r2t_length + offset != cmnd_write_size(req)) {
+               PRINT_ERROR_PR("Wrong cmd lengths (%x %u %u %u)",
+                       cmnd_itt(req), req->r2t_length,
+                       offset, cmnd_write_size(req));
+               mark_conn_closed(conn);
+               res = -EINVAL;
+               goto out;
+       }
+
+       req->r2t_length -= cmnd->pdu.datasize;
+
+       /* Check unsolicited burst data */
+       if ((req_hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) &&
+           (req->pdu.bhs.flags & ISCSI_FLG_FINAL)) {
+               PRINT_ERROR_PR("unexpected data from %x %x",
+                       cmnd_itt(cmnd), cmnd_ttt(cmnd));
+               mark_conn_closed(conn);
+               res = -EINVAL;
+               goto out;
+       }
+
+       TRACE(TRACE_D_WRITE, "%u %p %p %u %u", req_hdr->ttt, cmnd, req,
+               offset, cmnd->pdu.datasize);
+
+       res = cmnd_prepare_recv_pdu(conn, req, offset, cmnd->pdu.datasize);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+skip_pdu:
+       cmnd->pdu.bhs.opcode = ISCSI_OP_DATA_REJECT;
+       cmnd_prepare_skip_pdu(cmnd);
+       goto out;
+}
+
+static void data_out_end(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_data_out_hdr *req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
+       struct iscsi_cmnd *req;
+
+       sBUG_ON(cmnd == NULL);
+       req = cmnd->cmd_req;
+       sBUG_ON(req == NULL);
+
+       TRACE_DBG("cmnd %p, req %p", cmnd, req);
+
+       iscsi_extracheck_is_rd_thread(cmnd->conn);
+
+       if (!(cmnd->conn->ddigest_type & DIGEST_NONE)) {
+               TRACE_DBG("Adding RX ddigest cmd %p to digest list "
+                       "of req %p", cmnd, req);
+               list_add_tail(&cmnd->rx_ddigest_cmd_list_entry,
+                       &req->rx_ddigest_cmd_list);
+               cmnd_get(cmnd);
+       }
+
+       if (req_hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
+               TRACE_DBG("ISCSI_RESERVED_TAG, FINAL %x",
+                       req_hdr->flags & ISCSI_FLG_FINAL);
+               if (req_hdr->flags & ISCSI_FLG_FINAL) {
+                       req->is_unsolicited_data = 0;
+                       if (!req->pending)
+                               scsi_cmnd_exec(req);
+               }
+       } else {
+               TRACE_DBG("FINAL %x, outstanding_r2t %d, "
+                       "r2t_length %d", req_hdr->flags & ISCSI_FLG_FINAL,
+                       req->outstanding_r2t, req->r2t_length);
+               /* ToDo : proper error handling */
+               if (!(req_hdr->flags & ISCSI_FLG_FINAL) && (req->r2t_length == 0))
+                       PRINT_ERROR_PR("initiator error %x", cmnd_itt(req));
+
+               if (!(req_hdr->flags & ISCSI_FLG_FINAL))
+                       goto out;
+
+               req->outstanding_r2t--;
+
+               scsi_cmnd_exec(req);
+       }
+
+out:
+       cmnd_put(cmnd);
+       return;
+}
+
+/*
+ * Called under cmd_list_lock, but may drop it inside.
+ * Returns >0 if cmd_list_lock was dropped inside, 0 otherwise.
+ */
+static inline int __cmnd_abort(struct iscsi_cmnd *cmnd)
+{
+       int res = 0;
+
+       TRACE(TRACE_MGMT, "Aborting cmd %p, scst_cmd %p (scst state %x, "
+               "itt %x, op %x, r2t_len %x, CDB op %x, size to write %u, "
+               "is_unsolicited_data %u, outstanding_r2t %u)",
+               cmnd, cmnd->scst_cmd, cmnd->scst_state, cmnd_itt(cmnd),
+               cmnd_opcode(cmnd), cmnd->r2t_length, cmnd_scsicode(cmnd),
+               cmnd_write_size(cmnd), cmnd->is_unsolicited_data,
+               cmnd->outstanding_r2t);
+
+       iscsi_extracheck_is_rd_thread(cmnd->conn);
+
+       cmnd->tmfabort = 1;
+
+       if (cmnd->data_waiting) {
+               struct iscsi_conn *conn = cmnd->conn;
+               res = 1;
+               spin_unlock_bh(&conn->cmd_list_lock);
+               TRACE_MGMT_DBG("Releasing data waiting cmd %p", cmnd);
+               req_cmnd_release_force(cmnd, ISCSI_FORCE_RELEASE_WRITE);
+               spin_lock_bh(&conn->cmd_list_lock);
+       }
+
+       return res;
+}
+
+static int cmnd_abort(struct iscsi_session *session, u32 itt)
+{
+       struct iscsi_cmnd *cmnd;
+       int err;
+
+       if ((cmnd = cmnd_find_hash_get(session, itt, ISCSI_RESERVED_TAG))) {
+               struct iscsi_conn *conn = cmnd->conn;
+               spin_lock_bh(&conn->cmd_list_lock);
+               __cmnd_abort(cmnd);
+               spin_unlock_bh(&conn->cmd_list_lock);
+               cmnd_put(cmnd);
+               err = 0;
+       } else
+               err = ISCSI_RESPONSE_UNKNOWN_TASK;
+
+       return err;
+}
+
+static int target_abort(struct iscsi_cmnd *req, u16 *lun, int all)
+{
+       struct iscsi_target *target = req->conn->session->target;
+       struct iscsi_session *session;
+       struct iscsi_conn *conn;
+       struct iscsi_cmnd *cmnd;
+
+       mutex_lock(&target->target_mutex);
+
+       list_for_each_entry(session, &target->session_list, session_list_entry) {
+               list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
+                       spin_lock_bh(&conn->cmd_list_lock);
+again:
+                       list_for_each_entry(cmnd, &conn->cmd_list, cmd_list_entry) {
+                               int again = 0;
+                               if (cmnd == req)
+                                       continue;
+                               if (all)
+                                       again = __cmnd_abort(cmnd);
+                               else if (memcmp(lun, &cmnd_hdr(cmnd)->lun,
+                                               sizeof(cmnd_hdr(cmnd)->lun)) == 0)
+                                       again = __cmnd_abort(cmnd);
+                               if (again)
+                                       goto again;
+                       }
+                       spin_unlock_bh(&conn->cmd_list_lock);
+               }
+       }
+
+       mutex_unlock(&target->target_mutex);
+       return 0;
+}
+
+static void task_set_abort(struct iscsi_cmnd *req)
+{
+       struct iscsi_session *session = req->conn->session;
+       struct iscsi_target *target = session->target;
+       struct iscsi_conn *conn;
+       struct iscsi_cmnd *cmnd;
+
+       mutex_lock(&target->target_mutex);
+
+       list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
+               spin_lock_bh(&conn->cmd_list_lock);
+again:
+               list_for_each_entry(cmnd, &conn->cmd_list, cmd_list_entry) {
+                       if (cmnd != req)
+                               if (__cmnd_abort(cmnd))
+                                       goto again;
+               }
+               spin_unlock_bh(&conn->cmd_list_lock);
+       }
+
+       mutex_unlock(&target->target_mutex);
+       return;
+}
+
+void conn_abort(struct iscsi_conn *conn)
+{
+       struct iscsi_cmnd *cmnd;
+
+       TRACE_MGMT_DBG("Aborting conn %p", conn);
+
+       spin_lock_bh(&conn->cmd_list_lock);
+again:
+       list_for_each_entry(cmnd, &conn->cmd_list, cmd_list_entry) {
+               if (__cmnd_abort(cmnd))
+                       goto again;
+       }
+       spin_unlock_bh(&conn->cmd_list_lock);
+}
+
+static void execute_task_management(struct iscsi_cmnd *req)
+{
+       struct iscsi_conn *conn = req->conn;
+       struct iscsi_task_mgt_hdr *req_hdr = (struct iscsi_task_mgt_hdr *)&req->pdu.bhs;
+       int err = 0, function = req_hdr->function & ISCSI_FUNCTION_MASK;
+
+       TRACE(TRACE_MGMT, "TM cmd: req %p, itt %x, fn %d, rtt %x", req, cmnd_itt(req),
+               function, req_hdr->rtt);
+
+       switch (function) {
+       case ISCSI_FUNCTION_ABORT_TASK:
+               err = cmnd_abort(conn->session, req_hdr->rtt);
+               if (err == 0) {
+                       err = scst_rx_mgmt_fn_tag(conn->session->scst_sess,
+                               SCST_ABORT_TASK, req_hdr->rtt, SCST_NON_ATOMIC,
+                               req);
+               }
+               break;
+       case ISCSI_FUNCTION_ABORT_TASK_SET:
+               task_set_abort(req);
+               err = scst_rx_mgmt_fn_lun(conn->session->scst_sess,
+                       SCST_ABORT_TASK_SET, (uint8_t *)req_hdr->lun,
+                       sizeof(req_hdr->lun), SCST_NON_ATOMIC, req);
+               break;
+       case ISCSI_FUNCTION_CLEAR_TASK_SET:
+               task_set_abort(req);
+               err = scst_rx_mgmt_fn_lun(conn->session->scst_sess,
+                       SCST_CLEAR_TASK_SET, (uint8_t *)req_hdr->lun,
+                       sizeof(req_hdr->lun), SCST_NON_ATOMIC, req);
+               break;
+       case ISCSI_FUNCTION_CLEAR_ACA:
+               err = scst_rx_mgmt_fn_lun(conn->session->scst_sess,
+                       SCST_CLEAR_ACA, (uint8_t *)req_hdr->lun,
+                       sizeof(req_hdr->lun), SCST_NON_ATOMIC, req);
+               break;
+       case ISCSI_FUNCTION_TARGET_COLD_RESET:
+       case ISCSI_FUNCTION_TARGET_WARM_RESET:
+               target_abort(req, 0, 1);
+               err = scst_rx_mgmt_fn_lun(conn->session->scst_sess,
+                       SCST_TARGET_RESET, (uint8_t *)req_hdr->lun,
+                       sizeof(req_hdr->lun), SCST_NON_ATOMIC, req);
+               break;
+       case ISCSI_FUNCTION_LOGICAL_UNIT_RESET:
+               target_abort(req, req_hdr->lun, 0);
+               err = scst_rx_mgmt_fn_lun(conn->session->scst_sess,
+                       SCST_LUN_RESET, (uint8_t *)req_hdr->lun,
+                       sizeof(req_hdr->lun), SCST_NON_ATOMIC, req);
+               break;
+       case ISCSI_FUNCTION_TASK_REASSIGN:
+               iscsi_send_task_mgmt_resp(req, 
+                       ISCSI_RESPONSE_FUNCTION_UNSUPPORTED);
+               break;
+       default:
+               iscsi_send_task_mgmt_resp(req,
+                       ISCSI_RESPONSE_FUNCTION_REJECTED);
+               break;
+       }
+
+       if (err != 0) {
+               iscsi_send_task_mgmt_resp(req,
+                       ISCSI_RESPONSE_FUNCTION_REJECTED);
+       }
+}
+
+static void noop_out_exec(struct iscsi_cmnd *req)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_nop_in_hdr *rsp_hdr;
+
+       TRACE_DBG("%p", req);
+
+       if (cmnd_itt(req) != cpu_to_be32(ISCSI_RESERVED_TAG)) {
+               rsp = iscsi_cmnd_create_rsp_cmnd(req);
+
+               rsp_hdr = (struct iscsi_nop_in_hdr *)&rsp->pdu.bhs;
+               rsp_hdr->opcode = ISCSI_OP_NOOP_IN;
+               rsp_hdr->flags = ISCSI_FLG_FINAL;
+               rsp_hdr->itt = req->pdu.bhs.itt;
+               rsp_hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
+
+               if (req->pdu.datasize)
+                       sBUG_ON(req->sg == NULL);
+               else
+                       sBUG_ON(req->sg != NULL);
+
+               if (req->sg) {
+                       rsp->sg = req->sg;
+                       rsp->bufflen = req->bufflen;
+               }
+
+               sBUG_ON(get_pgcnt(req->pdu.datasize, 0) > ISCSI_CONN_IOV_MAX);
+               rsp->pdu.datasize = req->pdu.datasize;
+               iscsi_cmnd_init_write(rsp,
+                       ISCSI_INIT_WRITE_REMOVE_HASH | ISCSI_INIT_WRITE_WAKE);
+               req_cmnd_release(req);
+       } else
+               cmnd_put(req);
+}
+
+static void logout_exec(struct iscsi_cmnd *req)
+{
+       struct iscsi_logout_req_hdr *req_hdr;
+       struct iscsi_cmnd *rsp;
+       struct iscsi_logout_rsp_hdr *rsp_hdr;
+
+       PRINT_INFO_PR("Logout received from initiator %s",
+               req->conn->session->initiator_name);
+       TRACE_DBG("%p", req);
+
+       req_hdr = (struct iscsi_logout_req_hdr *)&req->pdu.bhs;
+       rsp = iscsi_cmnd_create_rsp_cmnd(req);
+       rsp_hdr = (struct iscsi_logout_rsp_hdr *)&rsp->pdu.bhs;
+       rsp_hdr->opcode = ISCSI_OP_LOGOUT_RSP;
+       rsp_hdr->flags = ISCSI_FLG_FINAL;
+       rsp_hdr->itt = req_hdr->itt;
+       rsp->should_close_conn = 1;
+       iscsi_cmnd_init_write(rsp,
+               ISCSI_INIT_WRITE_REMOVE_HASH | ISCSI_INIT_WRITE_WAKE);
+       req_cmnd_release(req);
+}
+
+static void iscsi_cmnd_exec(struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("%p,%x,%u", cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn);
+
+       if (unlikely(cmnd->tmfabort)) {
+               TRACE_MGMT_DBG("cmnd %p (scst_cmd %p) aborted", cmnd,
+                       cmnd->scst_cmd);
+               req_cmnd_release_force(cmnd, ISCSI_FORCE_RELEASE_WRITE);
+               goto out;
+       }
+
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_NOOP_OUT:
+               noop_out_exec(cmnd);
+               break;
+       case ISCSI_OP_SCSI_CMD:
+               scsi_cmnd_exec(cmnd);
+               break;
+       case ISCSI_OP_SCSI_TASK_MGT_MSG:
+               execute_task_management(cmnd);
+               break;
+       case ISCSI_OP_LOGOUT_CMD:
+               logout_exec(cmnd);
+               break;
+       case ISCSI_OP_SCSI_REJECT:
+               TRACE_MGMT_DBG("REJECT cmnd %p (scst_cmd %p)", cmnd,
+                       cmnd->scst_cmd);
+               iscsi_cmnd_init_write(get_rsp_cmnd(cmnd),
+                       ISCSI_INIT_WRITE_REMOVE_HASH | ISCSI_INIT_WRITE_WAKE);
+               req_cmnd_release(cmnd);
+               break;
+       default:
+               PRINT_ERROR_PR("unexpected cmnd op %x", cmnd_opcode(cmnd));
+               req_cmnd_release(cmnd);
+               break;
+       }
+out:
+       return;
+}
+
+static void __cmnd_send_pdu(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd,
+       u32 offset, u32 size)
+{
+       TRACE_DBG("%p %u,%u,%u", cmnd, offset, size, cmnd->bufflen);
+
+       iscsi_extracheck_is_wr_thread(conn);
+
+       sBUG_ON(offset > cmnd->bufflen);
+       sBUG_ON(offset + size > cmnd->bufflen);
+
+       conn->write_offset = offset;
+       conn->write_size += size;
+}
+
+static void cmnd_send_pdu(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
+{
+       u32 size;
+
+       if (!cmnd->pdu.datasize)
+               return;
+
+       size = (cmnd->pdu.datasize + 3) & -4;
+       sBUG_ON(cmnd->sg == NULL);
+       sBUG_ON(cmnd->bufflen != size);
+       __cmnd_send_pdu(conn, cmnd, 0, size);
+}
+
+static void set_cork(struct socket *sock, int on)
+{
+       int opt = on;
+       mm_segment_t oldfs;
+
+       oldfs = get_fs();
+       set_fs(get_ds());
+       sock->ops->setsockopt(sock, SOL_TCP, TCP_CORK, (void *)&opt, sizeof(opt));
+       set_fs(oldfs);
+}
+
+void cmnd_tx_start(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+
+       TRACE_DBG("%p:%p:%x", conn, cmnd, cmnd_opcode(cmnd));
+       iscsi_cmnd_set_length(&cmnd->pdu);
+
+       iscsi_extracheck_is_wr_thread(conn);
+
+       set_cork(conn->sock, 1);
+
+       conn->write_iop = conn->write_iov;
+       conn->write_iop->iov_base = &cmnd->pdu.bhs;
+       conn->write_iop->iov_len = sizeof(cmnd->pdu.bhs);
+       conn->write_iop_used = 1;
+       conn->write_size = sizeof(cmnd->pdu.bhs);
+
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_NOOP_IN:
+               cmnd_set_sn(cmnd, 1);
+               cmnd_send_pdu(conn, cmnd);
+               break;
+       case ISCSI_OP_SCSI_RSP:
+               cmnd_set_sn(cmnd, 1);
+               cmnd_send_pdu(conn, cmnd);
+               break;
+       case ISCSI_OP_SCSI_TASK_MGT_RSP:
+               cmnd_set_sn(cmnd, 1);
+               break;
+       case ISCSI_OP_TEXT_RSP:
+               cmnd_set_sn(cmnd, 1);
+               break;
+       case ISCSI_OP_SCSI_DATA_IN:
+       {
+               struct iscsi_data_in_hdr *rsp = (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs;
+               u32 offset = cpu_to_be32(rsp->buffer_offset);
+
+               cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLG_FINAL) ? 1 : 0);
+               __cmnd_send_pdu(conn, cmnd, offset, cmnd->pdu.datasize);
+               break;
+       }
+       case ISCSI_OP_LOGOUT_RSP:
+               cmnd_set_sn(cmnd, 1);
+               break;
+       case ISCSI_OP_R2T:
+               cmnd->pdu.bhs.sn = cmnd_set_sn(cmnd, 0);
+               break;
+       case ISCSI_OP_ASYNC_MSG:
+               cmnd_set_sn(cmnd, 1);
+               break;
+       case ISCSI_OP_REJECT:
+               cmnd_set_sn(cmnd, 1);
+               cmnd_send_pdu(conn, cmnd);
+               break;
+       default:
+               PRINT_ERROR_PR("unexpected cmnd op %x", cmnd_opcode(cmnd));
+               break;
+       }
+
+       // move this?
+       conn->write_size = (conn->write_size + 3) & -4;
+       iscsi_dump_pdu(&cmnd->pdu);
+}
+
+void cmnd_tx_end(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+
+       TRACE_DBG("%p:%x (should_close_conn %d)", cmnd, cmnd_opcode(cmnd),
+               cmnd->should_close_conn);
+
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_NOOP_IN:
+       case ISCSI_OP_SCSI_RSP:
+       case ISCSI_OP_SCSI_TASK_MGT_RSP:
+       case ISCSI_OP_TEXT_RSP:
+       case ISCSI_OP_R2T:
+       case ISCSI_OP_ASYNC_MSG:
+       case ISCSI_OP_REJECT:
+       case ISCSI_OP_SCSI_DATA_IN:
+       case ISCSI_OP_LOGOUT_RSP:
+               break;
+       default:
+               PRINT_ERROR_PR("unexpected cmnd op %x", cmnd_opcode(cmnd));
+               sBUG();
+               break;
+       }
+
+       if (cmnd->should_close_conn) {
+               PRINT_INFO_PR("Closing connection at initiator %s request",
+                       conn->session->initiator_name);
+               mark_conn_closed(conn);
+       }
+
+       set_cork(cmnd->conn->sock, 0);
+}
+
+/*
+ * Push the command for execution. This functions reorders the commands.
+ * Called from the read thread.
+ */
+static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_session *session = cmnd->conn->session;
+       struct list_head *entry;
+       u32 cmd_sn;
+
+       TRACE_DBG("%p:%x %u,%u",
+               cmnd, cmnd_opcode(cmnd), cmnd->pdu.bhs.sn, session->exp_cmd_sn);
+
+       iscsi_extracheck_is_rd_thread(cmnd->conn);
+
+       if (cmnd->pdu.bhs.opcode & ISCSI_OP_IMMEDIATE) {
+               iscsi_cmnd_exec(cmnd);
+               return;
+       }
+
+       spin_lock(&session->sn_lock);
+
+       cmd_sn = cmnd->pdu.bhs.sn;
+       if (cmd_sn == session->exp_cmd_sn) {
+               while (1) {
+                       session->exp_cmd_sn = ++cmd_sn;
+
+                       spin_unlock(&session->sn_lock);
+
+                       iscsi_cmnd_exec(cmnd);
+
+                       if (list_empty(&session->pending_list))
+                               break;
+                       cmnd = list_entry(session->pending_list.next, struct iscsi_cmnd,
+                                               pending_list_entry);
+                       if (cmnd->pdu.bhs.sn != cmd_sn)
+                               break;
+                       list_del(&cmnd->pending_list_entry);
+                       cmnd->pending = 0;
+
+                       spin_lock(&session->sn_lock);
+               }
+       } else {
+               cmnd->pending = 1;
+               if (before(cmd_sn, session->exp_cmd_sn)) { /* close the conn */
+                       PRINT_ERROR_PR("unexpected cmd_sn (%u,%u)", cmd_sn,
+                               session->exp_cmd_sn);
+               }
+
+               if (after(cmd_sn, session->exp_cmd_sn + session->max_queued_cmnds)) {
+                       PRINT_ERROR_PR("too large cmd_sn (%u,%u)", cmd_sn,
+                               session->exp_cmd_sn);
+               }
+
+               spin_unlock(&session->sn_lock);
+
+               list_for_each(entry, &session->pending_list) {
+                       struct iscsi_cmnd *tmp = list_entry(entry, struct iscsi_cmnd,
+                                                       pending_list_entry);
+                       if (before(cmd_sn, tmp->pdu.bhs.sn))
+                               break;
+               }
+
+               list_add_tail(&cmnd->pending_list_entry, entry);
+       }
+
+       return;
+}
+
+static int check_segment_length(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       struct iscsi_session *session = conn->session;
+
+       if (cmnd->pdu.datasize > session->sess_param.max_recv_data_length) {
+               PRINT_ERROR_PR("Initiator %s violated negotiated parameters: "
+                       "data too long (ITT %x, datasize %u, "
+                       "max_recv_data_length %u", session->initiator_name,
+                       cmnd_itt(cmnd), cmnd->pdu.datasize,
+                       session->sess_param.max_recv_data_length);
+               mark_conn_closed(conn);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int cmnd_rx_start(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       int res, rc;
+
+       iscsi_dump_pdu(&cmnd->pdu);
+
+       res = check_segment_length(cmnd);
+       if (res != 0)
+               goto out;
+
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_NOOP_OUT:
+               rc = noop_out_start(cmnd);
+               break;
+       case ISCSI_OP_SCSI_CMD:
+               rc = cmnd_insert_hash(cmnd);
+               if (likely(rc == 0)) {
+                       res = scsi_cmnd_start(cmnd);
+                       if (unlikely(res != 0))
+                               goto out;
+               }
+               break;
+       case ISCSI_OP_SCSI_TASK_MGT_MSG:
+               rc = cmnd_insert_hash(cmnd);
+               break;
+       case ISCSI_OP_SCSI_DATA_OUT:
+               res = data_out_start(conn, cmnd);
+               rc = 0; /* to avoid compiler warning */
+               if (unlikely(res != 0))
+                       goto out;
+               break;
+       case ISCSI_OP_LOGOUT_CMD:
+               rc = cmnd_insert_hash(cmnd);
+               break;
+       case ISCSI_OP_TEXT_CMD:
+       case ISCSI_OP_SNACK_CMD:
+               rc = -ISCSI_REASON_UNSUPPORTED_COMMAND;
+               break;
+       default:
+               rc = -ISCSI_REASON_UNSUPPORTED_COMMAND;
+               break;
+       }
+
+       if (unlikely(rc < 0)) {
+               struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd);
+               PRINT_ERROR_PR("Error %d (iSCSI opcode %x, ITT %x, op %x)", rc,
+                       cmnd_opcode(cmnd), cmnd_itt(cmnd),
+                       (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_CMD ?
+                               hdr->scb[0] : -1));
+               iscsi_cmnd_reject(cmnd, -rc);
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+void cmnd_rx_end(struct iscsi_cmnd *cmnd)
+{
+       if (unlikely(cmnd->tmfabort)) {
+               TRACE_MGMT_DBG("cmnd %p (scst_cmd %p) aborted", cmnd,
+                       cmnd->scst_cmd);
+               req_cmnd_release_force(cmnd, ISCSI_FORCE_RELEASE_WRITE);
+               return;
+       }
+
+       TRACE_DBG("%p:%x", cmnd, cmnd_opcode(cmnd));
+       switch (cmnd_opcode(cmnd)) {
+       case ISCSI_OP_SCSI_REJECT:
+       case ISCSI_OP_NOOP_OUT:
+       case ISCSI_OP_SCSI_CMD:
+       case ISCSI_OP_SCSI_TASK_MGT_MSG:
+       case ISCSI_OP_LOGOUT_CMD:
+               iscsi_session_push_cmnd(cmnd);
+               break;
+       case ISCSI_OP_SCSI_DATA_OUT:
+               data_out_end(cmnd);
+               break;
+       case ISCSI_OP_PDU_REJECT:
+               iscsi_cmnd_init_write(get_rsp_cmnd(cmnd),
+                       ISCSI_INIT_WRITE_REMOVE_HASH | ISCSI_INIT_WRITE_WAKE);
+               req_cmnd_release(cmnd);
+               break;
+       case ISCSI_OP_DATA_REJECT:
+               req_cmnd_release(cmnd);
+               break;
+       default:
+               PRINT_ERROR_PR("unexpected cmnd op %x", cmnd_opcode(cmnd));
+               req_cmnd_release(cmnd);
+               break;
+       }
+}
+
+#ifndef NET_PAGE_CALLBACKS_DEFINED
+static int iscsi_alloc_data_buf(struct scst_cmd *cmd)
+{
+       if (scst_cmd_get_data_direction(cmd) == SCST_DATA_READ) {
+               /*
+                * sock->ops->sendpage() is async zero copy operation,
+                * so we must be sure not to free and reuse
+                * the command's buffer before the sending was completed
+                * by the network layers. It is possible only if we
+                * don't use SGV cache.
+                */
+               scst_cmd_set_no_sgv(cmd);
+       }
+       return 1;
+}
+#endif
+
+static inline void iscsi_set_state_wake_up(struct iscsi_cmnd *req,
+       int new_state)
+{
+       /*
+        * We use wait_event() to wait for the state change, but it checks its
+        * condition without any protection, so without cmnd_get() it is
+        * possible that req will die "immediately" after the state assignment
+        * and wake_up() will operate on dead data.
+        */
+       cmnd_get_ordered(req);
+       req->scst_state = new_state;
+       wake_up(&req->scst_waitQ);
+       cmnd_put(req);
+       return;
+}
+
+static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
+{
+       struct iscsi_cmnd *req = (struct iscsi_cmnd*)
+                               scst_cmd_get_tgt_priv(scst_cmd);
+
+       TRACE_DBG("req %p", req);
+
+       EXTRACHECKS_BUG_ON(req->scst_state != ISCSI_CMD_STATE_RX_CMD);
+
+       iscsi_set_state_wake_up(req, ISCSI_CMD_STATE_AFTER_PREPROC);
+       return;
+}
+
+static void iscsi_try_local_processing(struct iscsi_conn *conn)
+{
+       int local;
+
+       TRACE_ENTRY();
+
+       spin_lock_bh(&iscsi_wr_lock);
+       switch(conn->wr_state) {
+       case ISCSI_CONN_WR_STATE_IN_LIST:
+               list_del(&conn->wr_list_entry);
+               /* go through */
+       case ISCSI_CONN_WR_STATE_IDLE:
+#ifdef EXTRACHECKS
+               conn->wr_task = current;
+#endif
+               conn->wr_state = ISCSI_CONN_WR_STATE_PROCESSING;
+               conn->wr_space_ready = 0;
+               local = 1;
+               break;
+       default:
+               local = 0;
+               break;
+       }
+       spin_unlock_bh(&iscsi_wr_lock);
+
+       if (local) {
+               int rc = 1;
+               while(test_write_ready(conn)) {
+                       rc = iscsi_send(conn);
+                       if (rc <= 0) {
+                               break;
+                       }
+               }
+
+               spin_lock_bh(&iscsi_wr_lock);
+#ifdef EXTRACHECKS
+               conn->wr_task = NULL;
+#endif
+               if ((rc <= 0) || test_write_ready(conn)) {
+                       list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
+                       conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
+                       wake_up(&iscsi_wr_waitQ);
+               } else
+                       conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
+               spin_unlock_bh(&iscsi_wr_lock);
+       }
+
+       TRACE_EXIT();
+       return;
+}
+
+static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
+{
+       int resp_flags = scst_cmd_get_tgt_resp_flags(scst_cmd);
+       struct iscsi_cmnd *req = (struct iscsi_cmnd*)
+                                       scst_cmd_get_tgt_priv(scst_cmd);
+       struct iscsi_conn *conn = req->conn;
+       int status = scst_cmd_get_status(scst_cmd);
+       u8 *sense = scst_cmd_get_sense_buffer(scst_cmd);
+       int sense_len = scst_cmd_get_sense_buffer_len(scst_cmd);
+       int old_state = req->scst_state;
+
+       scst_cmd_set_tgt_priv(scst_cmd, NULL);
+
+       req->tmfabort |= scst_cmd_aborted(scst_cmd) ? 1 : 0;
+       if (unlikely(req->tmfabort)) {
+               TRACE_MGMT_DBG("req %p (scst_cmd %p) aborted", req,
+                       req->scst_cmd);
+               if (old_state == ISCSI_CMD_STATE_RESTARTED) {
+                       req->scst_state = ISCSI_CMD_STATE_PROCESSED;
+                       req_cmnd_release_force(req, ISCSI_FORCE_RELEASE_WRITE);
+               } else
+                       iscsi_set_state_wake_up(req, ISCSI_CMD_STATE_PROCESSED);
+               goto out;
+       }
+
+       if (unlikely(old_state != ISCSI_CMD_STATE_RESTARTED)) {
+               TRACE_DBG("req %p on %d state", req, old_state);
+               create_status_rsp(req, status, sense, sense_len);
+               switch(old_state) {
+               case ISCSI_CMD_STATE_RX_CMD:
+               case ISCSI_CMD_STATE_AFTER_PREPROC:
+                       break;
+               default:
+                       sBUG();
+               }
+               iscsi_set_state_wake_up(req, ISCSI_CMD_STATE_PROCESSED);
+               goto out;
+       }
+
+       req->scst_state = ISCSI_CMD_STATE_PROCESSED;
+
+       req->bufflen = scst_cmd_get_resp_data_len(scst_cmd);
+       req->sg = scst_cmd_get_sg(scst_cmd);
+
+       TRACE_DBG("req %p, resp_flags=%x, req->bufflen=%d, req->sg=%p", req,
+               resp_flags, req->bufflen, req->sg);
+
+       if ((req->bufflen != 0) && !(resp_flags & SCST_TSC_FLAG_STATUS)) {
+               PRINT_ERROR_PR("%s", "Sending DATA without STATUS is unsupported");
+               scst_set_cmd_error(scst_cmd,
+                       SCST_LOAD_SENSE(scst_sense_hardw_error));
+               resp_flags = scst_cmd_get_tgt_resp_flags(scst_cmd);
+               sBUG();
+       }
+
+       if (req->bufflen != 0) {
+               /* 
+                * Check above makes sure that SCST_TSC_FLAG_STATUS is set,
+                * so status is valid here, but in future that could change.
+                * ToDo
+                */
+               if (status != SAM_STAT_CHECK_CONDITION) {
+                       send_data_rsp(req, status,
+                               resp_flags & SCST_TSC_FLAG_STATUS);
+               } else {
+                       struct iscsi_cmnd *rsp;
+                       struct iscsi_scsi_rsp_hdr *rsp_hdr;
+                       int resid;
+                       send_data_rsp(req, 0, 0);
+                       if (resp_flags & SCST_TSC_FLAG_STATUS) {
+                               rsp = create_status_rsp(req, status, sense,
+                                       sense_len);
+                               rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
+                               resid = cmnd_read_size(req) - req->bufflen;
+                               if (resid > 0) {
+                                       rsp_hdr->flags |=
+                                               ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                                       rsp_hdr->residual_count = cpu_to_be32(resid);
+                               } else if (resid < 0) {
+                                       rsp_hdr->flags |=
+                                               ISCSI_FLG_RESIDUAL_OVERFLOW;
+                                       rsp_hdr->residual_count = cpu_to_be32(-resid);
+                               }
+                               iscsi_cmnd_init_write(rsp,
+                                       ISCSI_INIT_WRITE_REMOVE_HASH);
+                       }
+               }
+       } else if (resp_flags & SCST_TSC_FLAG_STATUS) {
+               struct iscsi_cmnd *rsp;
+               struct iscsi_scsi_rsp_hdr *rsp_hdr;
+               u32 resid;
+               rsp = create_status_rsp(req, status, sense, sense_len);
+               rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs;
+               resid = cmnd_read_size(req);
+               if (resid != 0) {
+                       rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
+                       rsp_hdr->residual_count = cpu_to_be32(resid);
+               }
+               iscsi_cmnd_init_write(rsp, ISCSI_INIT_WRITE_REMOVE_HASH);
+       }
+#ifdef EXTRACHECKS
+       else
+               sBUG();
+#endif
+
+       atomic_inc(&conn->conn_ref_cnt);
+       smp_mb__after_atomic_inc();
+
+       req_cmnd_release(req);
+
+       iscsi_try_local_processing(conn);
+
+       smp_mb__before_atomic_dec();
+       atomic_dec(&conn->conn_ref_cnt);
+
+out:
+       return SCST_TGT_RES_SUCCESS;
+}
+
+static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status)
+{
+       struct iscsi_cmnd *rsp;
+       struct iscsi_task_mgt_hdr *req_hdr =
+                               (struct iscsi_task_mgt_hdr *)&req->pdu.bhs;
+       struct iscsi_task_rsp_hdr *rsp_hdr;
+
+       TRACE(TRACE_MGMT, "req %p, status %d", req, status);
+
+       rsp = iscsi_cmnd_create_rsp_cmnd(req);
+       rsp_hdr = (struct iscsi_task_rsp_hdr *)&rsp->pdu.bhs;
+
+       rsp_hdr->opcode = ISCSI_OP_SCSI_TASK_MGT_RSP;
+       rsp_hdr->flags = ISCSI_FLG_FINAL;
+       rsp_hdr->itt = req_hdr->itt;
+       rsp_hdr->response = status;
+
+       if ((req_hdr->function & ISCSI_FUNCTION_MASK) ==
+                       ISCSI_FUNCTION_TARGET_COLD_RESET)
+               rsp->should_close_conn = 1;
+
+       iscsi_cmnd_init_write(rsp,
+               ISCSI_INIT_WRITE_REMOVE_HASH | ISCSI_INIT_WRITE_WAKE);
+       req_cmnd_release(req);
+}
+
+static inline int iscsi_get_mgmt_response(int status)
+{
+       switch(status) {
+       case SCST_MGMT_STATUS_SUCCESS:
+               return ISCSI_RESPONSE_FUNCTION_COMPLETE;
+
+       case SCST_MGMT_STATUS_TASK_NOT_EXIST:
+               return ISCSI_RESPONSE_UNKNOWN_TASK;
+
+       case SCST_MGMT_STATUS_LUN_NOT_EXIST:
+               return ISCSI_RESPONSE_UNKNOWN_LUN;
+
+       case SCST_MGMT_STATUS_FN_NOT_SUPPORTED:
+               return ISCSI_RESPONSE_FUNCTION_UNSUPPORTED;
+
+       case SCST_MGMT_STATUS_REJECTED:
+       case SCST_MGMT_STATUS_FAILED:
+       default:
+               return ISCSI_RESPONSE_FUNCTION_REJECTED;
+       }
+}
+
+static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
+{
+       struct iscsi_cmnd *req = (struct iscsi_cmnd*)
+                               scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
+       int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd));
+
+       TRACE(TRACE_MGMT, "scst_mcmd %p, status %d", scst_mcmd, 
+               scst_mgmt_cmd_get_status(scst_mcmd));
+
+       iscsi_send_task_mgmt_resp(req, status);
+
+       scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL);
+}
+
+static int iscsi_target_detect(struct scst_tgt_template *templ)
+{
+       /* Nothing to do */
+       return 0;
+}
+
+static int iscsi_target_release(struct scst_tgt *scst_tgt)
+{
+       /* Nothing to do */
+       return 0;
+}
+
+struct scst_tgt_template iscsi_template = {
+       .name = "iscsi",
+       .sg_tablesize = ISCSI_CONN_IOV_MAX,
+       .threads_num = 0,
+       .no_clustering = 1,
+       .xmit_response_atomic = 0,
+       .preprocessing_done_atomic = 1,
+       .detect = iscsi_target_detect,
+       .release = iscsi_target_release,
+       .xmit_response = iscsi_xmit_response,
+#ifndef NET_PAGE_CALLBACKS_DEFINED
+       .alloc_data_buf = iscsi_alloc_data_buf,
+#endif
+       .preprocessing_done = iscsi_preprocessing_done,
+       .pre_exec = iscsi_pre_exec,
+       .task_mgmt_fn_done = iscsi_task_mgmt_fn_done,
+};
+
+static __init int iscsi_run_threads(int count, char *name, int (*fn)(void *))
+{
+       int res = 0;
+       int i;
+       struct iscsi_thread_t *thr;
+
+       for (i = 0; i < count; i++) {
+               thr = kmalloc(sizeof(*thr), GFP_KERNEL);
+               if (!thr) {
+                       res = -ENOMEM;
+                       PRINT_ERROR_PR("Failed to allocate thr %d", res);
+                       goto out;
+               }
+               thr->thr = kthread_run(fn, NULL, "%s%d", name, i);
+               if (IS_ERR(thr->thr)) {
+                       res = PTR_ERR(thr->thr);
+                       PRINT_ERROR_PR("kthread_create() failed: %d", res);
+                       kfree(thr);
+                       goto out;
+               }
+               list_add(&thr->threads_list_entry, &iscsi_threads_list);
+       }
+
+out:
+       return res;
+}
+
+static void iscsi_stop_threads(void)
+{
+       struct iscsi_thread_t *t, *tmp;
+
+       list_for_each_entry_safe(t, tmp, &iscsi_threads_list,
+                               threads_list_entry) {
+               int rc = kthread_stop(t->thr);
+               if (rc < 0) {
+                       TRACE_MGMT_DBG("kthread_stop() failed: %d", rc);
+               }
+               list_del(&t->threads_list_entry);
+               kfree(t);
+       }
+}
+
+static int __init iscsi_init(void)
+{
+       int err;
+       int num;
+
+       PRINT_INFO_PR("iSCSI SCST Target - version %s", ISCSI_VERSION_STRING);
+
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+       err = net_set_get_put_page_callbacks(iscsi_get_page_callback,
+                       iscsi_put_page_callback);
+       if (err != 0) {
+               PRINT_INFO_PR("Unable to set page callbackes: %d", err);
+               goto out;
+       }
+#else
+       PRINT_INFO_PR("%s", "Patch put_page_callback.patch not applied on your "
+               "kernel. Running in the performance degraded mode. Refer "
+               "README file for details");
+#endif
+
+       BUILD_BUG_ON(MAX_DATA_SEG_LEN != (ISCSI_CONN_IOV_MAX<<PAGE_SHIFT));
+
+       if ((ctr_major = register_chrdev(0, ctr_name, &ctr_fops)) < 0) {
+               PRINT_ERROR_PR("failed to register the control device %d", ctr_major);
+               err = ctr_major;
+               goto out_callb;
+       }
+
+       if ((err = event_init()) < 0)
+               goto out_reg;
+
+       iscsi_cmnd_cache = kmem_cache_create("scst_iscsi_cmnd",
+               sizeof(struct iscsi_cmnd), 0, 0, NULL, NULL);
+       if (!iscsi_cmnd_cache) {
+               err = -ENOMEM;
+               goto out_event;
+       }
+
+       if (scst_register_target_template(&iscsi_template) < 0) {
+               err = -ENODEV;
+               goto out_kmem;
+       }
+       iscsi_template_registered = 1;
+
+       if ((err = iscsi_procfs_init()) < 0)
+               goto out_reg_tmpl;
+
+       num = max(num_online_cpus(), 2);
+
+       err = iscsi_run_threads(num, "iscsird", istrd);
+       if (err != 0)
+               goto out_thr;
+
+       err = iscsi_run_threads(num, "iscsiwr", istwr);
+       if (err != 0)
+               goto out_thr;
+
+out:
+       return err;
+
+out_thr:
+       iscsi_procfs_exit();
+       iscsi_stop_threads();
+
+out_reg_tmpl:
+       scst_unregister_target_template(&iscsi_template);
+
+out_kmem:
+       kmem_cache_destroy(iscsi_cmnd_cache);
+
+out_event:
+       event_exit();
+
+out_reg:
+       unregister_chrdev(ctr_major, ctr_name);
+
+out_callb:
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+       net_set_get_put_page_callbacks(NULL, NULL);
+#endif 
+       goto out;
+}
+
+static void __exit iscsi_exit(void)
+{
+       iscsi_stop_threads();
+
+       unregister_chrdev(ctr_major, ctr_name);
+
+       iscsi_procfs_exit();
+       event_exit();
+
+       kmem_cache_destroy(iscsi_cmnd_cache);
+
+       scst_unregister_target_template(&iscsi_template);
+
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+       net_set_get_put_page_callbacks(NULL, NULL);
+#endif
+}
+
+
+module_init(iscsi_init);
+module_exit(iscsi_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h
new file mode 100644 (file)
index 0000000..2fea9bf
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef __ISCSI_H__
+#define __ISCSI_H__
+
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/mm.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+#include <scsi_tgt.h>
+
+#include "iscsi_hdr.h"
+#include "iscsi_u.h"
+
+#include "iscsi_dbg.h"
+
+#define iscsi_sense_crc_error          ABORTED_COMMAND, 0x47, 0x5
+
+struct iscsi_sess_param {
+       int initial_r2t;
+       int immediate_data;
+       int max_connections;
+       int max_recv_data_length;
+       int max_xmit_data_length;
+       int max_burst_length;
+       int first_burst_length;
+       int default_wait_time;
+       int default_retain_time;
+       int max_outstanding_r2t;
+       int data_pdu_inorder;
+       int data_sequence_inorder;
+       int error_recovery_level;
+       int header_digest;
+       int data_digest;
+       int ofmarker;
+       int ifmarker;
+       int ofmarkint;
+       int ifmarkint;
+};
+
+struct iscsi_trgt_param {
+       int queued_cmnds;
+};
+
+struct network_thread_info {
+       struct task_struct *task;
+       unsigned int ready;
+};
+
+struct iscsi_cmnd;
+
+struct iscsi_target {
+       struct scst_tgt *scst_tgt;
+
+       struct mutex target_mutex;
+
+       struct list_head session_list; /* protected by target_mutex */
+
+       /* Both protected by target_mutex */
+       struct iscsi_sess_param trgt_sess_param;
+       struct iscsi_trgt_param trgt_param;
+
+       struct list_head target_list_entry;
+       u32 tid;
+       char name[ISCSI_NAME_LEN];
+};
+
+#define ISCSI_HASH_ORDER       8
+#define        cmnd_hashfn(itt)        hash_long((itt), ISCSI_HASH_ORDER)
+
+struct iscsi_session {
+       struct iscsi_target *target;
+       struct scst_session *scst_sess;
+
+       /* All 2 unprotected, since accessed only from a single read thread */
+       struct list_head pending_list;
+       u32 next_ttt;
+
+       /* Both unprotected, since read-only */
+       u32 max_queued_cmnds; 
+       u32 max_cmd_sn; /* Not used ??, ToDo */
+
+       spinlock_t sn_lock;
+       u32 exp_cmd_sn; /* protected by sn_lock */
+
+       /* read only, if there are connection(s) */
+       struct iscsi_sess_param sess_param;
+
+       spinlock_t cmnd_hash_lock;
+       struct list_head cmnd_hash[1 << ISCSI_HASH_ORDER];
+
+       struct list_head conn_list; /* protected by target_mutex */
+
+       struct list_head session_list_entry;
+
+       /* Bot don't need any protection */
+       char *initiator_name;
+       u64 sid;
+};
+
+#define ISCSI_CONN_IOV_MAX                     (PAGE_SIZE/sizeof(struct iovec))
+
+#define ISCSI_CONN_RD_STATE_IDLE               0
+#define ISCSI_CONN_RD_STATE_IN_LIST            1               
+#define ISCSI_CONN_RD_STATE_PROCESSING         2
+
+#define ISCSI_CONN_WR_STATE_IDLE               0
+#define ISCSI_CONN_WR_STATE_IN_LIST            1
+#define ISCSI_CONN_WR_STATE_SPACE_WAIT         2
+#define ISCSI_CONN_WR_STATE_PROCESSING         3
+
+struct iscsi_conn {
+       struct iscsi_session *session; /* owning session */
+
+       /* Both protected by session->sn_lock */
+       u32 stat_sn;
+       u32 exp_stat_sn;        
+
+       spinlock_t cmd_list_lock; /* BH lock */
+
+       /*
+        * IMPORTANT! If you find a cmd in cmd_list and immediately get_cmnd()
+        * it, it still can be destroyed immediately after you drop
+        * cmd_list_lock no matter how big is its ref_cnt!
+        */
+
+       /* Protected by cmd_list_lock */
+       struct list_head cmd_list; /* in/outcoming pdus */
+
+       atomic_t conn_ref_cnt;
+
+       spinlock_t write_list_lock;
+       /* List of data pdus to be sent, protected by write_list_lock */
+       struct list_head write_list; 
+
+       /* All 2 protected by iscsi_wr_lock */
+       unsigned short wr_state;
+       unsigned short wr_space_ready:1;
+
+       struct list_head wr_list_entry;
+
+#ifdef EXTRACHECKS
+       struct task_struct *wr_task;
+#endif
+
+       /* All are unprotected, since accessed only from a single write thread */
+       struct iscsi_cmnd *write_cmnd;
+       struct iovec *write_iop;
+       int write_iop_used;
+       struct iovec write_iov[2];
+       u32 write_size;
+       u32 write_offset;
+       int write_state;
+
+       /* Both don't need any protection */
+       struct file *file;
+       struct socket *sock;
+
+       void (*old_state_change)(struct sock *);
+       void (*old_data_ready)(struct sock *, int);
+       void (*old_write_space)(struct sock *);
+
+       /* Both read only */
+       int hdigest_type;
+       int ddigest_type;
+
+       /* All 4 protected by iscsi_rd_lock */
+       unsigned short rd_state;
+       unsigned short rd_data_ready:1;
+       unsigned short closing:1; /* Let's save some cache footprint by putting it here */
+
+       struct list_head rd_list_entry;
+
+#ifdef EXTRACHECKS
+       struct task_struct *rd_task;
+#endif
+
+       /* All are unprotected, since accessed only from a single read thread */
+       struct iscsi_cmnd *read_cmnd;
+       struct msghdr read_msg;
+       u32 read_size;
+       int read_state;
+       struct iovec *read_iov;
+
+       struct iscsi_target *target;
+
+       struct list_head conn_list_entry;       /* list entry in session conn_list */
+
+       /* Doesn't need any protection */
+       u16 cid;
+};
+
+struct iscsi_pdu {
+       struct iscsi_hdr bhs;
+       void *ahs;
+       unsigned int ahssize;
+       unsigned int datasize;
+};
+
+typedef void (iscsi_show_info_t)(struct seq_file *seq, struct iscsi_target *target);
+
+/* Command's states */
+#define ISCSI_CMD_STATE_NEW               0    /* New command and SCST processes it */
+#define ISCSI_CMD_STATE_RX_CMD            1    /* SCST processes cmd after scst_rx_cmd() */
+#define ISCSI_CMD_STATE_AFTER_PREPROC     2    /* The command returned from preprocessing_done() */
+#define ISCSI_CMD_STATE_RESTARTED         3    /* scst_restart_cmd() called and SCST processing it */
+#define ISCSI_CMD_STATE_PROCESSED         4    /* SCST done processing */
+
+/* 
+ * Most of the fields don't need any protection, since accessed from only a
+ * single thread, except where noted.
+ */
+struct iscsi_cmnd {
+       struct iscsi_conn *conn;
+
+       /* Some flags protected by conn->write_list_lock */
+       unsigned int hashed:1;
+       unsigned int should_close_conn:1;
+       unsigned int pending:1;
+       unsigned int own_sg:1;
+       unsigned int on_write_list:1;
+       unsigned int write_processing_started:1;
+       unsigned int data_waiting:1;
+       unsigned int force_cleanup_done:1;
+#ifdef EXTRACHECKS
+       unsigned int release_called:1;
+#endif
+
+       unsigned long tmfabort; /* it's async. with the above flags */
+
+       struct list_head hash_list_entry;
+
+       spinlock_t rsp_cmd_lock; /* BH lock */
+
+       /* Unions are for readability and grepability */
+
+       /*
+        * IMPORTANT! If you find a cmd in rsp_cmd_list and immediately
+        * get_cmnd() it, it still can be destroyed immediately after you drop
+        * rsp_cmd_lock no matter how big is its ref_cnt!
+        */
+
+       union {
+               /* Protected by rsp_cmd_lock */
+               struct list_head rsp_cmd_list;
+               struct list_head rsp_cmd_list_entry;
+       };
+
+       union {
+               struct list_head pending_list_entry;
+               struct list_head write_list_entry;
+       };
+
+       /*
+        * Unprotected, since could be accessed from only a single 
+        * thread at time
+        */
+       struct list_head rx_ddigest_cmd_list;
+       struct list_head rx_ddigest_cmd_list_entry;
+
+       struct iscsi_cmnd *parent_req;
+       struct iscsi_cmnd *cmd_req;
+
+       struct iscsi_target *target;
+
+       wait_queue_head_t scst_waitQ;
+       int scst_state;
+       struct scst_cmd *scst_cmd;
+       atomic_t ref_cnt;
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+       atomic_t net_ref_cnt;
+#endif
+
+       struct iscsi_pdu pdu;
+
+       struct scatterlist *sg;
+       int bufflen;
+       u32 r2t_sn;
+       u32 r2t_length;
+       u32 is_unsolicited_data;
+       u32 target_task_tag;
+       u32 outstanding_r2t;
+
+       u32 hdigest;
+       u32 ddigest;
+
+       int sg_cnt; /* valid only if own_sg is 1 */
+       struct list_head cmd_list_entry;
+};
+
+#define ISCSI_OP_SCSI_REJECT   ISCSI_OP_VENDOR1_CMD
+#define ISCSI_OP_PDU_REJECT    ISCSI_OP_VENDOR2_CMD
+#define ISCSI_OP_DATA_REJECT   ISCSI_OP_VENDOR3_CMD
+
+/* Flags for req_cmnd_release_force() */
+#define ISCSI_FORCE_RELEASE_WRITE      1
+
+extern struct mutex target_mgmt_mutex;
+
+extern struct file_operations ctr_fops;
+
+extern spinlock_t iscsi_rd_lock;
+extern struct list_head iscsi_rd_list;
+extern wait_queue_head_t iscsi_rd_waitQ;
+
+extern spinlock_t iscsi_wr_lock;
+extern struct list_head iscsi_wr_list;
+extern wait_queue_head_t iscsi_wr_waitQ;
+
+/* iscsi.c */
+extern struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *,
+       struct iscsi_cmnd *parent);
+extern int cmnd_rx_start(struct iscsi_cmnd *);
+extern void cmnd_rx_end(struct iscsi_cmnd *);
+extern void cmnd_tx_start(struct iscsi_cmnd *);
+extern void cmnd_tx_end(struct iscsi_cmnd *);
+extern void req_cmnd_release(struct iscsi_cmnd *req);
+extern void req_cmnd_release_force(struct iscsi_cmnd *req, int flags);
+extern void rsp_cmnd_release(struct iscsi_cmnd *);
+extern void cmnd_done(struct iscsi_cmnd *cmnd);
+extern void conn_abort(struct iscsi_conn *conn);
+
+/* conn.c */
+extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
+extern int conn_add(struct iscsi_session *, struct conn_info *);
+extern int conn_del(struct iscsi_session *, struct conn_info *);
+extern int conn_free(struct iscsi_conn *);
+extern void mark_conn_closed(struct iscsi_conn *);
+extern void iscsi_make_conn_wr_active(struct iscsi_conn *);
+extern void conn_info_show(struct seq_file *, struct iscsi_session *);
+
+/* nthread.c */
+extern int iscsi_send(struct iscsi_conn *conn);
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+extern void iscsi_get_page_callback(struct page *page);
+extern void iscsi_put_page_callback(struct page *page);
+#endif
+extern int istrd(void *arg);
+extern int istwr(void *arg);
+
+/* target.c */
+struct iscsi_target *target_lookup_by_id(u32);
+extern int target_add(struct target_info *);
+extern int target_del(u32 id);
+extern void target_del_all(void);
+
+/* config.c */
+extern int iscsi_procfs_init(void);
+extern void iscsi_procfs_exit(void);
+extern int iscsi_info_show(struct seq_file *, iscsi_show_info_t *);
+
+/* session.c */
+extern struct file_operations session_seq_fops;
+extern struct iscsi_session *session_lookup(struct iscsi_target *, u64);
+extern int session_add(struct iscsi_target *, struct session_info *);
+extern int session_del(struct iscsi_target *, u64);
+extern int session_free(struct iscsi_session *session);
+
+/* params.c */
+extern int iscsi_param_set(struct iscsi_target *, struct iscsi_param_info *, int);
+
+/* event.c */
+extern int event_send(u32, u64, u32, u32, int);
+extern int event_init(void);
+extern void event_exit(void);
+
+#define get_pgcnt(size, offset)        ((((size) + ((offset) & ~PAGE_MASK)) + PAGE_SIZE - 1) >> PAGE_SHIFT)
+
+static inline void iscsi_cmnd_get_length(struct iscsi_pdu *pdu)
+{
+#if defined(__BIG_ENDIAN)
+       pdu->ahssize = pdu->bhs.length.ahslength * 4;
+       pdu->datasize = pdu->bhs.length.datalength;
+#elif defined(__LITTLE_ENDIAN)
+       pdu->ahssize = (pdu->bhs.length & 0xff) * 4;
+       pdu->datasize = be32_to_cpu(pdu->bhs.length & ~0xff);
+#else
+#error
+#endif
+}
+
+static inline void iscsi_cmnd_set_length(struct iscsi_pdu *pdu)
+{
+#if defined(__BIG_ENDIAN)
+       pdu->bhs.length.ahslength = pdu->ahssize / 4;
+       pdu->bhs.length.datalength = pdu->datasize;
+#elif defined(__LITTLE_ENDIAN)
+       pdu->bhs.length = cpu_to_be32(pdu->datasize) | (pdu->ahssize / 4);
+#else
+#error
+#endif
+}
+
+#define cmnd_hdr(cmnd) ((struct iscsi_scsi_cmd_hdr *) (&((cmnd)->pdu.bhs)))
+#define cmnd_ttt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.ttt)
+#define cmnd_itt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.itt)
+#define cmnd_opcode(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OPCODE_MASK)
+#define cmnd_scsicode(cmnd) cmnd_hdr(cmnd)->scb[0]
+
+extern struct scst_tgt_template iscsi_template;
+
+static inline void cmnd_get(struct iscsi_cmnd *cmnd)
+{
+       atomic_inc(&cmnd->ref_cnt);
+       TRACE_DBG("cmnd %p, new cmnd->ref_cnt %d", cmnd,
+               atomic_read(&cmnd->ref_cnt));
+}
+
+static inline void cmnd_get_ordered(struct iscsi_cmnd *cmnd)
+{
+       cmnd_get(cmnd);
+       smp_mb__after_atomic_inc();
+}
+
+static inline void cmnd_put(struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("cmnd %p, new cmnd->ref_cnt %d", cmnd,
+               atomic_read(&cmnd->ref_cnt)-1);
+       sBUG_ON(atomic_read(&cmnd->ref_cnt) == 0);
+       if (atomic_dec_and_test(&cmnd->ref_cnt))
+               cmnd_done(cmnd);
+}
+
+/* conn->write_list_lock supposed to be locked */
+static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
+       struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("%p", cmnd);
+       list_add_tail(&cmnd->write_list_entry, &conn->write_list);
+       cmnd->on_write_list = 1;
+}
+
+/* conn->write_list_lock supposed to be locked */
+static inline void cmd_del_from_write_list(struct iscsi_cmnd *cmnd)
+{
+       TRACE_DBG("%p", cmnd);
+       list_del(&cmnd->write_list_entry);
+       cmnd->on_write_list = 0;
+}
+
+static inline int test_write_ready(struct iscsi_conn *conn)
+{
+       /*
+        * No need for write_list protection, in the worst case we will be
+        * restarted again.
+        */
+       return (!list_empty(&conn->write_list) || conn->write_cmnd);
+}
+
+#ifdef EXTRACHECKS
+#define iscsi_extracheck_is_rd_thread(conn) sBUG_ON(current != (conn)->rd_task)
+#define iscsi_extracheck_is_wr_thread(conn) sBUG_ON(current != (conn)->wr_task)
+#else
+static inline void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn) {}
+static inline void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn) {}
+#endif
+
+#endif /* __ISCSI_H__ */
diff --git a/iscsi-scst/kernel/iscsi_dbg.h b/iscsi-scst/kernel/iscsi_dbg.h
new file mode 100644 (file)
index 0000000..c55f90f
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef ISCSI_DBG_H
+#define ISCSI_DBG_H
+
+#include <scst_debug.h>
+
+#define TRACE_D_READ           0x80000000
+#define TRACE_D_WRITE          0x40000000
+#define TRACE_CONN_OC          0x20000000
+#define TRACE_D_IOV            0x10000000
+#define TRACE_D_DUMP_PDU       0x08000000
+
+#define TRACE_D_DATA           (TRACE_D_READ | TRACE_D_WRITE)
+
+#define TRACE_ALL_NO_DATA      (TRACE_ALL & ~TRACE_D_IOV & ~TRACE_D_DUMP_PDU & ~TRACE_D_DATA)
+
+#define LOG_PREFIX "iscsi-scst"
+
+#ifdef DEBUG
+#define ISCSI_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_LINE | TRACE_PID | \
+       TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_DEBUG | \
+       TRACE_MINOR | TRACE_SPECIAL | TRACE_CONN_OC)
+#else
+#define ISCSI_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_PID | \
+       TRACE_OUT_OF_MEM | TRACE_MGMT | \
+       TRACE_MINOR | TRACE_SPECIAL)
+#endif
+
+#ifdef DEBUG
+struct msghdr;
+struct iscsi_pdu;
+extern void iscsi_dump_iov(struct msghdr *msg);
+extern void iscsi_dump_pdu(struct iscsi_pdu *pdu);
+#else
+#define iscsi_dump_iov(x) do {} while (0)
+#define iscsi_dump_pdu(x) do {} while (0)
+#endif
+
+#if defined(DEBUG) || defined(TRACING)
+extern unsigned long iscsi_trace_flag;
+#define trace_flag iscsi_trace_flag
+
+#define TRACE_CONN_CLOSE(format, args...)                          \
+do {                                                                \
+  if (trace_flag & TRACE_CONN_OC)                                  \
+  {                                                                 \
+    char *__tflag = LOG_FLAG;                                       \
+    if (debug_print_prefix(trace_flag, __FUNCTION__, __LINE__) > 0) \
+    {                                                               \
+      __tflag = NO_FLAG;                                            \
+    }                                                               \
+    PRINT(NO_FLAG, "%s" format, __tflag, args);                     \
+  }                                                                 \
+} while(0)
+
+#else /* defined(DEBUG) || defined(TRACING) */
+#define TRACE_CONN_CLOSE(format, args...) {}
+#endif
+
+#endif
diff --git a/iscsi-scst/kernel/iscsi_hdr.h b/iscsi-scst/kernel/iscsi_hdr.h
new file mode 100644 (file)
index 0000000..1233dd2
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef __ISCSI_HDR_H__
+#define __ISCSI_HDR_H__
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define ISCSI_VERSION                  0
+
+#ifndef __packed
+#define __packed __attribute__ ((packed))
+#endif
+
+struct iscsi_hdr {
+       u8  opcode;                     /* 0 */
+       u8  flags;
+       u8  spec1[2];
+#if defined(__BIG_ENDIAN_BITFIELD)
+       struct {                        /* 4 */
+               unsigned ahslength : 8;
+               unsigned datalength : 24;
+       } length;
+#elif defined(__LITTLE_ENDIAN_BITFIELD)
+       u32 length;                     /* 4 */
+#endif
+       u16 lun[4];                     /* 8 */
+       u32 itt;                        /* 16 */
+       u32 ttt;                        /* 20 */
+       u32 sn;                         /* 24 */
+       u32 exp_sn;                     /* 28 */
+       u32 max_sn;                     /* 32 */
+       u32 spec3[3];                   /* 36 */
+} __packed;                            /* 48 */
+
+/* Opcode encoding bits */
+#define ISCSI_OP_RETRY                 0x80
+#define ISCSI_OP_IMMEDIATE             0x40
+#define ISCSI_OPCODE_MASK              0x3F
+
+/* Client to Server Message Opcode values */
+#define ISCSI_OP_NOOP_OUT              0x00
+#define ISCSI_OP_SCSI_CMD              0x01
+#define ISCSI_OP_SCSI_TASK_MGT_MSG     0x02
+#define ISCSI_OP_LOGIN_CMD             0x03
+#define ISCSI_OP_TEXT_CMD              0x04
+#define ISCSI_OP_SCSI_DATA_OUT         0x05
+#define ISCSI_OP_LOGOUT_CMD            0x06
+#define ISCSI_OP_SNACK_CMD             0x10
+
+#define ISCSI_OP_VENDOR1_CMD           0x1c
+#define ISCSI_OP_VENDOR2_CMD           0x1d
+#define ISCSI_OP_VENDOR3_CMD           0x1e
+#define ISCSI_OP_VENDOR4_CMD           0x1f
+
+/* Server to Client Message Opcode values */
+#define ISCSI_OP_NOOP_IN               0x20
+#define ISCSI_OP_SCSI_RSP              0x21
+#define ISCSI_OP_SCSI_TASK_MGT_RSP     0x22
+#define ISCSI_OP_LOGIN_RSP             0x23
+#define ISCSI_OP_TEXT_RSP              0x24
+#define ISCSI_OP_SCSI_DATA_IN          0x25
+#define ISCSI_OP_LOGOUT_RSP            0x26
+#define ISCSI_OP_R2T                   0x31
+#define ISCSI_OP_ASYNC_MSG             0x32
+#define ISCSI_OP_REJECT                        0x3f
+
+struct iscsi_ahs_hdr {
+       u16 ahslength;
+       u8 ahstype;
+} __packed;
+
+#define ISCSI_AHSTYPE_CDB              1
+#define ISCSI_AHSTYPE_RLENGTH          2
+
+union iscsi_sid {
+       struct {
+               u8 isid[6];             /* Initiator Session ID */
+               u16 tsih;               /* Target Session ID */
+       } id;
+       u64 id64;
+} __packed;
+
+struct iscsi_scsi_cmd_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 itt;
+       u32 data_length;
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u8  scb[16];
+} __packed;
+
+#define ISCSI_CMD_FINAL                0x80
+#define ISCSI_CMD_READ         0x40
+#define ISCSI_CMD_WRITE                0x20
+#define ISCSI_CMD_ATTR_MASK    0x07
+#define ISCSI_CMD_UNTAGGED     0x00
+#define ISCSI_CMD_SIMPLE       0x01
+#define ISCSI_CMD_ORDERED      0x02
+#define ISCSI_CMD_HEAD_OF_QUEUE        0x03
+#define ISCSI_CMD_ACA          0x04
+
+struct iscsi_cdb_ahdr {
+       u16 ahslength;
+       u8  ahstype;
+       u8  reserved;
+       u8  cdb[0];
+} __packed;
+
+struct iscsi_rlength_ahdr {
+       u16 ahslength;
+       u8  ahstype;
+       u8  reserved;
+       u32 read_length;
+} __packed;
+
+struct iscsi_scsi_rsp_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  response;
+       u8  cmd_status;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd1[2];
+       u32 itt;
+       u32 snack;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 exp_data_sn;
+       u32 bi_residual_count;
+       u32 residual_count;
+} __packed;
+
+#define ISCSI_FLG_RESIDUAL_UNDERFLOW           0x02
+#define ISCSI_FLG_RESIDUAL_OVERFLOW            0x04
+#define ISCSI_FLG_BIRESIDUAL_UNDERFLOW         0x08
+#define ISCSI_FLG_BIRESIDUAL_OVERFLOW          0x10
+
+#define ISCSI_RESPONSE_COMMAND_COMPLETED       0x00
+#define ISCSI_RESPONSE_TARGET_FAILURE          0x01
+
+struct iscsi_sense_data {
+       u16 length;
+       u8  data[0];
+} __packed;
+
+struct iscsi_task_mgt_hdr {
+       u8  opcode;
+       u8  function;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 itt;
+       u32 rtt;
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u32 ref_cmd_sn;
+       u32 exp_data_sn;
+       u32 rsvd2[2];
+} __packed;
+
+#define ISCSI_FUNCTION_MASK                    0x7f
+
+#define ISCSI_FUNCTION_ABORT_TASK              1
+#define ISCSI_FUNCTION_ABORT_TASK_SET          2
+#define ISCSI_FUNCTION_CLEAR_ACA               3
+#define ISCSI_FUNCTION_CLEAR_TASK_SET          4
+#define ISCSI_FUNCTION_LOGICAL_UNIT_RESET      5
+#define ISCSI_FUNCTION_TARGET_WARM_RESET       6
+#define ISCSI_FUNCTION_TARGET_COLD_RESET       7
+#define ISCSI_FUNCTION_TASK_REASSIGN           8
+
+struct iscsi_task_rsp_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  response;
+       u8  rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u32 rsvd3;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 rsvd4[3];
+} __packed;
+
+#define ISCSI_RESPONSE_FUNCTION_COMPLETE       0
+#define ISCSI_RESPONSE_UNKNOWN_TASK            1
+#define ISCSI_RESPONSE_UNKNOWN_LUN             2
+#define ISCSI_RESPONSE_TASK_ALLEGIANT          3
+#define ISCSI_RESPONSE_FAILOVER_UNSUPPORTED    4
+#define ISCSI_RESPONSE_FUNCTION_UNSUPPORTED    5
+#define ISCSI_RESPONSE_NO_AUTHORIZATION                6
+#define ISCSI_RESPONSE_FUNCTION_REJECTED       255
+
+struct iscsi_data_out_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 itt;
+       u32 ttt;
+       u32 rsvd2;
+       u32 exp_stat_sn;
+       u32 rsvd3;
+       u32 data_sn;
+       u32 buffer_offset;
+       u32 rsvd4;
+} __packed;
+
+struct iscsi_data_in_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  rsvd1;
+       u8  cmd_status;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u32 ttt;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 data_sn;
+       u32 buffer_offset;
+       u32 residual_count;
+} __packed;
+
+#define ISCSI_FLG_STATUS               0x01
+
+struct iscsi_r2t_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 itt;
+       u32 ttt;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 r2t_sn;
+       u32 buffer_offset;
+       u32 data_length;
+} __packed;
+
+struct iscsi_async_msg_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 ffffffff;
+       u32 rsvd2;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u8  async_event;
+       u8  async_vcode;
+       u16 param1;
+       u16 param2;
+       u16 param3;
+       u32 rsvd3;
+} __packed;
+
+#define ISCSI_ASYNC_SCSI               0
+#define ISCSI_ASYNC_LOGOUT             1
+#define ISCSI_ASYNC_DROP_CONNECTION    2
+#define ISCSI_ASYNC_DROP_SESSION       3
+#define ISCSI_ASYNC_PARAM_REQUEST      4
+#define ISCSI_ASYNC_VENDOR             255
+
+struct iscsi_text_req_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u32 ttt;
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u32 rsvd3[4];
+} __packed;
+
+struct iscsi_text_rsp_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u32 ttt;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 rsvd3[3];
+} __packed;
+
+struct iscsi_login_req_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  max_version;                /* Max. version supported */
+       u8  min_version;                /* Min. version supported */
+       u8  ahslength;
+       u8  datalength[3];
+       union iscsi_sid sid;
+       u32 itt;                        /* Initiator Task Tag */
+       u16 cid;                        /* Connection ID */
+       u16 rsvd1;
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u32 rsvd2[4];
+} __packed;
+
+struct iscsi_login_rsp_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  max_version;                /* Max. version supported */
+       u8  active_version;             /* Active version */
+       u8  ahslength;
+       u8  datalength[3];
+       union iscsi_sid sid;
+       u32 itt;                        /* Initiator Task Tag */
+       u32 rsvd1;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u8  status_class;               /* see Login RSP ststus classes below */
+       u8  status_detail;              /* see Login RSP Status details below */
+       u8  rsvd2[10];
+} __packed;
+
+#define ISCSI_FLG_FINAL                        0x80
+#define ISCSI_FLG_TRANSIT              0x80
+#define ISCSI_FLG_CSG_SECURITY         0x00
+#define ISCSI_FLG_CSG_LOGIN            0x04
+#define ISCSI_FLG_CSG_FULL_FEATURE     0x0c
+#define ISCSI_FLG_CSG_MASK             0x0c
+#define ISCSI_FLG_NSG_SECURITY         0x00
+#define ISCSI_FLG_NSG_LOGIN            0x01
+#define ISCSI_FLG_NSG_FULL_FEATURE     0x03
+#define ISCSI_FLG_NSG_MASK             0x03
+
+/* Login Status response classes */
+#define ISCSI_STATUS_SUCCESS           0x00
+#define ISCSI_STATUS_REDIRECT          0x01
+#define ISCSI_STATUS_INITIATOR_ERR     0x02
+#define ISCSI_STATUS_TARGET_ERR                0x03
+
+/* Login Status response detail codes */
+/* Class-0 (Success) */
+#define ISCSI_STATUS_ACCEPT            0x00
+
+/* Class-1 (Redirection) */
+#define ISCSI_STATUS_TGT_MOVED_TEMP    0x01
+#define ISCSI_STATUS_TGT_MOVED_PERM    0x02
+
+/* Class-2 (Initiator Error) */
+#define ISCSI_STATUS_INIT_ERR          0x00
+#define ISCSI_STATUS_AUTH_FAILED       0x01
+#define ISCSI_STATUS_TGT_FORBIDDEN     0x02
+#define ISCSI_STATUS_TGT_NOT_FOUND     0x03
+#define ISCSI_STATUS_TGT_REMOVED       0x04
+#define ISCSI_STATUS_NO_VERSION                0x05
+#define ISCSI_STATUS_TOO_MANY_CONN     0x06
+#define ISCSI_STATUS_MISSING_FIELDS    0x07
+#define ISCSI_STATUS_CONN_ADD_FAILED   0x08
+#define ISCSI_STATUS_INV_SESSION_TYPE  0x09
+#define ISCSI_STATUS_SESSION_NOT_FOUND 0x0a
+#define ISCSI_STATUS_INV_REQ_TYPE      0x0b
+
+/* Class-3 (Target Error) */
+#define ISCSI_STATUS_TARGET_ERROR      0x00
+#define ISCSI_STATUS_SVC_UNAVAILABLE   0x01
+#define ISCSI_STATUS_NO_RESOURCES      0x02
+
+struct iscsi_logout_req_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u16 cid;
+       u16 rsvd3;
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u32 rsvd4[4];
+} __packed;
+
+struct iscsi_logout_rsp_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  response;
+       u8  rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u32 rsvd3;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 rsvd4;
+       u16 time2wait;
+       u16 time2retain;
+       u32 rsvd5;
+} __packed;
+
+struct iscsi_snack_req_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 itt;
+       u32 ttt;
+       u32 rsvd3;
+       u32 exp_stat_sn;
+       u32 rsvd4[2];
+       u32 beg_run;
+       u32 run_length;
+} __packed;
+
+struct iscsi_reject_hdr {
+       u8  opcode;
+       u8  flags;
+       u8  reason;
+       u8  rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u32 rsvd2[2];
+       u32 ffffffff;
+       u32 rsvd3;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 data_sn;
+       u32 rsvd4[2];
+} __packed;
+
+#define ISCSI_REASON_NO_FULL_FEATURE_PHASE     0x01
+#define ISCSI_REASON_DATA_DIGEST_ERROR         0x02
+#define ISCSI_REASON_DATA_SNACK_REJECT         0x03
+#define ISCSI_REASON_PROTOCOL_ERROR            0x04
+#define ISCSI_REASON_UNSUPPORTED_COMMAND       0x05
+#define ISCSI_REASON_IMMEDIATE_COMMAND_REJECT  0x06
+#define ISCSI_REASON_TASK_IN_PROGRESS          0x07
+#define ISCSI_REASON_INVALID_SNACK             0x08
+#define ISCSI_REASON_NO_BOOKMARK               0x09
+#define ISCSI_REASON_BOOKMARK_REJECT           0x0a
+#define ISCSI_REASON_NEGOTIATION_RESET         0x0b
+#define ISCSI_REASON_WAITING_LOGOUT            0x0c
+
+
+struct iscsi_nop_out_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 itt;
+       u32 ttt;
+       u32 cmd_sn;
+       u32 exp_stat_sn;
+       u32 rsvd2[4];
+} __packed;
+
+struct iscsi_nop_in_hdr {
+       u8  opcode;
+       u8  flags;
+       u16 rsvd1;
+       u8  ahslength;
+       u8  datalength[3];
+       u16 lun[4];
+       u32 itt;
+       u32 ttt;
+       u32 stat_sn;
+       u32 exp_cmd_sn;
+       u32 max_cmd_sn;
+       u32 rsvd2[3];
+} __packed;
+
+#define ISCSI_RESERVED_TAG     (0xffffffffU)
+
+#endif /* __ISCSI_HDR_H__ */
diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c
new file mode 100644 (file)
index 0000000..67a49ac
--- /dev/null
@@ -0,0 +1,999 @@
+/*
+ * Network thread.
+ * (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
+ * This code is licenced under the GPL.
+ */
+
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/kthread.h>
+#include <asm/ioctls.h>
+#include <linux/delay.h>
+#include <net/tcp.h>
+
+#include "iscsi.h"
+#include "digest.h"
+
+enum rx_state {
+       RX_INIT_BHS, /* Must be zero. */
+       RX_BHS,
+
+       RX_INIT_AHS,
+       RX_AHS,
+
+       RX_INIT_HDIGEST,
+       RX_HDIGEST,
+       RX_CHECK_HDIGEST,
+
+       RX_INIT_DATA,
+       RX_DATA,
+
+       RX_INIT_DDIGEST,
+       RX_DDIGEST,
+       RX_CHECK_DDIGEST,
+
+       RX_END,
+};
+
+enum tx_state {
+       TX_INIT, /* Must be zero. */
+       TX_BHS_DATA,
+       TX_INIT_DDIGEST,
+       TX_DDIGEST,
+       TX_END,
+};
+
+/* No locks */
+static void close_conn(struct iscsi_conn *conn)
+{
+       struct iscsi_session *session = conn->session;
+       struct iscsi_target *target = conn->target;
+
+       TRACE_ENTRY();
+
+       TRACE_CONN_CLOSE("conn %p, conn_ref_cnt=%d", conn,
+               atomic_read(&conn->conn_ref_cnt));
+
+       iscsi_extracheck_is_rd_thread(conn);
+
+       /* We want all our already send operations to complete */
+       conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
+
+       conn_abort(conn);
+
+       if (conn->read_state != RX_INIT_BHS) {
+               req_cmnd_release_force(conn->read_cmnd, 0);
+               conn->read_cmnd = NULL;
+               conn->read_state = RX_INIT_BHS;
+       }
+
+       /* ToDo: not the best way to wait */
+       while(atomic_read(&conn->conn_ref_cnt) != 0) {
+               struct iscsi_cmnd *cmnd;
+
+               if (!list_empty(&session->pending_list)) {
+                       struct list_head *pending_list = &session->pending_list;
+                       struct iscsi_cmnd *tmp;
+
+                       TRACE_CONN_CLOSE("Disposing pending commands on conn "
+                               "%p, conn_ref_cnt=%d", conn,
+                               atomic_read(&conn->conn_ref_cnt));
+                       list_for_each_entry_safe(cmnd, tmp, pending_list,
+                                               pending_list_entry) {
+                               if (cmnd->conn == conn) {
+                                       TRACE_CONN_CLOSE("Freeing pending cmd %p",
+                                               cmnd);
+                                       list_del(&cmnd->pending_list_entry);
+                                       cmnd->pending = 0;
+                                       req_cmnd_release_force(cmnd, 0);
+                               }
+                       }
+               }
+
+               iscsi_make_conn_wr_active(conn);
+               msleep(50);
+
+               TRACE_CONN_CLOSE("conn %p, conn_ref_cnt %d left, wr_state %d",
+                       conn, atomic_read(&conn->conn_ref_cnt), conn->wr_state);
+#ifdef DEBUG
+               {
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+                       struct iscsi_cmnd *rsp;
+#endif
+                       spin_lock_bh(&conn->cmd_list_lock);
+                       list_for_each_entry(cmnd, &conn->cmd_list, cmd_list_entry) {
+                               TRACE_DBG("cmd %p, scst_state %x, data_waiting "
+                                       "%d, ref_cnt %d, parent_req %p", cmnd,
+                                       cmnd->scst_state, cmnd->data_waiting,
+                                       atomic_read(&cmnd->ref_cnt), cmnd->parent_req);
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+                               TRACE_DBG("net_ref_cnt %d, sg %p",
+                                       atomic_read(&cmnd->net_ref_cnt), cmnd->sg);
+                               if (cmnd->sg != NULL) {
+                                       int sg_cnt, i;
+                                       sg_cnt = get_pgcnt(cmnd->bufflen,
+                                               cmnd->sg[0].offset);
+                                       for(i = 0; i < sg_cnt; i++) {
+                                               TRACE_DBG("page %p, net_priv %p, _count %d",
+                                                       cmnd->sg[i].page, cmnd->sg[i].page->net_priv,
+                                                       atomic_read(&cmnd->sg[i].page->_count));
+                                       }
+                               }
+
+                               sBUG_ON(cmnd->parent_req != NULL);
+                               
+                               spin_lock_bh(&cmnd->rsp_cmd_lock);
+                               list_for_each_entry(rsp, &cmnd->rsp_cmd_list, rsp_cmd_list_entry) {
+                                       TRACE_DBG("  rsp %p, ref_cnt %d, net_ref_cnt %d, "
+                                               "sg %p", rsp, atomic_read(&rsp->ref_cnt),
+                                               atomic_read(&rsp->net_ref_cnt), rsp->sg);
+                                       if ((rsp->sg != cmnd->sg) && (rsp->sg != NULL)) {
+                                               int sg_cnt, i;
+                                               sg_cnt = get_pgcnt(rsp->bufflen,
+                                                       rsp->sg[0].offset);
+                                               sBUG_ON(rsp->sg_cnt != sg_cnt);
+                                               for(i = 0; i < sg_cnt; i++) {
+                                                       TRACE_DBG("    page %p, net_priv %p, "
+                                                               "_count %d", rsp->sg[i].page,
+                                                               rsp->sg[i].page->net_priv,
+                                                               atomic_read(&rsp->sg[i].page->_count));
+                                               }
+                                       }
+                               }
+                               spin_unlock_bh(&cmnd->rsp_cmd_lock);
+#endif
+                       }
+                       spin_unlock_bh(&conn->cmd_list_lock);
+               }
+#endif
+       }
+
+       write_lock_bh(&conn->sock->sk->sk_callback_lock);
+       conn->sock->sk->sk_state_change = conn->old_state_change;
+       conn->sock->sk->sk_data_ready = conn->old_data_ready;
+       conn->sock->sk->sk_write_space = conn->old_write_space;
+       write_unlock_bh(&conn->sock->sk->sk_callback_lock);
+
+       while(conn->wr_state != ISCSI_CONN_WR_STATE_IDLE) {
+               TRACE_CONN_CLOSE("Waiting for wr thread (conn %p), wr_state %x",
+                       conn, conn->wr_state);
+               msleep(50);
+       }
+
+       mutex_lock(&target->target_mutex);
+       conn_free(conn);
+       if (list_empty(&session->conn_list))
+               session_del(target, session->sid);
+       mutex_unlock(&target->target_mutex);
+
+       TRACE_CONN_CLOSE("Notifying user space about closing conn %p", conn);
+       event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0);
+
+       TRACE_EXIT();
+       return;
+}
+
+static inline void iscsi_conn_init_read(struct iscsi_conn *conn, void *data, size_t len)
+{
+       len = (len + 3) & -4; // XXX ???
+       conn->read_iov[0].iov_base = data;
+       conn->read_iov[0].iov_len = len;
+       conn->read_msg.msg_iov = conn->read_iov;
+       conn->read_msg.msg_iovlen = 1;
+       conn->read_size = (len + 3) & -4;
+}
+
+static void iscsi_conn_read_ahs(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
+{
+       /* ToDo: __GFP_NOFAIL ?? */
+       cmnd->pdu.ahs = kmalloc(cmnd->pdu.ahssize, __GFP_NOFAIL|GFP_KERNEL);
+       sBUG_ON(cmnd->pdu.ahs == NULL);
+       iscsi_conn_init_read(conn, cmnd->pdu.ahs, cmnd->pdu.ahssize);
+}
+
+static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
+{
+       struct iscsi_cmnd *cmnd = NULL;
+
+       spin_lock(&conn->write_list_lock);
+       if (!list_empty(&conn->write_list)) {
+               cmnd = list_entry(conn->write_list.next, struct iscsi_cmnd,
+                               write_list_entry);
+               cmd_del_from_write_list(cmnd);
+               cmnd->write_processing_started = 1;
+       }
+       spin_unlock(&conn->write_list_lock);
+
+       return cmnd;
+}
+
+static int do_recv(struct iscsi_conn *conn, int state)
+{
+       mm_segment_t oldfs;
+       struct msghdr msg;
+       int res, first_len;
+
+       if (unlikely(conn->closing)) {
+               res = -EIO;
+               goto out;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = conn->read_msg.msg_iov;
+       msg.msg_iovlen = conn->read_msg.msg_iovlen;
+       first_len = msg.msg_iov->iov_len;
+
+       oldfs = get_fs();
+       set_fs(get_ds());
+       res = sock_recvmsg(conn->sock, &msg, conn->read_size, MSG_DONTWAIT | MSG_NOSIGNAL);
+       set_fs(oldfs);
+
+       if (res <= 0) {
+               switch (res) {
+               case -EAGAIN:
+               case -ERESTARTSYS:
+                       TRACE_DBG("EAGAIN or ERESTARTSYS (%d) received for "
+                               "conn %p", res, conn);
+                       break;
+               default:
+                       PRINT_ERROR_PR("sock_recvmsg() failed: %d", res);
+                       mark_conn_closed(conn);
+                       break;
+               }
+       } else {
+               /*
+                * To save some considerable effort and CPU power we suppose
+                * that TCP functions adjust conn->read_msg.msg_iov and
+                * conn->read_msg.msg_iovlen on amount of copied data. This
+                * BUG_ON is intended to catch if it is changed in the future.
+                */
+               sBUG_ON((res >= first_len) &&
+                       (conn->read_msg.msg_iov->iov_len != 0));
+               conn->read_size -= res;
+               if (conn->read_size) {
+                       if (res >= first_len) {
+                               int done = 1 + ((res - first_len) >> PAGE_SHIFT);
+                               conn->read_msg.msg_iov += done;
+                               conn->read_msg.msg_iovlen -= done;
+                       }
+               } else
+                       conn->read_state = state;
+       }
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static int rx_hdigest(struct iscsi_conn *conn)
+{
+       struct iscsi_cmnd *cmnd = conn->read_cmnd;
+       int res = digest_rx_header(cmnd);
+
+       if (unlikely(res != 0)) {
+               PRINT_ERROR_PR("rx header digest for initiator %s failed "
+                       "(%d)", conn->session->initiator_name, res);
+               mark_conn_closed(conn);
+       }
+       return res;
+}
+
+static struct iscsi_cmnd *create_cmnd(struct iscsi_conn *conn)
+{
+       struct iscsi_cmnd *cmnd;
+
+       cmnd = cmnd_alloc(conn, NULL);
+       iscsi_conn_init_read(cmnd->conn, &cmnd->pdu.bhs, sizeof(cmnd->pdu.bhs));
+       conn->read_state = RX_BHS;
+
+       return cmnd;
+}
+
+/* Returns >0 for success, <=0 for error or successful finish */
+static int recv(struct iscsi_conn *conn)
+{
+       struct iscsi_cmnd *cmnd = conn->read_cmnd;
+       int hdigest, ddigest, res = 1, rc;
+
+       TRACE_ENTRY();
+
+       hdigest = conn->hdigest_type & DIGEST_NONE ? 0 : 1;
+       ddigest = conn->ddigest_type & DIGEST_NONE ? 0 : 1;
+
+       switch (conn->read_state) {
+       case RX_INIT_BHS:
+               sBUG_ON(cmnd != NULL);
+               cmnd = conn->read_cmnd = create_cmnd(conn);
+       case RX_BHS:
+               res = do_recv(conn, RX_INIT_AHS);
+               if (res <= 0 || conn->read_state != RX_INIT_AHS)
+                       break;
+       case RX_INIT_AHS:
+               iscsi_cmnd_get_length(&cmnd->pdu);
+               if (cmnd->pdu.ahssize) {
+                       iscsi_conn_read_ahs(conn, cmnd);
+                       conn->read_state = RX_AHS;
+               } else
+                       conn->read_state = hdigest ? RX_INIT_HDIGEST : RX_INIT_DATA;
+
+               if (conn->read_state != RX_AHS)
+                       break;
+       case RX_AHS:
+               res = do_recv(conn, hdigest ? RX_INIT_HDIGEST : RX_INIT_DATA);
+               if (res <= 0 || conn->read_state != RX_INIT_HDIGEST)
+                       break;
+       case RX_INIT_HDIGEST:
+               iscsi_conn_init_read(conn, &cmnd->hdigest, sizeof(u32));
+               conn->read_state = RX_HDIGEST;
+       case RX_HDIGEST:
+               res = do_recv(conn, RX_CHECK_HDIGEST);
+               if (res <= 0 || conn->read_state != RX_CHECK_HDIGEST)
+                       break;
+       case RX_CHECK_HDIGEST:
+               rc = rx_hdigest(conn);
+               if (likely(rc == 0))
+                       conn->read_state = RX_INIT_DATA;
+               else {
+                       res = rc;
+                       break;
+               }
+       case RX_INIT_DATA:
+               rc = cmnd_rx_start(cmnd);
+               if (unlikely(rc != 0)) {
+                       sBUG_ON(!conn->closing);
+                       conn->read_state = RX_END;
+                       res = rc;
+                       /* cmnd will be freed in close_conn() */
+                       goto out;
+               }
+               conn->read_state = cmnd->pdu.datasize ? RX_DATA : RX_END;
+               if (conn->read_state != RX_DATA)
+                       break;
+       case RX_DATA:
+               res = do_recv(conn, ddigest ? RX_INIT_DDIGEST : RX_END);
+               if (res <= 0 || conn->read_state != RX_INIT_DDIGEST)
+                       break;
+       case RX_INIT_DDIGEST:
+               iscsi_conn_init_read(conn, &cmnd->ddigest, sizeof(u32));
+               conn->read_state = RX_DDIGEST;
+       case RX_DDIGEST:
+               res = do_recv(conn, RX_CHECK_DDIGEST);
+               if (res <= 0 || conn->read_state != RX_CHECK_DDIGEST)
+                       break;
+       case RX_CHECK_DDIGEST:
+               conn->read_state = RX_END;
+               if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_CMD) {
+                       TRACE_DBG("Adding RX ddigest cmd %p to digest list "
+                               "of self", cmnd);
+                       list_add_tail(&cmnd->rx_ddigest_cmd_list_entry,
+                               &cmnd->rx_ddigest_cmd_list);
+                       cmnd_get(cmnd);
+                       conn->read_state = RX_END;
+               } else if (cmnd_opcode(cmnd) != ISCSI_OP_SCSI_DATA_OUT) {
+                       /*
+                        * We could get here only for NOP-Out. ISCSI RFC doesn't
+                        * specify how to deal with digest errors in this case.
+                        * Is closing connection correct?
+                        */
+                       TRACE_DBG("cmnd %p, opcode %x: checking RX "
+                               "ddigest inline", cmnd, cmnd_opcode(cmnd));
+                       rc = digest_rx_data(cmnd);
+                       if (unlikely(rc != 0)) {
+                               conn->read_state = RX_CHECK_DDIGEST;
+                               mark_conn_closed(conn);
+                       }
+               }
+               break;
+       default:
+               PRINT_ERROR_PR("%d %x", conn->read_state, cmnd_opcode(cmnd));
+               sBUG();
+       }
+
+       if (res <= 0)
+               goto out;
+
+       if (conn->read_state != RX_END)
+               goto out;
+
+       if (conn->read_size) {
+               PRINT_ERROR_PR("%d %x %d", res, cmnd_opcode(cmnd), conn->read_size);
+               sBUG();
+       }
+
+       cmnd_rx_end(cmnd);
+
+       sBUG_ON(conn->read_size != 0);
+
+       conn->read_cmnd = NULL;
+       conn->read_state = RX_INIT_BHS;
+       res = 0;
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* No locks, conn is rd processing */
+static int process_read_io(struct iscsi_conn *conn, int *closed)
+{
+       int res;
+
+       do {
+               res = recv(conn);
+               if (unlikely(conn->closing)) {
+                       close_conn(conn);
+                       *closed = 1;
+                       break;
+               }
+       } while(res > 0);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/*
+ * Called under iscsi_rd_lock and BHs disabled, but will drop it inside,
+ * then reaquire.
+ */
+static void scst_do_job_rd(void)
+{
+       TRACE_ENTRY();
+
+       /* We delete/add to tail connections to maintain fairness between them */
+
+       while(!list_empty(&iscsi_rd_list)) {
+               int rc, closed = 0;
+               struct iscsi_conn *conn = list_entry(iscsi_rd_list.next,
+                       typeof(*conn), rd_list_entry);
+
+               list_del(&conn->rd_list_entry);
+
+               sBUG_ON(conn->rd_state == ISCSI_CONN_RD_STATE_PROCESSING);
+               conn->rd_data_ready = 0;
+               conn->rd_state = ISCSI_CONN_RD_STATE_PROCESSING;
+#ifdef EXTRACHECKS
+               conn->rd_task = current;
+#endif
+               spin_unlock_bh(&iscsi_rd_lock);
+
+               rc = process_read_io(conn, &closed);
+
+               spin_lock_bh(&iscsi_rd_lock);
+
+               if (closed)
+                       continue;
+
+#ifdef EXTRACHECKS
+               conn->rd_task = NULL;
+#endif
+               if ((rc == 0) || conn->rd_data_ready) {
+                       list_add_tail(&conn->rd_list_entry, &iscsi_rd_list);
+                       conn->rd_state = ISCSI_CONN_RD_STATE_IN_LIST;
+               } else
+                       conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
+       }
+
+       TRACE_EXIT();
+       return;
+}
+
+static inline int test_rd_list(void)
+{
+       int res = !list_empty(&iscsi_rd_list) ||
+                 unlikely(kthread_should_stop());
+       return res;
+}
+
+int istrd(void *arg)
+{
+       TRACE_ENTRY();
+
+       current->flags |= PF_NOFREEZE;
+
+       spin_lock_bh(&iscsi_rd_lock);
+       while(!kthread_should_stop()) {
+               wait_queue_t wait;
+               init_waitqueue_entry(&wait, current);
+
+               if (!test_rd_list()) {
+                       add_wait_queue_exclusive(&iscsi_rd_waitQ, &wait);
+                       for (;;) {
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               if (test_rd_list())
+                                       break;
+                               spin_unlock_bh(&iscsi_rd_lock);
+                               schedule();
+                               spin_lock_bh(&iscsi_rd_lock);
+                       }
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&iscsi_rd_waitQ, &wait);
+               }
+               scst_do_job_rd();
+       }
+       spin_unlock_bh(&iscsi_rd_lock);
+
+       /*
+        * If kthread_should_stop() is true, we are guaranteed to be
+        * on the module unload, so iscsi_rd_list must be empty.
+        */
+       sBUG_ON(!list_empty(&iscsi_rd_list));
+
+       TRACE_EXIT();
+       return 0;
+}
+
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+void iscsi_get_page_callback(struct page *page)
+{
+       struct iscsi_cmnd *cmd = (struct iscsi_cmnd*)page->net_priv;
+       int v;
+
+       TRACE_DBG("cmd %p, page %p, _count %d, new net_ref_cnt %d",
+               cmd, page, atomic_read(&page->_count),
+               atomic_read(&cmd->net_ref_cnt)+1);
+
+       v = atomic_inc_return(&cmd->net_ref_cnt);
+       if (v == 1) {
+               TRACE_DBG("getting cmd %p for page %p", cmd, page);
+               cmnd_get(cmd);
+       }
+}
+
+void iscsi_put_page_callback(struct page *page)
+{
+       struct iscsi_cmnd *cmd = (struct iscsi_cmnd*)page->net_priv;
+
+       TRACE_DBG("cmd %p, page %p, _count %d, new net_ref_cnt %d",
+               cmd, page, atomic_read(&page->_count),
+               atomic_read(&cmd->net_ref_cnt)-1);
+
+       if (atomic_dec_and_test(&cmd->net_ref_cnt)) {
+               int i, sg_cnt = get_pgcnt(cmd->bufflen, cmd->sg[0].offset);
+               for(i = 0; i < sg_cnt; i++) {
+                       TRACE_DBG("Clearing page %p", cmd->sg[i].page);
+                       cmd->sg[i].page->net_priv = NULL;
+               }
+               cmnd_put(cmd);
+       }
+}
+#endif
+
+/* This is partially taken from the Ardis code. */
+static int write_data(struct iscsi_conn *conn)
+{
+       mm_segment_t oldfs;
+       struct file *file;
+       struct socket *sock;
+       ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int);
+       struct iscsi_cmnd *write_cmnd = conn->write_cmnd;
+       struct iscsi_cmnd *ref_cmd;
+       struct scatterlist *sg;
+       struct iovec *iop;
+       int saved_size, size, sendsize;
+       int offset, idx;
+       int flags, res, count;
+
+       iscsi_extracheck_is_wr_thread(conn);
+
+       if (write_cmnd->own_sg == 0)
+               ref_cmd = write_cmnd->parent_req;
+       else
+               ref_cmd = write_cmnd;
+
+       file = conn->file;
+       saved_size = size = conn->write_size;
+       iop = conn->write_iop;
+       count = conn->write_iop_used;
+
+       if (iop) while (1) {
+               loff_t off = 0;
+               int rest;
+
+               sBUG_ON(count > sizeof(conn->write_iov)/sizeof(conn->write_iov[0]));
+retry:
+               oldfs = get_fs();
+               set_fs(KERNEL_DS);
+               res = vfs_writev(file, (struct iovec __user *)iop, count, &off);
+               set_fs(oldfs);
+               TRACE(TRACE_D_DATA, "%#Lx:%u: %d(%ld)",
+                       (unsigned long long) conn->session->sid, conn->cid,
+                       res, (long) iop->iov_len);
+               if (unlikely(res <= 0)) {
+                       if (res == -EAGAIN) {
+                               conn->write_iop = iop;
+                               conn->write_iop_used = count;
+                               goto out_iov;
+                       } else if (res == -EINTR)
+                               goto retry;
+                       goto err;
+               }
+
+               rest = res;
+               size -= res;
+               while (iop->iov_len <= rest && rest) {
+                       rest -= iop->iov_len;
+                       iop++;
+                       count--;
+               }
+               if (count == 0) {
+                       conn->write_iop = NULL;
+                       conn->write_iop_used = 0;
+                       if (size)
+                               break;
+                       goto out_iov;
+               }
+               sBUG_ON(iop > conn->write_iov + 
+                       sizeof(conn->write_iov)/sizeof(conn->write_iov[0]));
+               iop->iov_base += rest;
+               iop->iov_len -= rest;
+       }
+
+       sg = write_cmnd->sg;
+       if (sg == NULL) {
+               PRINT_ERROR_PR("%s", "warning data missing!");
+               return 0;
+       }
+       offset = conn->write_offset;
+       idx = offset >> PAGE_SHIFT;
+       offset &= ~PAGE_MASK;
+
+       sock = conn->sock;
+
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+       sendpage = sock->ops->sendpage;
+#else
+       if ((write_cmnd->parent_req->scst_cmd != NULL) &&
+           scst_cmd_get_data_buff_alloced(write_cmnd->parent_req->scst_cmd))
+               sendpage = sock_no_sendpage;
+       else
+               sendpage = sock->ops->sendpage;
+#endif
+
+       flags = MSG_DONTWAIT;
+
+       while (1) {
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+               if (unlikely((sg[idx].page->net_priv != NULL) &&
+                               (sg[idx].page->net_priv != ref_cmd))) {
+                       PRINT_ERROR_PR("net_priv isn't NULL and != ref_cmd "
+                               "(write_cmnd %p, ref_cmd %p, sg %p, idx %d, "
+                               "net_priv %p)", write_cmnd, ref_cmd, sg, idx,
+                               sg[idx].page->net_priv);
+                       sBUG();
+               }
+               sg[idx].page->net_priv = ref_cmd;
+#endif
+               sendsize = PAGE_SIZE - offset;
+               if (size <= sendsize) {
+retry2:
+                       res = sendpage(sock, sg[idx].page, offset, size, flags);
+                       TRACE(TRACE_D_DATA, "%s %#Lx:%u: %d(%lu,%u,%u)",
+                               sock->ops->sendpage ? "sendpage" : "sock_no_sendpage",
+                               (unsigned long long)conn->session->sid, conn->cid,
+                               res, sg[idx].page->index, offset, size);
+                       if (unlikely(res <= 0)) {
+                               if (res == -EINTR)
+                                       goto retry2;
+                               else
+                                       goto out_res;
+                       }
+                       if (res == size) {
+                               conn->write_size = 0;
+                               return saved_size;
+                       }
+                       offset += res;
+                       size -= res;
+                       continue;
+               }
+
+retry1:
+               res = sendpage(sock, sg[idx].page, offset, sendsize,
+                       flags | MSG_MORE);
+               TRACE(TRACE_D_DATA, "%s %#Lx:%u: %d(%lu,%u,%u)",
+                       sock->ops->sendpage ? "sendpage" : "sock_no_sendpage",
+                       (unsigned long long ) conn->session->sid, conn->cid,
+                       res, sg[idx].page->index, offset, sendsize);
+               if (unlikely(res <= 0)) {
+                       if (res == -EINTR)
+                               goto retry1;
+                       else
+                               goto out_res;
+               }
+               if (res == sendsize) {
+                       idx++;
+                       offset = 0;
+               } else
+                       offset += res;
+               size -= res;
+       }
+out:
+       conn->write_offset = (idx << PAGE_SHIFT) + offset;
+out_iov:
+       conn->write_size = size;
+       if ((saved_size == size) && res == -EAGAIN)
+               return res;
+
+       return saved_size - size;
+
+out_res:
+#ifdef NET_PAGE_CALLBACKS_DEFINED
+       if (atomic_read(&ref_cmd->net_ref_cnt) == 0) {
+               TRACE_DBG("sendpage() returned %d, zeroing net_priv", res);
+               sg[idx].page->net_priv = NULL;
+       }
+#endif
+       if (res == -EAGAIN)
+               goto out;
+       /* else go through */
+
+err:
+#ifndef DEBUG
+       if (!conn->closing)
+#endif
+       {
+               PRINT_ERROR_PR("error %d at sid:cid %#Lx:%u, cmnd %p", res,
+                       (unsigned long long)conn->session->sid, conn->cid,
+                       conn->write_cmnd);
+       }
+       return res;
+}
+
+static int exit_tx(struct iscsi_conn *conn, int res)
+{
+       iscsi_extracheck_is_wr_thread(conn);
+
+       switch (res) {
+       case -EAGAIN:
+       case -ERESTARTSYS:
+               res = 0;
+               break;
+       default:
+#ifndef DEBUG
+               if (!conn->closing)
+#endif
+               {
+                       PRINT_ERROR_PR("Sending data failed: initiator %s, "
+                               "write_size %d, write_state %d, res %d",
+                               conn->session->initiator_name, conn->write_size,
+                               conn->write_state, res);
+               }
+               conn->write_state = TX_END;
+               conn->write_size = 0;
+               mark_conn_closed(conn);
+               break;
+       }
+       return res;
+}
+
+static int tx_ddigest(struct iscsi_cmnd *cmnd, int state)
+{
+       int res, rest = cmnd->conn->write_size;
+       struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
+       struct kvec iov;
+
+       iscsi_extracheck_is_wr_thread(cmnd->conn);
+
+       TRACE_DBG("Sending data digest %x (cmd %p)", cmnd->ddigest, cmnd);
+
+       iov.iov_base = (char *) (&cmnd->ddigest) + (sizeof(u32) - rest);
+       iov.iov_len = rest;
+
+       res = kernel_sendmsg(cmnd->conn->sock, &msg, &iov, 1, rest);
+       if (res > 0) {
+               cmnd->conn->write_size -= res;
+               if (!cmnd->conn->write_size)
+                       cmnd->conn->write_state = state;
+       } else
+               res = exit_tx(cmnd->conn, res);
+
+       return res;
+}
+
+static void init_tx_hdigest(struct iscsi_cmnd *cmnd)
+{
+       struct iscsi_conn *conn = cmnd->conn;
+       struct iovec *iop;
+
+       iscsi_extracheck_is_wr_thread(conn);
+
+       digest_tx_header(cmnd);
+
+       sBUG_ON(conn->write_iop_used >= sizeof(conn->write_iov)/sizeof(conn->write_iov[0]));
+       iop = &conn->write_iop[conn->write_iop_used];
+       conn->write_iop_used++;
+       iop->iov_base = &(cmnd->hdigest);
+       iop->iov_len = sizeof(u32);
+       conn->write_size += sizeof(u32);
+
+       return;
+}
+
+static int iscsi_do_send(struct iscsi_conn *conn, int state)
+{
+       int res;
+
+       iscsi_extracheck_is_wr_thread(conn);
+
+       res = write_data(conn);
+       if (res > 0) {
+               if (!conn->write_size)
+                       conn->write_state = state;
+       } else
+               res = exit_tx(conn, res);
+
+       return res;
+}
+
+/* No locks, conn is wr processing */
+int iscsi_send(struct iscsi_conn *conn)
+{
+       struct iscsi_cmnd *cmnd = conn->write_cmnd;
+       int ddigest, res = 0;
+
+       TRACE_ENTRY();
+
+       TRACE_DBG("conn %p, write_cmnd %p", conn, cmnd);
+
+       iscsi_extracheck_is_wr_thread(conn);
+
+       ddigest = conn->ddigest_type != DIGEST_NONE ? 1 : 0;
+
+       switch (conn->write_state) {
+       case TX_INIT:
+               sBUG_ON(cmnd != NULL);
+               cmnd = conn->write_cmnd = iscsi_get_send_cmnd(conn);
+               if (!cmnd)
+                       goto out;
+               cmnd_tx_start(cmnd);
+               if (!(conn->hdigest_type & DIGEST_NONE))
+                   init_tx_hdigest(cmnd);
+               conn->write_state = TX_BHS_DATA;
+       case TX_BHS_DATA:
+               res = iscsi_do_send(conn, ddigest && cmnd->pdu.datasize ? 
+                                       TX_INIT_DDIGEST : TX_END);
+               if (res <= 0 || conn->write_state != TX_INIT_DDIGEST)
+                       break;
+       case TX_INIT_DDIGEST:
+               cmnd->conn->write_size = sizeof(u32);
+               conn->write_state = TX_DDIGEST;
+       case TX_DDIGEST:
+               res = tx_ddigest(cmnd, TX_END);
+               break;
+       default:
+               PRINT_ERROR_PR("%d %d %x", res, conn->write_state,
+                       cmnd_opcode(cmnd));
+               sBUG();
+       }
+
+       if (res == 0)
+               goto out;
+
+       if (conn->write_state != TX_END)
+               goto out;
+
+       if (conn->write_size) {
+               PRINT_ERROR_PR("%d %x %u", res, cmnd_opcode(cmnd),
+                       conn->write_size);
+               sBUG();
+       }
+       cmnd_tx_end(cmnd);
+       rsp_cmnd_release(cmnd);
+       conn->write_cmnd = NULL;
+       conn->write_state = TX_INIT;
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* No locks, conn is wr processing */
+static int process_write_queue(struct iscsi_conn *conn)
+{
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       if (likely(test_write_ready(conn)))
+               res = iscsi_send(conn);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/*
+ * Called under iscsi_wr_lock and BHs disabled, but will drop it inside,
+ * then reaquire.
+ */
+static void scst_do_job_wr(void)
+{
+       TRACE_ENTRY();
+
+       /* We delete/add to tail connections to maintain fairness between them */
+
+       while(!list_empty(&iscsi_wr_list)) {
+               int rc;
+               struct iscsi_conn *conn = list_entry(iscsi_wr_list.next,
+                       typeof(*conn), wr_list_entry);
+
+               TRACE_DBG("conn %p, wr_state %x, wr_space_ready %d, "
+                       "write ready %d", conn, conn->wr_state,
+                       conn->wr_space_ready, test_write_ready(conn));
+
+               list_del(&conn->wr_list_entry);
+
+               sBUG_ON(conn->wr_state == ISCSI_CONN_WR_STATE_PROCESSING);
+
+               conn->wr_state = ISCSI_CONN_WR_STATE_PROCESSING;
+               conn->wr_space_ready = 0;
+#ifdef EXTRACHECKS
+               conn->wr_task = current;
+#endif
+               spin_unlock_bh(&iscsi_wr_lock);
+
+               rc = process_write_queue(conn);
+
+               spin_lock_bh(&iscsi_wr_lock);
+#ifdef EXTRACHECKS
+               conn->wr_task = NULL;
+#endif
+               if ((rc == -EAGAIN) && !conn->wr_space_ready) {
+                       conn->wr_state = ISCSI_CONN_WR_STATE_SPACE_WAIT;
+                       continue;
+               }
+
+               if (test_write_ready(conn)) {
+                       list_add_tail(&conn->wr_list_entry, &iscsi_wr_list);
+                       conn->wr_state = ISCSI_CONN_WR_STATE_IN_LIST;
+               } else
+                       conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
+       }
+
+       TRACE_EXIT();
+       return;
+}
+
+static inline int test_wr_list(void)
+{
+       int res = !list_empty(&iscsi_wr_list) ||
+                 unlikely(kthread_should_stop());
+       return res;
+}
+
+int istwr(void *arg)
+{
+       TRACE_ENTRY();
+
+       current->flags |= PF_NOFREEZE;
+
+       spin_lock_bh(&iscsi_wr_lock);
+       while(!kthread_should_stop()) {
+               wait_queue_t wait;
+               init_waitqueue_entry(&wait, current);
+
+               if (!test_wr_list()) {
+                       add_wait_queue_exclusive(&iscsi_wr_waitQ, &wait);
+                       for (;;) {
+                               set_current_state(TASK_INTERRUPTIBLE);
+                               if (test_wr_list())
+                                       break;
+                               spin_unlock_bh(&iscsi_wr_lock);
+                               schedule();
+                               spin_lock_bh(&iscsi_wr_lock);
+                       }
+                       set_current_state(TASK_RUNNING);
+                       remove_wait_queue(&iscsi_wr_waitQ, &wait);
+               }
+               scst_do_job_wr();
+       }
+       spin_unlock_bh(&iscsi_wr_lock);
+
+       /*
+        * If kthread_should_stop() is true, we are guaranteed to be
+        * on the module unload, so iscsi_wr_list must be empty.
+        */
+       sBUG_ON(!list_empty(&iscsi_wr_list));
+
+       TRACE_EXIT();
+       return 0;
+}
diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c
new file mode 100644 (file)
index 0000000..d66773b
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * (C) 2005 FUJITA Tomonori <tomof@acm.org>
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "iscsi.h"
+#include "digest.h"
+
+#define        CHECK_PARAM(info, iparam, word, min, max)                       \
+do {                                                                   \
+       if (!info->partial || (info->partial & 1 << key_##word))        \
+               if (iparam[key_##word] < min ||                         \
+                       iparam[key_##word] > max) {                     \
+                       PRINT_ERROR_PR("%s: %u is out of range (%u %u)",\
+                               #word, iparam[key_##word], min, max);   \
+                       iparam[key_##word] = min;                       \
+               }                                                       \
+} while (0)
+
+#define        SET_PARAM(param, info, iparam, word)                            \
+({                                                                     \
+       int changed = 0;                                                \
+       if (!info->partial || (info->partial & 1 << key_##word)) {      \
+               if (param->word != iparam[key_##word])                  \
+                       changed = 1;                                    \
+               param->word = iparam[key_##word];                       \
+       }                                                               \
+       changed;                                                        \
+})
+
+#define        GET_PARAM(param, info, iparam, word)                            \
+do {                                                                   \
+       iparam[key_##word] = param->word;                               \
+} while (0)
+
+static const char *get_bool_name(int val)
+{
+       if (val)
+               return "Yes";
+       else
+               return "No";
+}
+
+static const char *get_digest_name(int val)
+{
+       if (val == DIGEST_NONE)
+               return "None";
+       else if (val == DIGEST_CRC32C)
+               return "CRC32C";
+       else
+               return "Unknown";
+}
+
+static void log_params(struct iscsi_sess_param *param)
+{
+       PRINT_INFO_PR("Negotiated parameters: InitialR2T %s, ImmediateData %s, "
+               "MaxConnections %d, MaxRecvDataSegmentLength %d, "
+               "MaxXmitDataSegmentLength %d, ", get_bool_name(param->initial_r2t),
+               get_bool_name(param->immediate_data), param->max_connections,
+               param->max_recv_data_length, param->max_xmit_data_length);
+       PRINT_INFO_PR("    MaxBurstLength %d, FirstBurstLength %d, "
+               "DefaultTime2Wait %d, DefaultTime2Retain %d, ",
+               param->max_burst_length, param->first_burst_length,
+               param->default_wait_time, param->default_retain_time);
+       PRINT_INFO_PR("    MaxOutstandingR2T %d, DataPDUInOrder %s, "
+               "DataSequenceInOrder %s, ErrorRecoveryLevel %d, ",
+               param->max_outstanding_r2t, get_bool_name(param->data_pdu_inorder),
+               get_bool_name(param->data_sequence_inorder),
+               param->error_recovery_level);
+       PRINT_INFO_PR("    HeaderDigest %s, DataDigest %s, OFMarker %s, "
+               "IFMarker %s, OFMarkInt %d, IFMarkInt %d",
+               get_digest_name(param->header_digest),
+               get_digest_name(param->data_digest),
+               get_bool_name(param->ofmarker), get_bool_name(param->ifmarker),
+               param->ofmarkint, param->ifmarkint);
+}
+
+/* target_mutex supposed to be locked */
+static void sess_param_check(struct iscsi_param_info *info)
+{
+       u32 *iparam = info->session_param;
+
+       CHECK_PARAM(info, iparam, max_connections, 1, 1);
+       CHECK_PARAM(info, iparam, max_recv_data_length, 512,
+                   (u32) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
+       CHECK_PARAM(info, iparam, max_xmit_data_length, 512,
+                   (u32) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
+       CHECK_PARAM(info, iparam, error_recovery_level, 0, 0);
+       CHECK_PARAM(info, iparam, data_pdu_inorder, 0, 1);
+       CHECK_PARAM(info, iparam, data_sequence_inorder, 0, 1);
+
+       digest_alg_available(&iparam[key_header_digest]);
+       digest_alg_available(&iparam[key_data_digest]);
+
+       CHECK_PARAM(info, iparam, ofmarker, 0, 0);
+       CHECK_PARAM(info, iparam, ifmarker, 0, 0);
+}
+
+/* target_mutex supposed to be locked */
+static void sess_param_set(struct iscsi_sess_param *param, struct iscsi_param_info *info)
+{
+       u32 *iparam = info->session_param;
+
+       SET_PARAM(param, info, iparam, initial_r2t);
+       SET_PARAM(param, info, iparam, immediate_data);
+       SET_PARAM(param, info, iparam, max_connections);
+       SET_PARAM(param, info, iparam, max_recv_data_length);
+       SET_PARAM(param, info, iparam, max_xmit_data_length);
+       SET_PARAM(param, info, iparam, max_burst_length);
+       SET_PARAM(param, info, iparam, first_burst_length);
+       SET_PARAM(param, info, iparam, default_wait_time);
+       SET_PARAM(param, info, iparam, default_retain_time);
+       SET_PARAM(param, info, iparam, max_outstanding_r2t);
+       SET_PARAM(param, info, iparam, data_pdu_inorder);
+       SET_PARAM(param, info, iparam, data_sequence_inorder);
+       SET_PARAM(param, info, iparam, error_recovery_level);
+       SET_PARAM(param, info, iparam, header_digest);
+       SET_PARAM(param, info, iparam, data_digest);
+       SET_PARAM(param, info, iparam, ofmarker);
+       SET_PARAM(param, info, iparam, ifmarker);
+       SET_PARAM(param, info, iparam, ofmarkint);
+       SET_PARAM(param, info, iparam, ifmarkint);
+}
+
+static void sess_param_get(struct iscsi_sess_param *param, struct iscsi_param_info *info)
+{
+       u32 *iparam = info->session_param;
+
+       GET_PARAM(param, info, iparam, initial_r2t);
+       GET_PARAM(param, info, iparam, immediate_data);
+       GET_PARAM(param, info, iparam, max_connections);
+       GET_PARAM(param, info, iparam, max_recv_data_length);
+       GET_PARAM(param, info, iparam, max_xmit_data_length);
+       GET_PARAM(param, info, iparam, max_burst_length);
+       GET_PARAM(param, info, iparam, first_burst_length);
+       GET_PARAM(param, info, iparam, default_wait_time);
+       GET_PARAM(param, info, iparam, default_retain_time);
+       GET_PARAM(param, info, iparam, max_outstanding_r2t);
+       GET_PARAM(param, info, iparam, data_pdu_inorder);
+       GET_PARAM(param, info, iparam, data_sequence_inorder);
+       GET_PARAM(param, info, iparam, error_recovery_level);
+       GET_PARAM(param, info, iparam, header_digest);
+       GET_PARAM(param, info, iparam, data_digest);
+       GET_PARAM(param, info, iparam, ofmarker);
+       GET_PARAM(param, info, iparam, ifmarker);
+       GET_PARAM(param, info, iparam, ofmarkint);
+       GET_PARAM(param, info, iparam, ifmarkint);
+}
+
+/* target_mutex supposed to be locked */
+static void trgt_param_check(struct iscsi_param_info *info)
+{
+       u32 *iparam = info->target_param;
+
+       CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS, MAX_NR_QUEUED_CMNDS);
+}
+
+/* target_mutex supposed to be locked */
+static void trgt_param_set(struct iscsi_target *target, struct iscsi_param_info *info)
+{
+       struct iscsi_trgt_param *param = &target->trgt_param;
+       u32 *iparam = info->target_param;
+
+       SET_PARAM(param, info, iparam, queued_cmnds);
+}
+
+/* target_mutex supposed to be locked */
+static void trgt_param_get(struct iscsi_trgt_param *param, struct iscsi_param_info *info)
+{
+       u32 *iparam = info->target_param;
+
+       GET_PARAM(param, info, iparam, queued_cmnds);
+}
+
+/* target_mutex supposed to be locked */
+static int trgt_param(struct iscsi_target *target, struct iscsi_param_info *info, int set)
+{
+       if (set) {
+               trgt_param_check(info);
+               trgt_param_set(target, info);
+       } else
+               trgt_param_get(&target->trgt_param, info);
+
+       return 0;
+}
+
+/* target_mutex supposed to be locked */
+static int sess_param(struct iscsi_target *target, struct iscsi_param_info *info, int set)
+{
+       struct iscsi_session *session = NULL;
+       struct iscsi_sess_param *param;
+       int err = -ENOENT;
+
+       if (set)
+               sess_param_check(info);
+
+       if (info->sid) {
+               if (!(session = session_lookup(target, info->sid)))
+                       goto out;
+               if (set && !list_empty(&session->conn_list)) {
+                       err = -EBUSY;
+                       goto out;
+               }
+               param = &session->sess_param;
+       } else
+               param = &target->trgt_sess_param;
+
+       if (set) {
+               sess_param_set(param, info);
+               if (session != NULL)
+                       log_params(param);
+       } else
+               sess_param_get(param, info);
+
+       err = 0;
+out:
+       return err;
+}
+
+/* target_mutex supposed to be locked */
+int iscsi_param_set(struct iscsi_target *target, struct iscsi_param_info *info, int set)
+{
+       int err;
+
+       if (info->param_type == key_session)
+               err = sess_param(target, info, set);
+       else if (info->param_type == key_target)
+               err = trgt_param(target, info, set);
+       else
+               err = -EINVAL;
+
+       return err;
+}
diff --git a/iscsi-scst/kernel/patches/put_page_callback-2.6.16.patch b/iscsi-scst/kernel/patches/put_page_callback-2.6.16.patch
new file mode 100644 (file)
index 0000000..785756e
--- /dev/null
@@ -0,0 +1,245 @@
+diff -upr linux-2.6.16.29/include/linux/mm.h linux-2.6.16.29/include/linux/mm.h
+--- linux-2.6.16.29/include/linux/mm.h 2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/include/linux/mm.h 2007-08-07 19:54:27.000000000 +0400
+@@ -263,6 +263,15 @@ struct page {
+       void *virtual;                  /* Kernel virtual address (NULL if
+                                          not kmapped, ie. highmem) */
+ #endif /* WANT_PAGE_VIRTUAL */
++      /*
++       * Used to implement support for notification on zero-copy TCP transfer
++       * completeion. Not good to have this field here, it's better to have
++       * it in struct sk_buff, but it would make the code much more
++       * complicated and fragile, if maintained as a separate patch, since all
++       * skb then would have to contain only pages with the same value in this
++       * field.
++       */
++       void *net_priv;
+ };
+ #define page_private(page)            ((page)->private)
+diff -upr linux-2.6.16.29/include/linux/net.h linux-2.6.16.29/include/linux/net.h
+--- linux-2.6.16.29/include/linux/net.h        2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/include/linux/net.h        2007-08-07 19:55:27.000000000 +0400
+@@ -22,6 +22,7 @@
+ #include <linux/wait.h>
+ #include <linux/stringify.h>
+ #include <asm/socket.h>
++#include <linux/mm.h>
+ struct poll_table_struct;
+ struct inode;
+@@ -294,5 +295,30 @@ extern int net_msg_cost;
+ extern int net_msg_burst;
+ #endif
++/* Support for notification on zero-copy TCP transfer completeion */
++#define NET_PAGE_CALLBACKS_DEFINED
++typedef void (*net_get_page_callback_t)(struct page *page);
++typedef void (*net_put_page_callback_t)(struct page *page);
++
++extern net_get_page_callback_t net_get_page_callback;
++extern net_put_page_callback_t net_put_page_callback;
++
++extern int net_set_get_put_page_callbacks(
++      net_get_page_callback_t get_callback,
++      net_put_page_callback_t put_callback);
++
++static inline void net_get_page(struct page *page)
++{
++      if (page->net_priv != 0)
++              net_get_page_callback(page);
++      get_page(page);
++}
++static inline void net_put_page(struct page *page)
++{
++      if (page->net_priv != 0)
++              net_put_page_callback(page);
++      put_page(page);
++}
++
+ #endif /* __KERNEL__ */
+ #endif        /* _LINUX_NET_H */
+diff -upr linux-2.6.16.29/net/core/skbuff.c linux-2.6.16.29/net/core/skbuff.c
+--- linux-2.6.16.29/net/core/skbuff.c  2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/net/core/skbuff.c  2007-08-07 19:55:51.000000000 +0400
+@@ -271,7 +271,7 @@ void skb_release_data(struct sk_buff *sk
+               if (skb_shinfo(skb)->nr_frags) {
+                       int i;
+                       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+-                              put_page(skb_shinfo(skb)->frags[i].page);
++                              net_put_page(skb_shinfo(skb)->frags[i].page);
+               }
+               if (skb_shinfo(skb)->frag_list)
+@@ -588,7 +588,7 @@ struct sk_buff *pskb_copy(struct sk_buff
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+-                      get_page(skb_shinfo(n)->frags[i].page);
++                      net_get_page(skb_shinfo(n)->frags[i].page);
+               }
+               skb_shinfo(n)->nr_frags = i;
+       }
+@@ -642,7 +642,7 @@ int pskb_expand_head(struct sk_buff *skb
+       memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+-              get_page(skb_shinfo(skb)->frags[i].page);
++              net_get_page(skb_shinfo(skb)->frags[i].page);
+       if (skb_shinfo(skb)->frag_list)
+               skb_clone_fraglist(skb);
+@@ -794,7 +794,7 @@ int ___pskb_trim(struct sk_buff *skb, un
+                                       return -ENOMEM;
+                       }
+                       if (len <= offset) {
+-                              put_page(skb_shinfo(skb)->frags[i].page);
++                              net_put_page(skb_shinfo(skb)->frags[i].page);
+                               skb_shinfo(skb)->nr_frags--;
+                       } else {
+                               skb_shinfo(skb)->frags[i].size = len - offset;
+@@ -940,7 +940,7 @@ pull_pages:
+       k = 0;
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               if (skb_shinfo(skb)->frags[i].size <= eat) {
+-                      put_page(skb_shinfo(skb)->frags[i].page);
++                      net_put_page(skb_shinfo(skb)->frags[i].page);
+                       eat -= skb_shinfo(skb)->frags[i].size;
+               } else {
+                       skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+@@ -1522,7 +1522,7 @@ static inline void skb_split_no_header(s
+                                *    where splitting is expensive.
+                                * 2. Split is accurately. We make this.
+                                */
+-                              get_page(skb_shinfo(skb)->frags[i].page);
++                              net_get_page(skb_shinfo(skb)->frags[i].page);
+                               skb_shinfo(skb1)->frags[0].page_offset += len - pos;
+                               skb_shinfo(skb1)->frags[0].size -= len - pos;
+                               skb_shinfo(skb)->frags[i].size  = len - pos;
+diff -upr linux-2.6.16.29/net/core/utils.c linux-2.6.16.29/net/core/utils.c
+--- linux-2.6.16.29/net/core/utils.c   2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/net/core/utils.c   2007-08-07 19:54:27.000000000 +0400
+@@ -24,11 +24,15 @@
+ #include <linux/random.h>
+ #include <linux/percpu.h>
+ #include <linux/init.h>
++#include <linux/skbuff.h>
+ #include <asm/byteorder.h>
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
++net_get_page_callback_t net_get_page_callback __read_mostly;
++net_put_page_callback_t net_put_page_callback __read_mostly;
++
+ /*
+   This is a maximally equidistributed combined Tausworthe generator
+   based on code from GNU Scientific Library 1.5 (30 Jun 2004)
+@@ -190,3 +194,29 @@ __be32 in_aton(const char *str)
+ }
+ EXPORT_SYMBOL(in_aton);
++
++int net_set_get_put_page_callbacks(
++      net_get_page_callback_t get_callback,
++      net_put_page_callback_t put_callback)
++{
++      int res = 0;
++
++      if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
++          (net_get_page_callback != get_callback)) {
++              res = -EBUSY;
++              goto out;
++      }
++
++      if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
++          (net_put_page_callback != put_callback)) {
++              res = -EBUSY;
++              goto out;
++      }
++
++      net_get_page_callback = get_callback;
++      net_put_page_callback = put_callback;
++
++out:
++      return res;
++}
++EXPORT_SYMBOL(net_set_get_put_page_callbacks);
+diff -upr linux-2.6.16.29/net/ipv4/ip_output.c linux-2.6.16.29/net/ipv4/ip_output.c
+--- linux-2.6.16.29/net/ipv4/ip_output.c       2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/net/ipv4/ip_output.c       2007-08-07 19:54:27.000000000 +0400
+@@ -997,7 +997,7 @@ alloc_new_skb:
+                                               err = -EMSGSIZE;
+                                               goto error;
+                                       }
+-                                      get_page(page);
++                                      net_get_page(page);
+                                       skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
+                                       frag = &skb_shinfo(skb)->frags[i];
+                               }
+@@ -1155,7 +1155,7 @@ ssize_t  ip_append_page(struct sock *sk, 
+               if (skb_can_coalesce(skb, i, page, offset)) {
+                       skb_shinfo(skb)->frags[i-1].size += len;
+               } else if (i < MAX_SKB_FRAGS) {
+-                      get_page(page);
++                      net_get_page(page);
+                       skb_fill_page_desc(skb, i, page, offset, len);
+               } else {
+                       err = -EMSGSIZE;
+diff -upr linux-2.6.16.29/net/ipv4/tcp.c linux-2.6.16.29/net/ipv4/tcp.c
+--- linux-2.6.16.29/net/ipv4/tcp.c     2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/net/ipv4/tcp.c     2007-08-07 19:54:27.000000000 +0400
+@@ -558,7 +558,7 @@ new_segment:
+               if (can_coalesce) {
+                       skb_shinfo(skb)->frags[i - 1].size += copy;
+               } else {
+-                      get_page(page);
++                      net_get_page(page);
+                       skb_fill_page_desc(skb, i, page, offset, copy);
+               }
+@@ -767,7 +767,7 @@ new_segment:
+                                       goto new_segment;
+                               } else if (page) {
+                                       if (off == PAGE_SIZE) {
+-                                              put_page(page);
++                                              net_put_page(page);
+                                               TCP_PAGE(sk) = page = NULL;
+                                               off = 0;
+                                       }
+@@ -808,9 +808,9 @@ new_segment:
+                               } else {
+                                       skb_fill_page_desc(skb, i, page, off, copy);
+                                       if (TCP_PAGE(sk)) {
+-                                              get_page(page);
++                                              net_get_page(page);
+                                       } else if (off + copy < PAGE_SIZE) {
+-                                              get_page(page);
++                                              net_get_page(page);
+                                               TCP_PAGE(sk) = page;
+                                       }
+                               }
+diff -upr linux-2.6.16.29/net/ipv4/tcp_output.c linux-2.6.16.29/net/ipv4/tcp_output.c
+--- linux-2.6.16.29/net/ipv4/tcp_output.c      2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/net/ipv4/tcp_output.c      2007-08-07 19:54:27.000000000 +0400
+@@ -633,7 +633,7 @@ static unsigned char *__pskb_trim_head(s
+       k = 0;
+       for (i=0; i<skb_shinfo(skb)->nr_frags; i++) {
+               if (skb_shinfo(skb)->frags[i].size <= eat) {
+-                      put_page(skb_shinfo(skb)->frags[i].page);
++                      net_put_page(skb_shinfo(skb)->frags[i].page);
+                       eat -= skb_shinfo(skb)->frags[i].size;
+               } else {
+                       skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+diff -upr linux-2.6.16.29/net/ipv6/ip6_output.c linux-2.6.16.29/net/ipv6/ip6_output.c
+--- linux-2.6.16.29/net/ipv6/ip6_output.c      2006-09-12 22:02:10.000000000 +0400
++++ linux-2.6.16.29/net/ipv6/ip6_output.c      2007-08-07 19:54:27.000000000 +0400
+@@ -1100,7 +1100,7 @@ alloc_new_skb:
+                                               err = -EMSGSIZE;
+                                               goto error;
+                                       }
+-                                      get_page(page);
++                                      net_get_page(page);
+                                       skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
+                                       frag = &skb_shinfo(skb)->frags[i];
+                               }
diff --git a/iscsi-scst/kernel/patches/put_page_callback-2.6.18.patch b/iscsi-scst/kernel/patches/put_page_callback-2.6.18.patch
new file mode 100644 (file)
index 0000000..563379c
--- /dev/null
@@ -0,0 +1,254 @@
+diff -upr linux-2.6.18.1/include/linux/mm.h linux-2.6.18.1/include/linux/mm.h
+--- linux-2.6.18.1/include/linux/mm.h  2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1/include/linux/mm.h  2007-08-07 19:35:51.000000000 +0400
+@@ -267,6 +267,15 @@ struct page {
+       void *virtual;                  /* Kernel virtual address (NULL if
+                                          not kmapped, ie. highmem) */
+ #endif /* WANT_PAGE_VIRTUAL */
++      /*
++       * Used to implement support for notification on zero-copy TCP transfer
++       * completeion. Not good to have this field here, it's better to have
++       * it in struct sk_buff, but it would make the code much more
++       * complicated and fragile, if maintained as a separate patch, since all
++       * skb then would have to contain only pages with the same value in this
++       * field.
++       */
++       void *net_priv;
+ };
+ #define page_private(page)            ((page)->private)
+diff -upr linux-2.6.18.1/include/linux/net.h linux-2.6.18.1/include/linux/net.h
+--- linux-2.6.18.1/include/linux/net.h 2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1/include/linux/net.h 2007-08-07 19:35:51.000000000 +0400
+@@ -19,6 +19,7 @@
+ #define _LINUX_NET_H
+ #include <linux/wait.h>
++#include <linux/mm.h>
+ #include <asm/socket.h>
+ struct poll_table_struct;
+@@ -304,5 +305,30 @@ extern int net_msg_cost;
+ extern int net_msg_burst;
+ #endif
++/* Support for notification on zero-copy TCP transfer completeion */
++#define NET_PAGE_CALLBACKS_DEFINED
++typedef void (*net_get_page_callback_t)(struct page *page);
++typedef void (*net_put_page_callback_t)(struct page *page);
++
++extern net_get_page_callback_t net_get_page_callback;
++extern net_put_page_callback_t net_put_page_callback;
++
++extern int net_set_get_put_page_callbacks(
++      net_get_page_callback_t get_callback,
++      net_put_page_callback_t put_callback);
++
++static inline void net_get_page(struct page *page)
++{
++      if (page->net_priv != 0)
++              net_get_page_callback(page);
++      get_page(page);
++}
++static inline void net_put_page(struct page *page)
++{
++      if (page->net_priv != 0)
++              net_put_page_callback(page);
++      put_page(page);
++}
++
+ #endif /* __KERNEL__ */
+ #endif        /* _LINUX_NET_H */
+diff -upr linux-2.6.18.1/net/core/skbuff.c linux-2.6.18.1/net/core/skbuff.c
+--- linux-2.6.18.1/net/core/skbuff.c   2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1/net/core/skbuff.c   2007-08-07 19:35:51.000000000 +0400
+@@ -309,7 +309,7 @@ static void skb_release_data(struct sk_b
+               if (skb_shinfo(skb)->nr_frags) {
+                       int i;
+                       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+-                              put_page(skb_shinfo(skb)->frags[i].page);
++                              net_put_page(skb_shinfo(skb)->frags[i].page);
+               }
+               if (skb_shinfo(skb)->frag_list)
+@@ -646,7 +646,7 @@ struct sk_buff *pskb_copy(struct sk_buff
+               for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+                       skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
+-                      get_page(skb_shinfo(n)->frags[i].page);
++                      net_get_page(skb_shinfo(n)->frags[i].page);
+               }
+               skb_shinfo(n)->nr_frags = i;
+       }
+@@ -700,7 +700,7 @@ int pskb_expand_head(struct sk_buff *skb
+       memcpy(data + size, skb->end, sizeof(struct skb_shared_info));
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+-              get_page(skb_shinfo(skb)->frags[i].page);
++              net_get_page(skb_shinfo(skb)->frags[i].page);
+       if (skb_shinfo(skb)->frag_list)
+               skb_clone_fraglist(skb);
+@@ -882,7 +882,7 @@ drop_pages:
+               skb_shinfo(skb)->nr_frags = i;
+               for (; i < nfrags; i++)
+-                      put_page(skb_shinfo(skb)->frags[i].page);
++                      net_put_page(skb_shinfo(skb)->frags[i].page);
+               if (skb_shinfo(skb)->frag_list)
+                       skb_drop_fraglist(skb);
+@@ -1051,7 +1051,7 @@ pull_pages:
+       k = 0;
+       for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+               if (skb_shinfo(skb)->frags[i].size <= eat) {
+-                      put_page(skb_shinfo(skb)->frags[i].page);
++                      net_put_page(skb_shinfo(skb)->frags[i].page);
+                       eat -= skb_shinfo(skb)->frags[i].size;
+               } else {
+                       skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+@@ -1633,7 +1633,7 @@ static inline void skb_split_no_header(s
+                                *    where splitting is expensive.
+                                * 2. Split is accurately. We make this.
+                                */
+-                              get_page(skb_shinfo(skb)->frags[i].page);
++                              net_get_page(skb_shinfo(skb)->frags[i].page);
+                               skb_shinfo(skb1)->frags[0].page_offset += len - pos;
+                               skb_shinfo(skb1)->frags[0].size -= len - pos;
+                               skb_shinfo(skb)->frags[i].size  = len - pos;
+@@ -2002,7 +2002,7 @@ struct sk_buff *skb_segment(struct sk_bu
+                       BUG_ON(i >= nfrags);
+                       *frag = skb_shinfo(skb)->frags[i];
+-                      get_page(frag->page);
++                      net_get_page(frag->page);
+                       size = frag->size;
+                       if (pos < offset) {
+diff -upr linux-2.6.18.1/net/core/utils.c linux-2.6.18.1/net/core/utils.c
+--- linux-2.6.18.1/net/core/utils.c    2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1/net/core/utils.c    2007-08-07 19:36:59.000000000 +0400
+@@ -24,11 +24,15 @@
+ #include <linux/random.h>
+ #include <linux/percpu.h>
+ #include <linux/init.h>
++#include <linux/skbuff.h>
+ #include <asm/byteorder.h>
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
++net_get_page_callback_t net_get_page_callback __read_mostly;
++net_put_page_callback_t net_put_page_callback __read_mostly;
++
+ /*
+   This is a maximally equidistributed combined Tausworthe generator
+   based on code from GNU Scientific Library 1.5 (30 Jun 2004)
+@@ -191,3 +195,29 @@ __be32 in_aton(const char *str)
+ }
+ EXPORT_SYMBOL(in_aton);
++
++int net_set_get_put_page_callbacks(
++      net_get_page_callback_t get_callback,
++      net_put_page_callback_t put_callback)
++{
++      int res = 0;
++
++      if ((net_get_page_callback != NULL) && (get_callback != NULL) &&
++          (net_get_page_callback != get_callback)) {
++              res = -EBUSY;
++              goto out;
++      }
++
++      if ((net_put_page_callback != NULL) && (put_callback != NULL) &&
++          (net_put_page_callback != put_callback)) {
++              res = -EBUSY;
++              goto out;
++      }
++
++      net_get_page_callback = get_callback;
++      net_put_page_callback = put_callback;
++
++out:
++      return res;
++}
++EXPORT_SYMBOL(net_set_get_put_page_callbacks);
+diff -upr linux-2.6.18.1/net/ipv4/ip_output.c linux-2.6.18.1/net/ipv4/ip_output.c
+--- linux-2.6.18.1/net/ipv4/ip_output.c        2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1/net/ipv4/ip_output.c        2007-08-07 19:37:24.000000000 +0400
+@@ -999,7 +999,7 @@ alloc_new_skb:
+                                               err = -EMSGSIZE;
+                                               goto error;
+                                       }
+-                                      get_page(page);
++                                      net_get_page(page);
+                                       skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0);
+                                       frag = &skb_shinfo(skb)->frags[i];
+                               }
+@@ -1159,7 +1159,7 @@ ssize_t  ip_append_page(struct sock *sk, 
+               if (skb_can_coalesce(skb, i, page, offset)) {
+                       skb_shinfo(skb)->frags[i-1].size += len;
+               } else if (i < MAX_SKB_FRAGS) {
+-                      get_page(page);
++                      net_get_page(page);
+                       skb_fill_page_desc(skb, i, page, offset, len);
+               } else {
+                       err = -EMSGSIZE;
+diff -upr linux-2.6.18.1/net/ipv4/tcp.c linux-2.6.18.1/net/ipv4/tcp.c
+--- linux-2.6.18.1/net/ipv4/tcp.c      2006-09-20 07:42:06.000000000 +0400
++++ linux-2.6.18.1/net/ipv4/tcp.c      2007-08-07 19:35:51.000000000 +0400
+@@ -559,7 +559,7 @@ new_segment:
+               if (can_coalesce) {
+                       skb_shinfo(skb)->frags[i - 1].size += copy;
+               } else {
+-                      get_page(page);
++                      net_get_page(page);
+                       skb_fill_page_desc(skb, i, page, offset, copy);
+               }
+@@ -762,7 +762,7 @@ new_segment:
+                                       goto new_segment;
+                               } else if (page) {
+                                       if (off == PAGE_SIZE) {
+-                                              put_page(page);
++                                              net_put_page(page);
+                                               TCP_PAGE(sk) = page = NULL;
+                                               off = 0;
+                                       }
+@@ -803,9 +803,9 @@ new_segment:
+                               } else {
+                                       skb_fill_page_desc(skb, i, page, off, copy);
+                                       if (TCP_PAGE(sk)) {
+-                                              get_page(page);
++                        &nb