From 56aa034300681ae47bc6550c2d31fe260a40fd801fa9060eb7798f261caa0cc3 Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Fri, 25 Jul 2008 14:58:50 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/open-iscsi?expand=0&rev=6 --- mkinitrd-boot.sh | 120 + mkinitrd-setup.sh | 73 + mkinitrd-stop.sh | 21 + open-iscsi-2.0-865.tar.bz2 | 3 - open-iscsi-2.0-870-rc1.tar.bz2 | 3 + open-iscsi-866.diff | 12 - open-iscsi-backport-fwparam_ibft | 317 - open-iscsi-format-luns | 29 - open-iscsi-fwparam-scan-in-blocks | 69 - open-iscsi-git-update | 28294 ---------------- open-iscsi-ibft-fill-initiator-values | 110 - open-iscsi-ibft-print-ifspec | 105 - open-iscsi-ibft-scan-memory-area | 48 - open-iscsi-print-ibft-error-to-stderr | 24 - open-iscsi-start-target-before-initiator | 13 + ...-suse-build => open-iscsi-suse-build-fixes | 25 +- open-iscsi-teardown-device-stack | 205 - open-iscsi-update-init-script | 72 + open-iscsi-update-nodes | 13 - open-iscsi.changes | 9 + open-iscsi.spec | 64 +- 21 files changed, 351 insertions(+), 29278 deletions(-) create mode 100644 mkinitrd-boot.sh create mode 100644 mkinitrd-setup.sh create mode 100644 mkinitrd-stop.sh delete mode 100644 open-iscsi-2.0-865.tar.bz2 create mode 100644 open-iscsi-2.0-870-rc1.tar.bz2 delete mode 100644 open-iscsi-866.diff delete mode 100644 open-iscsi-backport-fwparam_ibft delete mode 100644 open-iscsi-format-luns delete mode 100644 open-iscsi-fwparam-scan-in-blocks delete mode 100644 open-iscsi-git-update delete mode 100644 open-iscsi-ibft-fill-initiator-values delete mode 100644 open-iscsi-ibft-print-ifspec delete mode 100644 open-iscsi-ibft-scan-memory-area delete mode 100644 open-iscsi-print-ibft-error-to-stderr create mode 100644 open-iscsi-start-target-before-initiator rename open-iscsi-fix-suse-build => open-iscsi-suse-build-fixes (80%) delete mode 100644 open-iscsi-teardown-device-stack create mode 100644 open-iscsi-update-init-script delete mode 100644 open-iscsi-update-nodes diff --git a/mkinitrd-boot.sh b/mkinitrd-boot.sh new file mode 100644 index 0000000..8d97db6 --- /dev/null +++ b/mkinitrd-boot.sh @@ -0,0 +1,120 @@ +#!/bin/bash +#%stage: device +#%depends: network +#%programs: /sbin/iscsid /sbin/iscsiadm /sbin/fwparam_ibft usleep +#%modules: iscsi_tcp crc32c scsi_transport_iscsi +#%if: "$root_iscsi" -o "$TargetAddress" +# +##### iSCSI initialization +## +## This script initializes iSCSI (SCSI over IP). +## To be able to use this script, the network has to be setup. +## When everything worked as expected, the iSCSI devices will show +## up as real SCSI devices. +## +## Command line parameters +## ----------------------- +## +## TargetAddress the address of the iscsi server +## TargetPort the port of the iscsi server (defaults to 3260) +## TargetName the iscsi target name (connect to all if empty) +## iSCSI_ignoreNodes if set all stored nodes will be ignored (only +## iBFT and commandline nodes get parsed) +## + +### iSCSI_warning_InitiatorName +# shows a warning about iSCSI InitiatorName differences +# Origin should be something like "commandline" or "iBFT" +iSCSI_warning_InitiatorName() { + echo "iSCSI: WARNING" + echo "iSCSI: =======================" + echo "iSCSI: " + echo "iSCSI: InitiatorName given on $2 and internally stored Initiator are different." + echo "iSCSI: New: $tmp_InitiatorName" + echo "iSCSI: Stored: $InitiatorName" + echo "iSCSI: " + echo "iSCSI: using the $2 version" +} + +if [ "$iSCSI_ignoreNodes" ]; then + # make us forget we have to initialize stored nodes + echo "iSCSI: removing node information..." + iscsi_sessions= + rm -rf /etc/iscsi/nodes + mkdir /etc/iscsi/nodes +fi + +# get the command line InitiatorName +tmp_InitiatorName="$(get_param InitiatorName)" +# reads the InitiatorName variable +. /etc/iscsi/initiatorname.iscsi + +# Check of iBFT settings +if [ -x /sbin/fwparam_ibft ] ; then + eval $(/sbin/fwparam_ibft -b 2> /dev/null ) + # only use the iBFT InitiatorName if the commandline argument is not "default" + if [ "$iSCSI_INITIATOR_NAME" -a "$tmp_InitiatorName" != "default" ] ; then + iSCSI_warning_InitiatorName "$iSCSI_INITIATOR_NAME" "iBFT" + InitiatorName=$iSCSI_INITIATOR_NAME + fi + + TargetNameiBFT=$iSCSI_TARGET_NAME + TargetAddressiBFT=$iSCSI_TARGET_IPADDR + TargetPortiBFT=$iSCSI_TARGET_PORT +fi + +if [ "$tmp_InitiatorName" != "$InitiatorName" -a "$tmp_InitiatorName" != "default" -a "$tmp_InitiatorName" ]; then + iSCSI_warning_InitiatorName "$tmp_InitiatorName" "cmdline" + InitiatorName=$tmp_InitiatorName +fi + +# store the detected InitiatorName permanently +echo "InitiatorName=$InitiatorName" > /etc/iscsi/initiatorname.iscsi + +unset iSCSI_warning_InitiatorName + +# ... hier wuerde viel s390-init zeug kommen + +iscsi_mark_root_nodes() +{ + local iscsi_tgts + + if [ -z "$iscsitarget" ] ; then + iscsi_tgts=$(/sbin/iscsiadm -m node | sed -n "s/.*$iscsiserver:$iscsiport,.* \(iqn.*\)/\1/p") + else + iscsi_tgts="$iscsitarget" + fi + + for tgt in $iscsi_tgts ; do + /sbin/iscsiadm -m node -p $iscsiserver:$iscsiport -T $tgt -o update -n node.conn[0].startup -v automatic + done +} + +load_modules + +echo "Starting iSCSI daemon" +/sbin/iscsid +usleep 5000000 + +# loop through all stored iscsi sessions, the command line and iBFT settings +for session in iscsi_sessions "" iBFT; do + # get the current config + iscsiserver=$(eval echo \$TargetAddress$session) + iscsiport=$(eval echo \$TargetPort$session) + iscsitarget=$(eval echo \$TargetName$session) + + # try to detect and connect to the iscsi server + if [ "$iscsiserver" -a "$iscsiport" ] ; then + echo -n "Starting discovery on ${iscsiserver},${iscsiport}: " + if /sbin/iscsiadm -m discovery -t st -p ${iscsiserver}:${iscsiport} 2> /dev/null ; then + echo "ok." + else + echo "failed." + fi + iscsi_mark_root_nodes + fi +done + +/sbin/iscsiadm -m node -L automatic + +unset iscsi_mark_root_nodes diff --git a/mkinitrd-setup.sh b/mkinitrd-setup.sh new file mode 100644 index 0000000..7ffff67 --- /dev/null +++ b/mkinitrd-setup.sh @@ -0,0 +1,73 @@ +#!/bin/bash +# +#%stage: device +# +check_iscsi_root() { + local devname=$1 + local sysfs_path + + sysfs_path=$(/sbin/udevadm info -q path -n $rootdev 2> /dev/null) + if [ -z "$sysfs_path" ] || [ ! -d /sys$sysfs_path ] ; then + return; + fi + + pushd /sys$sysfs_path > /dev/null + if [ ! -d device ] ; then + cd .. + fi + + if [ ! -d device ] ; then + # no device link; return + popd > /dev/null + return; + fi + + cd -P device + cd ../.. + + if [ -d connection* ]; then + cd -P connection* + cid=${PWD#*connection} + sid=${cid%%:*} + if [ -d /sys/class/iscsi_session/session$sid ]; then + cd -P /sys/class/iscsi_session/session$sid + echo $(basename $PWD) + fi + fi + popd > /dev/null +} + +for bd in $blockdev; do + update_blockdev $bd + sid=$(check_iscsi_root $bd) + if [ "$sid" ]; then + root_iscsi=1 + iscsi_sessions="$iscsi_sessions ${sid#session}" + fi +done + +save_var root_iscsi +save_var iscsi_sessions + +if [ "${root_iscsi}" ]; then + for session in $iscsi_sessions; do + eval TargetName${session}=$(cat /sys/class/iscsi_session/session${session}/targetname) + eval TargetAddress${session}=$(cat /sys/class/iscsi_connection/connection${session}:0/address) + eval TargetPort${session}=$(cat /sys/class/iscsi_connection/connection${session}:0/port) + + save_var TargetName${session} + save_var TargetAddress${session} + save_var TargetPort${session} + + # copy the iscsi configuration + cp -rp /etc/iscsi etc/ + mkdir -p $tmp_mnt/var/run + mkdir -p $tmp_mnt/var/lock/iscsi + if [ -z "$interface" ] ; then + interface="default" + fi + done +fi + +save_var TargetPort 3260 # in case the port was not defined via command line we assign a default port + diff --git a/mkinitrd-stop.sh b/mkinitrd-stop.sh new file mode 100644 index 0000000..2b1c895 --- /dev/null +++ b/mkinitrd-stop.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +#%stage: setup +#%provides: killprogs +# +#%if: "$iscsi_root" +#%dontshow +# +##### kill iscsi +## +## Because we will run and use the iSCSI daemon from the new root +## the old one has to be killed. During that time no iSCSI +## exceptions should occur! +## +## Command line parameters +## ----------------------- +## + +# kill iscsid, will be restarted from the real root +iscsi_pid=$(pidof iscsid) +[ "$iscsi_pid" ] && kill -KILL $iscsi_pid diff --git a/open-iscsi-2.0-865.tar.bz2 b/open-iscsi-2.0-865.tar.bz2 deleted file mode 100644 index 4b4d060..0000000 --- a/open-iscsi-2.0-865.tar.bz2 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:60c6082f6129958f6c444ef22bb5bca5738e12a8699af7d2237f383a9aafbb4f -size 171378 diff --git a/open-iscsi-2.0-870-rc1.tar.bz2 b/open-iscsi-2.0-870-rc1.tar.bz2 new file mode 100644 index 0000000..031c3c2 --- /dev/null +++ b/open-iscsi-2.0-870-rc1.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:881b0ea5209f1617cad0a1126c93e33c837075ebed01fa0219d36dd368c475c0 +size 237601 diff --git a/open-iscsi-866.diff b/open-iscsi-866.diff deleted file mode 100644 index 9aaac7c..0000000 --- a/open-iscsi-866.diff +++ /dev/null @@ -1,12 +0,0 @@ -Index: usr/iscsiadm.c -=================================================================== ---- usr/iscsiadm.c (revision 865) -+++ usr/iscsiadm.c (revision 866) -@@ -852,7 +852,6 @@ - printf("\n"); - - t = get_transport_by_sid(curr->sid); -- rc = iface_get_by_bind_info(db, &curr->iface, &iface); - - printf("\t\t**********\n"); - printf("\t\tInterface:\n"); diff --git a/open-iscsi-backport-fwparam_ibft b/open-iscsi-backport-fwparam_ibft deleted file mode 100644 index 74371c5..0000000 --- a/open-iscsi-backport-fwparam_ibft +++ /dev/null @@ -1,317 +0,0 @@ -commit 318863586af9278c82bd8543b8891002ce76d485 -Author: Hannes Reinecke -Date: Mon Mar 10 11:21:49 2008 +0100 - - Add compability program fwparam_ibft - - We still need the fwparam_ibft program as the fw discovery won't - give us details about the network settings. - - Signed-off-by: Hannes Reinecke - -diff --git a/usr/Makefile b/usr/Makefile -index 0e54cf6..42a8788 100644 ---- a/usr/Makefile -+++ b/usr/Makefile -@@ -42,19 +42,19 @@ COMMON_SRCS = $(ISCSI_LIB_SRCS) - # core initiator files - INITIATOR_SRCS = initiator.o scsi.o actor.o mgmt_ipc.o isns.o transport.o - # fw boot files --FW_BOOT_SRCS = $(wildcard ../utils/fwparam_ibft/*.o) -+FWPARAM_LIB = ../utils/fwparam_ibft/libfwparam.a - - all: $(PROGRAMS) - - iscsid: $(COMMON_SRCS) $(IPC_OBJ) $(INITIATOR_SRCS) iscsid.o - $(CC) $(CFLAGS) $^ -o $@ - --iscsiadm: $(COMMON_SRCS) $(FW_BOOT_SRCS) strings.o discovery.o iscsiadm.o -- $(CC) $(CFLAGS) $^ -o $@ -+iscsiadm: $(COMMON_SRCS) strings.o discovery.o iscsiadm.o -+ $(CC) $(CFLAGS) $^ -o $@ $(FWPARAM_LIB) - --iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \ -+iscsistart: $(COMMON_SRCS) $(IPC_OBJ) $(INITIATOR_SRCS) \ - iscsistart.o statics.o -- $(CC) $(CFLAGS) -static $^ -o $@ -+ $(CC) $(CFLAGS) -static $^ -o $@ $(FWPARAM_LIB) - - clean: - rm -f *.o $(PROGRAMS) .depend -diff --git a/utils/fwparam_ibft/Makefile b/utils/fwparam_ibft/Makefile -index 6d7d00a..1414f06 100644 ---- a/utils/fwparam_ibft/Makefile -+++ b/utils/fwparam_ibft/Makefile -@@ -21,6 +21,9 @@ - # "Prasanna Mumbai" - # - -+PROG = fwparam_ibft -+MAIN = fwparam_main.o -+LIB = libfwparam.a - OBJS := fwparam_ibft.o fw_entry.o - OBJS += prom_lex.o prom_parse.tab.o fwparam_ppc.o - CLEANFILES = $(OBJS) $(GENFILES) *.output *~ -@@ -39,7 +42,10 @@ OPTFLAGS ?= -O2 -g -fPIC - WARNFLAGS ?= -Wall -Wstrict-prototypes - CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../../include - --all: $(OBJS) -+all: $(LIB) $(PROG) -+ -+$(LIB): $(OBJS) -+ $(AR) rcv $(LIB) $(OBJS) - - clean: - rm -f *.o $(CLEANFILES) .depend -@@ -54,6 +60,9 @@ $(GENFILES): Makefile - - $(OBJS): prom_parse.tab.h prom_parse.h fwparam_ibft.h - -+$(PROG): $(MAIN) $(LIB) -+ gcc $(CFLAGS) -o $@ $< libfwparam.a -+ - depend: - gcc $(CFLAGS) -M `ls *.c` > .depend - -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index dcc63c3..4397f94 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -319,7 +319,7 @@ dump_ibft(void *ibft_loc, struct boot_context *context) - struct ibft_initiator *initiator = NULL; - struct ibft_nic *nic0 = NULL, *nic1 = NULL; - struct ibft_tgt *tgt0 = NULL, *tgt1 = NULL; -- char ipbuf[32]; -+ char buf[32]; - - control = ibft_loc + sizeof(*ibft_hdr); - CHECK_HDR(control, control); -@@ -359,6 +359,28 @@ dump_ibft(void *ibft_loc, struct boot_context *context) - CHECK_HDR(tgt1, target); - } - -+ if (!context) { -+ snprintf(buf, sizeof(buf), "iSCSI_INITIATOR_"); -+ -+ if (initiator && (initiator->hdr.flags & -+ INIT_FLAG_FW_SEL_BOOT)) -+ dump_initiator_prefix(ibft_loc, initiator, buf); -+ -+ if (nic0 && (nic0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -+ dump_nic_prefix(ibft_loc, nic0, buf); -+ else if (nic1 && (nic1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -+ dump_nic_prefix(ibft_loc, nic1, buf); -+ -+ snprintf(buf, sizeof(buf), "iSCSI_TARGET_"); -+ -+ if (tgt0 && (tgt0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -+ dump_tgt_prefix(ibft_loc, tgt0, buf); -+ else if (tgt1 && (tgt1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -+ dump_tgt_prefix(ibft_loc, tgt1, buf); -+ -+ return 0; -+ } -+ - strncpy(context->initiatorname, - (char *)ibft_loc+initiator->initiator_name_off, - initiator->initiator_name_len + 1); -@@ -367,10 +389,10 @@ dump_ibft(void *ibft_loc, struct boot_context *context) - strncpy((char *)context->targetname, - (char *)(ibft_loc+tgt0->tgt_name_off), - tgt0->tgt_name_len); -- format_ipaddr(ipbuf, sizeof(ipbuf), -+ format_ipaddr(buf, sizeof(buf), - tgt0->ip_addr); -- strncpy((char *)context->target_ipaddr, ipbuf, -- sizeof(ipbuf)); -+ strncpy((char *)context->target_ipaddr, buf, -+ sizeof(buf)); - context->target_port = tgt0->port; - strncpy(context->chap_name, - (char *)(ibft_loc + tgt0->chap_name_off), -@@ -389,10 +411,10 @@ dump_ibft(void *ibft_loc, struct boot_context *context) - strncpy((char *)context->targetname, - (char *)(ibft_loc+tgt1->tgt_name_off), - tgt1->tgt_name_len); -- format_ipaddr(ipbuf, sizeof(ipbuf), -+ format_ipaddr(buf, sizeof(buf), - tgt1->ip_addr); -- strncpy((char *)context->target_ipaddr,ipbuf, -- sizeof(ipbuf)); -+ strncpy((char *)context->target_ipaddr,buf, -+ sizeof(buf)); - context->target_port = tgt1->port; - strncpy(context->chap_name, - (char *)(ibft_loc + tgt1->chap_name_off), -@@ -411,7 +433,7 @@ dump_ibft(void *ibft_loc, struct boot_co - return 0; - } - --char *search_ibft(unsigned char *start, int length) -+char *search_ibft(unsigned char *start, int start_addr, int length) - { - unsigned char *cur_ptr, *rom_end; - struct ibft_table_hdr *ibft_hdr; -@@ -426,35 +448,51 @@ char *search_ibft(unsigned char *start, - continue; - } - memcpy(&rom_size, cur_ptr + 2, 1); -- /* Don't search past the end of the ROM BIOS block */ -- rom_end = cur_ptr + (rom_size * 512) - strlen(iBFTSTR); -- while (cur_ptr < rom_end) { -- if (!memcmp(cur_ptr, iBFTSTR,strlen(iBFTSTR))) -- break; -- cur_ptr++; -- } - -- if (cur_ptr == rom_end) { -- cur_ptr += strlen(iBFTSTR); -- continue; -- } -+ if (debug > 1) -+ fprintf(stderr, "Found rom at %x of size %d\n", -+ ((int)(cur_ptr - start) + start_addr), -+ rom_size * 512); - -- ibft_hdr = (struct ibft_table_hdr *)cur_ptr; -- /* Make sure it's correct version. */ -- if (ibft_hdr->revision != iBFT_REV) { -- cur_ptr = rom_end + strlen(iBFTSTR); -+ if (rom_size == 0) { -+ /* Skip empty rom areas */ -+ cur_ptr += 512; - continue; - } - -- /* Make sure that length is valid. */ -- if ((cur_ptr + ibft_hdr->length) <= (start + length)) { -- /* Let verify the checksum */ -- for (i = 0, check_sum = 0; i < ibft_hdr->length; i++) -- check_sum += cur_ptr[i]; -+ /* Don't search past the end of rom area */ -+ rom_end = (cur_ptr + (rom_size * 512)) - strlen(iBFTSTR); - -- if (check_sum == 0) -- return (char *)cur_ptr; -+ while (cur_ptr < rom_end) { -+ if (memcmp(cur_ptr, iBFTSTR,strlen(iBFTSTR))) { -+ cur_ptr++; -+ continue; -+ } -+ -+ if (debug > 1) -+ fprintf(stderr, "Found iBFT table at %x\n", -+ (int)(cur_ptr - start) + start_addr); -+ -+ ibft_hdr = (struct ibft_table_hdr *)cur_ptr; -+ -+ /* Make sure it's correct version. */ -+ if (ibft_hdr->revision != iBFT_REV) { -+ cur_ptr = rom_end; -+ continue; -+ } -+ -+ /* Make sure that length is valid. */ -+ if ((cur_ptr + ibft_hdr->length) <= (start + length)) { -+ /* Let verify the checksum */ -+ for (i = 0, check_sum = 0; i < ibft_hdr->length; i++) -+ check_sum += cur_ptr[i]; -+ -+ if (check_sum == 0) -+ return (char *)cur_ptr; -+ } -+ cur_ptr = rom_end; - } -+ cur_ptr += strlen(iBFTSTR); - } - return NULL; - } -@@ -505,7 +543,7 @@ fwparam_ibft(struct boot_context *contex - goto done; - } - -- ibft_loc = search_ibft((unsigned char *)filebuf, end_search); -+ ibft_loc = search_ibft((unsigned char *)filebuf, start, end_search); - if (ibft_loc) - ret = dump_ibft(ibft_loc, context); - else { -diff --git a/utils/fwparam_ibft/fwparam_main.c b/utils/fwparam_ibft/fwparam_main.c -new file mode 100644 -index 0000000..3a91b52 ---- /dev/null -+++ b/utils/fwparam_ibft/fwparam_main.c -@@ -0,0 +1,73 @@ -+/* -+ * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -+ * USA. -+ * -+ * Copyright (C) IBM Corporation, 2006 -+ * -+ * Authors: Patrick Mansfield -+ * Mike Anderson -+ * Hannes Reinecke -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "fwparam_ibft.h" -+#include "fw_context.h" -+ -+extern int debug; -+ -+int -+main (int argc, char **argv) -+{ -+ int option, ret; -+ char *progname, *filebuf = NULL; -+ -+ progname = argv[0]; -+ -+ while (1) { -+ option = getopt(argc, argv, "f:vhb"); -+ if (option == -1) -+ break; -+ switch (option) { -+ case 'b': -+ /* Ignored for compability */ -+ break; -+ case 'f': -+ filebuf = optarg; -+ break; -+ case 'v': -+ debug++; -+ break; -+ default: -+ fprintf(stderr, "Unknown or bad option '%c'\n", option); -+ case 'h': -+ printf("Usage: %s OPTIONS\n" -+ "-b print only fw boot selected sections\n" -+ "-f file_to_search (default /dev/mem)\n" -+ "-v verbose\n", -+ progname); -+ exit(1); -+ } -+ } -+ -+ ret = fwparam_ibft(NULL, filebuf); -+ -+ exit(ret); -+} diff --git a/open-iscsi-format-luns b/open-iscsi-format-luns deleted file mode 100644 index d5a03c4..0000000 --- a/open-iscsi-format-luns +++ /dev/null @@ -1,29 +0,0 @@ -commit 1a44165c0757fb3aefd0fae0cb1554756b38c2a6 -Author: Hannes Reinecke -Date: Mon Mar 10 10:56:15 2008 +0100 - - Format LUNs correctly - - We are printing two characters at a time, so we should skip the same - amount of characters for each round. - - Signed-off-by: Hannes Reinecke - -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index d6b8b7f..dcc63c3 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -81,8 +81,11 @@ format_lun(char *buf, size_t size, uint8_t *lun) - { - int i; - -- for (i = 0; i < 8; i++) -- snprintf(buf++, size--, "%x", lun[i]); -+ for (i = 0; i < 8; i++) { -+ snprintf(buf, size, "%02x", lun[i]); -+ buf += 2; -+ size -= 2; -+ } - } - - void diff --git a/open-iscsi-fwparam-scan-in-blocks b/open-iscsi-fwparam-scan-in-blocks deleted file mode 100644 index 3eb6e89..0000000 --- a/open-iscsi-fwparam-scan-in-blocks +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c -diff --git a/kernel/iscsi_tcp.h b/kernel/iscsi_tcp.h -diff --git a/kernel/libiscsi.c b/kernel/libiscsi.c -diff --git a/kernel/libiscsi.h b/kernel/libiscsi.h -diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c -diff --git a/kernel/scsi_transport_iscsi.h b/kernel/scsi_transport_iscsi.h -diff --git a/usr/discovery.c b/usr/discovery.c -diff --git a/usr/idbm.c b/usr/idbm.c -diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index 02f8ac8..4557016 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -39,6 +39,8 @@ int debug; - int dev_count; - char filename[FILENAMESZ]; - -+char ID_ROMEXT[]={0x55, 0xaa, 0}; /* extended rom magic */ -+ - const char nulls[16]; /* defaults to zero */ - - int -@@ -408,22 +410,38 @@ dump_ibft(void *ibft_loc, struct boot_context *context) - - char *search_ibft(unsigned char *start, int length) - { -- unsigned char *cur_ptr; -+ unsigned char *cur_ptr, *rom_end; - struct ibft_table_hdr *ibft_hdr; -- unsigned char check_sum; -+ unsigned char check_sum, rom_size; - uint32_t i; - - cur_ptr = (unsigned char *)start; -- for (cur_ptr = (unsigned char *)start; -- cur_ptr < (start + length); -- cur_ptr++) { -- if (memcmp(cur_ptr, iBFTSTR,strlen(iBFTSTR))) -+ while (cur_ptr < (start + length)) { -+ if (memcmp(cur_ptr, ID_ROMEXT, strlen(ID_ROMEXT)) != 0) { -+ /* Skip this block */ -+ cur_ptr += 512; - continue; -+ } -+ memcpy(&rom_size, cur_ptr + 2, 1); -+ /* Don't search past the end of the ROM BIOS block */ -+ rom_end = cur_ptr + (rom_size * 512) - strlen(iBFTSTR); -+ while (cur_ptr < rom_end) { -+ if (!memcmp(cur_ptr, iBFTSTR,strlen(iBFTSTR))) -+ break; -+ cur_ptr++; -+ } -+ -+ if (cur_ptr == rom_end) { -+ cur_ptr += strlen(iBFTSTR); -+ continue; -+ } - - ibft_hdr = (struct ibft_table_hdr *)cur_ptr; - /* Make sure it's correct version. */ -- if (ibft_hdr->revision != iBFT_REV) -+ if (ibft_hdr->revision != iBFT_REV) { -+ cur_ptr = rom_end + strlen(iBFTSTR); - continue; -+ } - - /* Make sure that length is valid. */ - if ((cur_ptr + ibft_hdr->length) <= (start + length)) { -diff --git a/utils/fwparam_ibft/fwparam_ibft.h b/utils/fwparam_ibft/fwparam_ibft.h diff --git a/open-iscsi-git-update b/open-iscsi-git-update deleted file mode 100644 index 8cdaba6..0000000 --- a/open-iscsi-git-update +++ /dev/null @@ -1,28294 +0,0 @@ -diff --git a/Makefile b/Makefile -index b2ba0c3..e405c9c 100644 ---- a/Makefile -+++ b/Makefile -@@ -15,8 +15,7 @@ etcdir = /etc - initddir = $(etcdir)/init.d - - MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8 --PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery \ -- utils/fwparam_ibft/fwparam_ibft utils/iscsi-iname -+PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery utils/iscsi-iname - INSTALL = install - ETCFILES = etc/iscsid.conf - IFACEFILES = etc/iface.example -@@ -26,10 +25,10 @@ IFACEFILES = etc/iface.example - # over multiple makefile. - - all: -+ $(MAKE) -C utils/fwparam_ibft - $(MAKE) -C usr - $(MAKE) -C kernel - $(MAKE) -C utils -- $(MAKE) -C utils/fwparam_ibft - @echo - @echo "Compilation complete Output file" - @echo "----------------------------------- ----------------" -@@ -38,15 +37,14 @@ all: - @echo "Built iSCSI over TCP kernel module: kernel/iscsi_tcp.ko" - @echo "Built iSCSI daemon: usr/iscsid" - @echo "Built management application: usr/iscsiadm" -- @echo "Built utility: utils/fwparam_ibft/fwparam_ibft" - @echo - @echo Read README file for detailed information. - - clean: -+ $(MAKE) -C utils/fwparam_ibft clean - $(MAKE) -C utils clean - $(MAKE) -C usr clean - $(MAKE) -C kernel clean -- $(MAKE) -C utils/fwparam_ibft clean - - # this is for safety - # now -jXXX will still be safe -@@ -58,6 +56,9 @@ clean: - install: install_kernel install_programs install_doc install_etc \ - install_initd install_iname install_iface - -+install_usr: install_programs install_doc install_etc \ -+ install_initd install_iname install_iface -+ - install_programs: $(PROGRAMS) - $(INSTALL) -d $(DESTDIR)$(sbindir) - $(INSTALL) -m 755 $^ $(DESTDIR)$(sbindir) -@@ -78,6 +79,8 @@ install_initd_suse: - $(INSTALL) -d $(DESTDIR)$(initddir) - $(INSTALL) -m 755 etc/initd/initd.suse \ - $(DESTDIR)$(initddir)/open-iscsi -+ $(INSTALL) -m 755 etc/initd/boot.suse \ -+ $(DESTDIR)$(initddir)/boot.open-iscsi - - install_initd_redhat: - $(INSTALL) -d $(DESTDIR)$(initddir) -@@ -94,8 +97,10 @@ install_iface: $(IFACEFILES) - $(INSTALL) -m 644 $^ $(DESTDIR)$(etcdir)/iscsi/ifaces - - install_etc: $(ETCFILES) -- $(INSTALL) -d $(DESTDIR)$(etcdir)/iscsi -- $(INSTALL) -m 644 $^ $(DESTDIR)$(etcdir)/iscsi -+ if [ ! -f /etc/iscsi/iscsid.conf ]; then \ -+ $(INSTALL) -d $(DESTDIR)$(etcdir)/iscsi ; \ -+ $(INSTALL) -m 644 $^ $(DESTDIR)$(etcdir)/iscsi ; \ -+ fi - - install_doc: $(MANPAGES) - $(INSTALL) -d $(DESTDIR)$(mandir)/man8 -@@ -113,4 +118,9 @@ install_iname: - echo "***************************************************" ; \ - fi - -+depend: -+ for dir in usr utils utils/fwparam_ibft; do \ -+ $(MAKE) -C $$dir $@; \ -+ done -+ - # vim: ft=make tw=72 sw=4 ts=4: -diff --git a/README b/README -index 59878ce..21f961a 100644 ---- a/README -+++ b/README -@@ -4,7 +4,7 @@ - - ================================================================= - -- Jan 26, 2007 -+ Mar 14, 2008 - - Contents - ======== -@@ -16,7 +16,8 @@ Contents - - 5. Open-iSCSI Configuration Utility - - 6. Configuration - - 7. Getting Started --- 8. iSCSI System Info -+- 8. Advanced Configuration -+- 9. iSCSI System Info - - - 1. In This Release -@@ -69,9 +70,14 @@ iscsid, and a management utility iscsiadm. - =============== - - As of today, the Open-iSCSI Initiator requires a host running the --Linux operating system with kernel version 2.6.16, or later. See --http://www.open-iscsi.org/cgi-bin/wiki.pl/Supported_Kernels --for a more information about which kernels Open-iSCSI supports. -+Linux operating system with kernel version 2.6.16, or later. 2.6.14 and -+2.6.15 are partially supported. Known issues with 2.6.14 - .15 support: -+ -+- If the device is using a write back cache, during session logout -+the cache sync command will fail. -+- iscsiadm's -P 3 option will not print out scsi devices. -+- iscsid will not automatically online devices. -+ - You need to enable "Cryptographic API" under "Cryptographic options" in the - kernel config. And you must enable "CRC32c CRC algorithm" even if - you do not use header or data digests. They are the kernel options, -@@ -95,16 +101,26 @@ To compile on SUSE Linux you'll have to use - - where is the kernel configuration to use (eg. 'smp'). - --If you choose to install the Debian packages instead of building from source, --please read the file /usr/share/doc/linux-iscsi/README.debian for information --on how to build kernel modules against your specific kernel. -- - For Red Hat/Fedora and Debian distributions open-iscsi can be installed by - typing "make install". This will copy iscsid and iscsiadm to /usr/sbin, the - init script to /etc/init.d, and the kernel modules: iscsi_tcp.ko, libiscsi.ko - and scsi_transport_iscsi to /lib/modules/`uname -r`/kernel/drivers/scsi/ - overwriting existing iscsi modules. - -+For Debian, be sure to install the linux-headers package that -+corresponds to your kernel in order to compile the kernel modules -+('aptitude install linux-headers-`uname -r`'). You may also wish to -+run 'make -C kernel/ dpkg_divert' before installing kernel modules if -+you run a Debian-provided kernel. This will use dpkg-divert(8) to -+move the packaged kernel modules out of the way, and ensure that -+future kernel upgrades will not overwrite them. -+ -+Also, please be aware that the compatibility patches that enable these -+iscsi modules to run on kernels older than 2.6.25 will not update the -+ib_iser module; you may get warnings related to mismatched symbols on -+this driver, in which case you'll be unable to load ib_iser and -+open-iscsi simultaneously. -+ - 4. Open-iSCSI daemon - ==================== - -@@ -168,10 +184,36 @@ Usage: iscsiadm [OPTION] - - -m, --mode specify operational mode op = - -m discovery --type=[type] --interface=iscsi_ifacename \ -- --portal=[ip:port] --login --print=[N] -+ --portal=[ip:port] --login --print=[N] \ -+ --op=[op]=[NEW | UPDATE | DELETE] - perform [type] discovery for target portal with - ip-address [ip] and port [port]. - -+ By default this command will remove records -+ for portals no longer returned. And, if a portal is -+ returned by the target, then the discovery command -+ will create a new record or modify an existing one -+ with values from iscsi.conf and the command line. -+ -+ [op] can be passed in multiple times to this -+ command, and it will alter the DB manipulation. -+ -+ If [op] is passed in and the value is -+ "new", iscsiadm will add records for portals that do -+ not yet have records in the db. -+ -+ If [op] is passed in and the value is -+ "update", iscsiadm will update records using info -+ from iscsi.conf and the command line for portals -+ that are returned during discovery and have -+ a record in the db. -+ -+ if [op] is passed in and the value is "delete", -+ iscsiadm will delete records for portals that -+ were not returned during discovery. -+ -+ See the example section for more info. -+ - See below for how to setup iscsi ifaces for - software iscsi or override the system defaults. - -@@ -214,7 +256,10 @@ Usage: iscsiadm [OPTION] - op could be one of: - [new], [delete], [update] or [show]. In case of - [update], you have to provide [name] and [value] -- you wish to update -+ you wish to update. -+ [delete] - Note that if a session is using the -+ node record, the session will be logged out then -+ the record will be deleted. - - Print level can be 0 to 1. - -@@ -224,11 +269,11 @@ Usage: iscsiadm [OPTION] - Stats prints the iSCSI stats for the session. - -m node --logoutall=[all|manual|automatic] - Logout "all" the running sessions or just the ones -- with a node or conn startup value manual or automatic. -+ with a node startup value manual or automatic. - Nodes marked as ONBOOT are skipped. - -m node --loginall=[all|manual|automatic] - Login "all" the running sessions or just the ones -- with a node or conn startup value manual or automatic. -+ with a node startup value manual or automatic. - Nodes marked as ONBOOT are skipped. - -m session display all active sessions and connections - -m session --sid=[sid] [ --print=level | --rescan | --logout ] -@@ -322,7 +367,7 @@ TCP/IP. Then in /etc/iscsi/ifaces/iface0 you would enter: - iface.transport_name = tcp - iface.hwaddress = 00:0F:1F:92:6B:BF - --and in /etc/iscsi/ifaces1 you would enter: -+and in /etc/iscsi/ifaces/iface1 you would enter: - - iface.transport_name = tcp - iface.hwaddress = 00:C0:DD:08:63:E7 -@@ -340,7 +385,7 @@ with the name "iface0" this command will overwrite it.) - (This will set the hwaddress.) - # iscsiadm -m iface -I iface0 --op=update -n iface.hwaddress -v 00:0F:1F:92:6B:BF - --If you had sessions logged in iscsiadm will not update, delete or overwrite -+If you had sessions logged in iscsiadm will not update, overwrite - a iface. You must log out first. If you have a iface bound to a node/portal - but you have not logged in then, iscsiadm will update the config and - all existing bindings. -@@ -400,7 +445,49 @@ iscsiadm -m node -p ip:port -I iface0 --op=delete - discovery will be setup so that they can logged in through - those interfaces. - -- - SendTargets iSCSI Discovery with a specific interface. If you -+ - SendTargets iSCSI Discovery updating existing records: -+ -+ ./iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260 \ -+ -o update -+ -+ If there a record for targetX and portalY exists in the DB, and -+ is returned during discovery, it will be updated with the info -+ from the iscsi.conf. No new portals will be added and stale -+ portals will not be removed. -+ -+ - SendTargets iSCSI Discovery deleting existing records: -+ -+ ./iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260 \ -+ -o delete -+ -+ If there a record for targetX and portalY exists in the DB, but -+ is not returned during discovery it will be removed from the DB. -+ No new portals will be added and existing portal records will not -+ be changed. -+ -+ Note: If a session is logged into portal we are going to delete -+ a record for, it will be logged out then the record will be -+ deleted. -+ -+ - SendTargets iSCSI Discovery adding new records: -+ -+ ./iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260 \ -+ -o new -+ -+ If there targetX and portalY is returned during discovery and does -+ not have a record, it will be added. Existing records are not -+ modified. -+ -+ - SendTargets iSCSI Discovery using multiple ops: -+ -+ ./iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260 \ -+ -o new -o delete -+ -+ This command will add new portals and delete records for portals -+ no longer returned. It will not change the record information for -+ existing portals. -+ -+ - SendTargets iSCSI Discovery with a specific interface. If you - wish to only use a subset of the interfaces in /etc/iscsi/ifaces - then you can pass them in during discovery: - -@@ -439,12 +526,14 @@ iscsiadm -m node -p ip:port -I iface0 --op=delete - - To specify a IPv6 address the following can be used: - -- ./iscsiadm -m node -T -p 2001:c90::211:9ff:feb8:a9e9 -l -+ ./iscsiadm -m node -T iqn.2005-03.com.max \ -+ -p 2001:c90::211:9ff:feb8:a9e9 -l - - The above command would use the default port, 3260. To specify a - port use the following: - -- ./iscsiadm -m node -T -p [2001:c90::211:9ff:feb8:a9e9]:3260 -l -+ ./iscsiadm -m node -T iqn.2005-03.com.max \ -+ -p [2001:c90::211:9ff:feb8:a9e9]:3260 -l - - - iSCSI Login to a specific portal through the NIC setup as iface0: - -@@ -714,6 +803,9 @@ running: - - iscsiadm -m discovery -t isns - -+*** Warning *** iSNS support is experimental in this release. The command -+line options for it will change in future releases. -+ - Both commands will print out the list of all discovered targets and their - portals: - -@@ -763,16 +855,16 @@ Note: this may only work for distros with init scripts. - To automate login to a node, use the following with the record ID - (record ID is the targetname and portal) of the node discovered in the - discovery above: -- iscsiadm -m node -T targetname -p ip:port --op update -n node.conn[0].startup -v automatic -+ iscsiadm -m node -T targetname -p ip:port --op update -n node.startup -v automatic - - To set the automatic setting to all portals on a target through every - interface setup for each protal, the following can be run: -- iscsiadm -m node -T targetname --op update -n node.conn[0].startup -v automatic -+ iscsiadm -m node -T targetname --op update -n node.startup -v automatic - --Or to set the "node.conn[0].statup" attribute to "startup" as default for -+Or to set the "node.startup" attribute to "startup" as default for - all sessions add the following to the /etc/iscsi/iscsid.conf: - -- node.conn[0].startup = automatic -+ node.startup = automatic - - Setting this in iscsid.conf, will not affect existing nodes. It will only - affect nodes that are discovered after setting the value. -@@ -782,7 +874,149 @@ e.g /etc/init.d/open-iscsi restart. On your next startup the nodes will - be logged into autmotically. - - --8. iSCSI System Info -+8. Advanced Configuration -+========================= -+ -+8.1 iSCSI settings for dm-multipath -+----------------------------------- -+ -+When using dm-multipath, the iSCSI timers should be set so that commands -+are quickly failed to the dm-multipath layer. For dm-multipath you should -+then set values like queue if no path, so that IO errors are retried and -+queued if all paths are failed in the multipath layer. -+ -+ -+8.1.1 iSCSI ping/Nop-Out settings -+--------------------------------- -+To quickly detect problems in the network, the iSCSI layer will send iSCSI -+pings (iSCSI NOP-Out requests) to the target. If a NOP-Out times out the -+iSCSI layer will respond by failing running commands and asking the SCSI -+layer to requeue them if possible (SCSI disk commands get 5 retries if not -+using multipath). If dm-multipath is being used the SCSI layer will fail -+the command to the multipath layer instead of retrying. The multipath layer -+will then retry the command on another path. -+ -+To control how often a NOP-Out is sent the following value can be set: -+ -+node.conn[0].timeo.noop_out_interval = X -+ -+Where X is in seconds and the default is 10 seconds. To control the -+timeout for the NOP-Out the noop_out_timeout value can be used: -+ -+node.conn[0].timeo.noop_out_timeout = X -+ -+Again X is in seconds and the default is 15 seconds. -+ -+Normally for these values you can use: -+ -+node.conn[0].timeo.noop_out_interval = 5 -+node.conn[0].timeo.noop_out_timeout = 10 -+ -+If there are a lot of IO error messages, then the above values may be too -+aggresive and you may need to increase the values for your network conditions -+and workload, or you may need to check your network for possible problems. -+ -+ -+8.1.2 replacement_timeout -+------------------------- -+The next iSCSI timer that will need to be tweaked is: -+ -+node.session.timeo.replacement_timeout = X -+ -+Here X is in seconds. -+ -+replacement_timeout will control how long to wait for session re-establishment -+before failing pending SCSI commands and commands that are being operated on by -+the SCSI layer's error handler up to a higher level like multipath or to -+an application if multipath is not being used. -+ -+ -+8.1.2.1 Running Commands, the SCSI Error Handler, and replacement_timeout -+------------------------------------------------------------------------- -+Remember, from the Nop-out discussion that if a network problem is detected, -+the running commands are failed immediately. There is one exception to this -+and that is when the SCSI layer's error handler is running. To check if -+the SCSI error handler is running iscsiadm can be run as: -+ -+iscsiadm -m session -P 3 -+ -+You will then see: -+ -+Host Number: X State: Recovery -+ -+When the SCSI EH is running, commands will not be failed until -+node.session.timeo.replacement_timeout seconds. -+ -+To modify the the timer that starts the SCSI EH, you can either write -+directly to the devices's sysfs file: -+ -+echo X > /sys/block/sdX/device/timeout -+ -+where X is in seconds or on most distros you can modify the udev rule. -+ -+To modify the udev rule open /etc/udev/rules.d/50-udev.rules, and find the -+following lines: -+ -+ACTION=="add", SUBSYSTEM=="scsi" , SYSFS{type}=="0|7|14", \ -+ RUN+="/bin/sh -c 'echo 60 > /sys$$DEVPATH/timeout'" -+ -+And change the echo 60 part of the line to the value that you want. -+ -+The default timeout for normal File System commands is 30 seconds when udev -+is not being used. If udev is used the default is the above value which -+is normally 60 seconds. -+ -+ -+8.1.2.2 Pending Commands and replacement_timeout -+------------------------------------------------ -+Commonly, the SCSI/BLOCK layer will queue 256 commands, but the path can -+only take 32. When a network problem is detected, the 32 commands -+in flight will be sent back to the SCSI layer immediately and because -+multipath is being used this will cause the commands to be sent to the multipath -+layer for execution on another path. However the other 96 commands that were -+still in the SCSI/BLOCK queue, will remain here until the session is -+re-established or until node.session.timeo.replacement_timeout seconds has -+gone by. After replacement_timeout seconds, the pending commands will be -+failed to the multipath layer, and all new incoming commands will be -+immediately failed back to the multipath layer. If a session is later -+re-established, then new commands will be queued and executed. Normally, -+multipathd's path tester mechanism will detect that the session has been -+re-established and the path is accessable again, and it will inform -+dm-multipath. -+ -+ -+8.1.3 Optimal replacement_timeout Value -+--------------------------------------- -+ -+The default value for replacement_timeout is 120 seconds, but because -+multipath's queue_if_no_path and no_path_retyr setting can prevent IO errors -+from being propogated to the application, replacement_timeout can be set to a -+shorter value like 5 to 15 seconds. By setting it lower pending IO is quickly -+sent to a new path and executed while the iSCSI layer attempts -+re-establishment of the session. If all paths end up being failed, then the -+multipath and device mapper layer will internally queue IO based on the -+multipath.conf settings, instead of the iSCSI layer. -+ -+ -+8.2 iSCSI settings for iSCSI root -+--------------------------------- -+ -+When accessing the root parition directly through a iSCSI disk, the -+iSCSI timers should be set so that iSCSI layer has several chances to try to -+re-establish a session and so that commands are not quickly requeued to -+the SCSI layer. Basically you want the opposite of when using dm-multipath. -+ -+For this setup, you can turn off iSCSI pings by setting: -+ -+node.conn[0].timeo.noop_out_interval = 0 -+node.conn[0].timeo.noop_out_timeout = 0 -+ -+And you can turn the replacement_timer to a very long value: -+ -+node.session.timeo.replacement_timeout = 86400 -+ -+ -+9. iSCSI System Info - ==================== - - To get information about the running sessions: including the session and -diff --git a/doc/iscsi_discovery.8 b/doc/iscsi_discovery.8 -index e772130..c46223b 100644 ---- a/doc/iscsi_discovery.8 -+++ b/doc/iscsi_discovery.8 -@@ -8,32 +8,45 @@ - .SH NAME - iscsi_discovery \- discover iscsi devices - .SH SYNOPSIS --.B iscsi_discovery [port] -+.B iscsi_discovery [-p ] [-d] [-t [-f]] [-m] [-l] - - .SH DESCRIPTION - Perform send-targets discovery to the specified IP. If a discovery record --is generated, try to login to the portal using iSER and TCP transports. -+is generated, try to login to the portal using iSER and TCP transports -+(-t flag specifies the requested transport type, TCP is the default). - If login using a certain transport succeeds, mark the portal for automatic --login, and disconnect. -+login (unless -m flag is used), and disconnect (unless -l flag is used). - - For iscsi discovery to work, open-iscsi services must be running. e.g. iscsid - should be up, and the iscsi modules loaded. This is best accomplished by the - init.d startup script. - - .\" .SH OPTIONS --.\" .TP --.\" .B \- --.\" --.\" .TP --.\" .B \- --.\" --.\" .TP --.\" .B --.\" .SH "SEE ALSO" --.\" -+.TP -+.BI [-p=]\fIport\-number\fP -+set the port number (defualt is 3260). -+.TP -+.BI [-d] -+print debugging information. -+.TP -+.BI [-t=]\fItransport\-type\fP -+set transport (default is tcp). -+.TP -+.BI [-f] -+force specific transport - -+disable the fallback to tcp (default is fallback enabled). -+force the transport specified by the argument of the -t flag. -+ -+.TP -+.BI [-m] -+manual startup - will set manual startup (default is automatic startup). -+.TP -+.BI [-l] -+login - login to the new discovered nodes (defualt is false). -+ - .SH AUTHOR - Written by Dan Bar Dov - .SH "REPORTING BUGS" --Report bugs to . -+Report bugs to . - .SH COPYRIGHT - Copyright \(co Voltaire Ltd. 2006. -diff --git a/doc/iscsiadm.8 b/doc/iscsiadm.8 -index c750b4d..11f49e2 100644 ---- a/doc/iscsiadm.8 -+++ b/doc/iscsiadm.8 -@@ -2,15 +2,19 @@ - .SH NAME - iscsiadm \- open-iscsi administration utility - .SH SYNOPSIS --\fBiscsiadm\fR -m discovery [ [ -dhV ] --print=[N] ] [ -I iface -t type -p ip:port [ -l ] ] | -+\fBiscsiadm\fR -m discovery [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I iface -t type -p ip:port [ -l ] ] | - [ -o operation ] [ -n name ] [ -v value ] - --\fBiscsiadm\fR -m node [ -dhV ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I iface ] [ -l | -u | -R | -s] ] -+\fBiscsiadm\fR -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I iface ] [ -l | -u | -R | -s] ] - [ [ -o operation ] [ -n name ] [ -v value ] [ -p ip:port ] ] - --\fBiscsiadm\fR -m session [ -dhV ] [ -P printlevel ] [ -r sessionid | sysfsdir [ -R ] [ -u | -s ] ] -+\fBiscsiadm\fR -m session [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -r sessionid | sysfsdir [ -R ] [ -u | -s ] ] - --\fBiscsiadm\fR -m iface [ -dhV ] [ -P printlevel ] [ -I ifacename ] [ [ -o operation ] [ -n name ] [ -v value ] ] -+\fBiscsiadm\fR -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename ] [ [ -o operation ] [ -n name ] [ -v value ] ] -+ -+\fBiscsiadm\fR -m fw [-l] -+ -+\fBiscsiadm\fR -k priority - - .SH "DESCRIPTION" - The iscsiadm utility is a command-line tool allowing discovery and login -@@ -33,8 +37,8 @@ daemon (iscsid) be running. - .SH OPTIONS - - .TP --\fB\-d\fR, \fB\-\-debug\fR --print debugging information -+\fB\-d\fR, \fB\-\-debug=\fIdebug_level\fP -+print debugging information. Valid values for debug_level are 0 to 8. - - .TP - \fB\-h\fR, \fB\-\-help\fR -@@ -75,10 +79,18 @@ In node mode, only a single interface is supported in each call to iscsiadm. - .IP - This option is valid for discovery, node and iface mode. - -+.TP -+\fB\-k\fR, \fB\-\-killiscsid=\fI[priority]\fR -+Currently priority must be zero. This will immediately stop all iscsid -+operations and shutdown iscsid. It does not logout any sessions. Running -+this command is the same as doing "killall iscsid". Neither should -+normally not be used, because if iscsid is doing error recovery or if there -+is an error while iscsid is not running, the system may not be able to recover. -+This command and iscsid's SIGTERM handling are experimental. - - .TP - \fB\-l\fR, \fB\-\-login\fR --For node mode, login to a specified record. For discovery mode, login to -+For node and fw mode, login to a specified record. For discovery mode, login to - all discovered targets. - .IP - This option is only valid for discovery and node modes. -@@ -95,11 +107,12 @@ for session mode). - .TP - \fB\-m, \-\-mode \fIop\fR - specify the mode. \fIop\fR --must be one of \fIdiscovery\fR, \fInode\fR, or \fIsession\fR. -+must be one of \fIdiscovery\fR, \fInode\fR, \fIfw\fR or \fIsession\fR. - .IP - If no other options are specified: for \fIdiscovery\fR and \fInode\fR, all - of their respective records are displayed; for \fIsession\fR, all active --sessions and connections are displayed. -+sessions and connections are displayed; for \fIfw\fR, all boot firmware -+values are displayed. - - .TP - \fB\-n\fR, \fB\-\-name=\fIname\fR -@@ -112,16 +125,14 @@ operator. - Specifies a database operator \fIop\fR. \fIop\fR must be one of - \fInew\fR, \fIdelete\fR, \fIupdate\fR or \fIshow\fR. - .IP --This option is only valid for all modes, but delete should not be used on a running session. -+This option is valid for all modes except fw. Delete should not be used on a running session. If it is iscsiadm will stop the session and then delete the -+record. - .IP --\fInew\fR is currently valid only for node, session and iface mode. It creates --a new database record for a given \fIportal\fR (IP address and port number). -+\fInew\fR creates a new database record for a given \fIportal\fR (IP address and port number). In discovery mode, iscsiadm will create new records for portals returned by the target. - .IP --\fIdelete\fR deletes a specified \fIrecid\fR. -+\fIdelete\fR deletes a specified \fIrecid\fR. In discovery node, iscsiadm will delete records for portals that are no longer returned. - .IP --\fIupdate\fR is currently valid only for node, session, and iface mode. --It updates a specified --\fIrecid\fR with \fIname\fR to the specified \fIvalue\fR. -+\fIupdate\fR will update the \fIrecid\fR with \fIname\fR to the specified \fIvalue\fR. In discovery node the \fIrecid\fR, \fIname\fR and \fIvalue\fR arguments are not needed. The update operation will operate on the portals returned by the target, and will update the record with info from the config file and command line. - .IP - \fIshow\fR is the default behaviour for node, discovery and iface mode. It is - also used when there are no commands passed into session mode and a running -@@ -181,8 +192,8 @@ This option is only valid for node and session mode. - .TP - \fB\-t\fR, \fB\-\-type=\fItype\fR - \fItype\fR must be \fIsendtargets\fR (or abbreviated as \fIst\fR), --\fIslp\fR, or \fIisns\fR. Currently only sendtargets and iSNS is supported, --see the DISCOVERY TYPES section. -+\fIslp\fR, \fIisns\fR or \fIfw\fR. Currently only sendtargets, fw, and -+iSNS is supported, see the DISCOVERY TYPES section. - .IP - This option is only valid for discovery mode. - -@@ -238,6 +249,22 @@ and iscsiadm must be run in discovery mode with the "isns" discovery type. - iSNS support in open-iscsi is experimental. The iscsid.conf settings, - iscsiadm syntax and node DB layout may change. - -+.TP -+.B -+fw -+Several NICs and systems contain a mini iSCSI initiator which can be used -+for boot. To get the values used for boot the fw option can be used. -+Doing fw discovery, does not store persistent records in the node or -+discovery DB, because the values are stored in the system's or NIC's -+resource. -+ -+Performing fw discovery will print the portals, like with other discovery -+methods. To see other settings like CHAP values and initiator settings, -+like you would in node mode, run "iscsiadm -m fw". -+ -+fw support in open-iscsi is experimental. The settings and iscsiadm -+syntax and output format may change. -+ - .P - iscsiadm supports the - .B -diff --git a/doc/iscsid.8 b/doc/iscsid.8 -index e545f4f..1dfa1e5 100644 ---- a/doc/iscsid.8 -+++ b/doc/iscsid.8 -@@ -26,8 +26,8 @@ run - .B iscsid - in the foreground. - .TP --.BI [-d|--debug] --print debugging information. -+.BI [-d|--debug=]\fIdebug_level\fP -+print debugging information. Valid values for debug_level are 0 to 8. - .TP - .BI [-u|--uid=]\fIuid\fP - run under user ID \fIuid\fR (default is the current user ID) -diff --git a/etc/iface.example b/etc/iface.example -index 5587a73..7684aea 100644 ---- a/etc/iface.example -+++ b/etc/iface.example -@@ -4,22 +4,41 @@ - # There must be a seperate iscsi interface config file for each NIC, network - # interface or port or iscsi HBA you want to bind sessions to. - # --# For hardware/offload iscsi, this is created for you when you run iscsiadm. -+# For hardware iscsi, this is created for you when you run iscsiadm. - # For software iscsi, you must define these files yourself. - # - -+# REQUIRED: iface.transport_name -+# - # Set the iscsi transport/driver to use for the iface by setting - # iface.transport_name - # example: - # iface.transport_name = tcp - --# This values is required and valid values for iface.transport_name are: -+# This value is required and valid values for iface.transport_name are: - # - tcp (Software iSCSI over TCP/IP) - # - iser (Software iSCSI over infinniband - # - qla4xxx (Qlogic QLA4XXX HBAs) -- -- --# __One__ of the following values are required for the binding. -+# -+# -+#OPTIONAL: iface.initiatorname -+# To use a initiator name other than the one set in -+# /etc/iscsi/initiatorname.iscsi for normal sessions set the -+# iface.initiatorname. This is only used for normal sessions. -+# For discovery sessions the /etc/iscsi/initiatorname.iscsi value -+# is used. -+# -+# iface.initiatorname = iqn.2003-04.com.fedora:test -+# -+# -+# REQUIRED to be able to bind a session to a network device: -+# [iface.net_ifacename | iface.hwaddress] -+# -+# OPTIONAL if you are creating ifaces so you can create multiple sessions -+# using the default behavior where the network layer selects the device. -+# -+# __One__ of the following values are required for binding a session -+# to a specific nic/netdevice. - # - # To bind by network interface name (example: eth0, eth2:2, eth1.3) - # set iface.net_ifacename -diff --git a/etc/initd/boot.suse b/etc/initd/boot.suse -new file mode 100644 -index 0000000..df64e21 ---- /dev/null -+++ b/etc/initd/boot.suse -@@ -0,0 +1,82 @@ -+#!/bin/bash -+# -+# /etc/init.d/iscsi -+# -+### BEGIN INIT INFO -+# Provides: iscsiboot -+# Required-Start: boot.proc -+# Should-Start: -+# Required-Stop: -+# Should-Stop: -+# Default-Start: B -+# Default-Stop: -+# Short-Description: Starts the iSCSI initiator daemon -+# -+### END INIT INFO -+ -+ISCSIADM=/sbin/iscsiadm -+PID_FILE=/var/run/iscsi.pid -+CONFIG_FILE=/etc/iscsid.conf -+DAEMON=/sbin/iscsid -+ARGS="-c $CONFIG_FILE -p $PID_FILE" -+ -+# Source LSB init functions -+. /etc/rc.status -+ -+# -+# This service is run right after booting. So all activated targets -+# must be enabled during mkinitrd run and thus should not be removed -+# when the open-iscsi service is stopped. -+# -+iscsi_mark_root_nodes() -+{ -+ $ISCSIADM -m session 2> /dev/null | while read t num i target ; do -+ ip=${i%%:*} -+ STARTUP=`$ISCSIADM -m node -p $ip -T $target | grep "node.conn\[0\].startup" | cut -d' ' -f3` -+ if [ "$STARTUP" != "onboot" ] ; then -+ $ISCSIADM -m node -p $ip -T $target -o update -n node.conn[0].startup -v onboot -+ fi -+ done -+} -+ -+# Reset status of this service -+rc_reset -+ -+# We only need to start this for root on iSCSI -+if ! grep -q iscsi_tcp /proc/modules ; then -+ rc_failed 6 -+ rc_exit -+fi -+ -+case "$1" in -+ start) -+ [ ! -d /var/lib/open-iscsi ] && mkdir -p /var/lib/open-iscsi -+ echo -n "Starting iSCSI initiator for the root device: " -+ startproc $DAEMON $ARGS -+ rc_status -v -+ iscsi_mark_root_nodes -+ ;; -+ stop) -+ rc_failed 0 -+ ;; -+ status) -+ echo -n "Checking for iSCSI initiator service: " -+ if checkproc $DAEMON ; then -+ rc_status -v -+ else -+ rc_failed 3 -+ rc_status -v -+ fi -+ ;; -+ restart) -+ $0 stop -+ sleep 1 -+ $0 start -+ ;; -+ *) -+ echo "Usage: $0 {start|stop|status|restart}" -+ exit 1 -+ ;; -+esac -+rc_exit -+ -diff --git a/etc/initd/initd.debian b/etc/initd/initd.debian -index 9e9f134..c0dfd1e 100644 ---- a/etc/initd/initd.debian -+++ b/etc/initd/initd.debian -@@ -54,9 +54,16 @@ stop() { - log_daemon_msg "Stopping iSCSI initiator service" - start-stop-daemon --stop --quiet --pidfile $PIDFILE --exec $DAEMON - rm -f $PIDFILE -+ status=0 - modprobe -r ib_iser 2>/dev/null -+ if [ "$?" -ne "0" -a "$?" -ne "1" ]; then -+ status=1 -+ fi - modprobe -r iscsi_tcp 2>/dev/null -- log_end_msg 0 -+ if [ "$?" -ne "0" -a "$?" -ne "1" ]; then -+ status=1 -+ fi -+ log_end_msg $status - } - - restart() { -diff --git a/etc/initd/initd.redhat b/etc/initd/initd.redhat -index c591534..d68f135 100644 ---- a/etc/initd/initd.redhat -+++ b/etc/initd/initd.redhat -@@ -39,13 +39,23 @@ stop() - echo -n $"Stopping iSCSI initiator service: " - sync - iscsiadm -m node --logoutall=all -- pkill -KILL iscsid -+ killproc iscsid - rm -f /var/run/iscsid.pid -- #killproc iscsid - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/open-iscsi -+ status=0 - modprobe -r iscsi_tcp 2>/dev/null -+ if [ "$?" -ne "0" -a "$?" -ne "1" ]; then -+ status=1 -+ fi - modprobe -r ib_iser 2>/dev/null -- success -+ if [ "$?" -ne "0" -a "$?" -ne "1" ]; then -+ status=1 -+ fi -+ if [ "$status" -eq "0" ]; then -+ success -+ else -+ failure -+ fi - echo - - } -@@ -73,7 +83,7 @@ case "$1" in - RETVAL=$? - ;; - condrestart) -- [ -f /var/lock/subsys/iscsi ] && restart -+ [ -f /var/lock/subsys/open-iscsi ] && restart - ;; - *) - echo $"Usage: $0 {start|stop|restart|status|condrestart}" -diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse -index 163479e..d8b91cc 100644 ---- a/etc/initd/initd.suse -+++ b/etc/initd/initd.suse -@@ -5,7 +5,7 @@ - ### BEGIN INIT INFO - # Provides: iscsi - # Required-Start: $network --# Should-Start: -+# Should-Start: iscsitarget - # Required-Stop: - # Should-Stop: - # Default-Start: 3 5 -@@ -29,25 +29,65 @@ rc_reset - iscsi_login_all_nodes() - { - echo -n "Setting up iSCSI targets: " -- $ISCSIADM -m node --loginall=automatic -+ $ISCSIADM -m node --loginall=automatic 2> /dev/null -+ if [ $? == 19 ] ; then -+ rc_failed 6 -+ fi - rc_status -v - } - - iscsi_logout_all_nodes() - { -- # Logout from all active sessions -- if $ISCSIADM -m node --logoutall=all ; then -- rc_status -v -- else -- RETVAL=$? -+ echo -n "Closing all iSCSI connections: " -+ # Logout from all sessions marked automatic -+ if ! $ISCSIADM -m node --logoutall=automatic 2> /dev/null; then -+ if [ $? == 19 ] ; then -+ RETVAL=6 -+ else -+ RETVAL=1 -+ fi - rc_failed $RETVAL - fi -+ rc_status -v - - # Not sure whether this is still needed - sleep 1 - return ${RETVAL:-0} - } - -+iscsi_umount_all_luns() -+{ -+ local d m dev p s -+ -+ cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do -+ if [ "$m" = "/" ] ; then -+ continue; -+ fi -+ if [ -L "$d" ] ; then -+ d=$(readlink -f $d) -+ fi -+ dev=${d##/dev} -+ -+ if [ "${dev##/sd}" = "$dev" ] ; then -+ continue; -+ fi -+ p="/sys/block${dev%%[0-9]*}" -+ -+ if [ ! -d ${p} ] && [ ! -d ${p}/device ] ; then -+ continue; -+ fi -+ -+ s=$(cd -P ${p}/device && echo $PWD) -+ -+ case "$s" in -+ */session[0-9]*/*) -+ # This is an iSCSI device -+ umount "$m" -+ ;; -+ esac -+ done -+} -+ - iscsi_list_all_nodes() - { - # Check for active sessions -@@ -61,6 +101,38 @@ iscsi_list_all_nodes() - done - } - -+iscsi_discover_all_targets() -+{ -+ # Strip off any existing ID information -+ RAW_NODE_LIST=`iscsiadm -m node | sed -nre 's/^(\[[0-9a-f]*\] )?(.*)$/\2/p'` -+ # Obtain IPv4 list -+ IPV4_NODE_LIST=`echo "$RAW_NODE_LIST" | sed -nre 's/^([0-9]{1,3}(\.[0-9]{1,3}){3}):[^: ]* (.*)$/\1 \3/p'` -+ # Now obtain IPv6 list -+ IPV6_NODE_LIST=`echo "$RAW_NODE_LIST" | sed -nre 's/^([0-9a-f]{1,4}(:[0-9a-f]{0,4}){6}:[0-9a-f]{1,4}):[^: ]* (.*)$/\1 \3/p'` -+ -+ DISC_TARGETS="" -+ while read NODE_ADDR NODE_NAME; do -+ [ -z "$NODE_ADDR" -a -z "$NODE_NAME" ] && continue -+ NODE_ATTRS=`iscsiadm -m node -p "$NODE_ADDR" -T "$NODE_NAME"` -+ NODE_STATUS=`echo "$NODE_ATTRS" | sed -nre 's/^.*node\.conn\[0\]\.startup = ([a-z]*).*$/\1/p'` -+ -+ if [ "$NODE_STATUS" == 'automatic' ]; then -+ DISC_TARGETS=`echo "$DISC_TARGETS" | sed -re '/'"$NODE_ADDR"'/!{s/(.*)/\1 '"$NODE_ADDR"'/}'` -+ fi -+ done < <(echo "$IPV4_NODE_LIST"; echo "$IPV6_NODE_LIST") -+ -+ for TARGET_ADDR in $DISC_TARGETS; do -+ echo -n "Attempting discovery on target at ${TARGET_ADDR}: " -+ iscsiadm -m discovery -t st -p "$TARGET_ADDR" > /dev/null 2>&1 -+ if [ "$?" -ne 0 ]; then -+ rc_failed 1 -+ rc_status -v -+ return 1 -+ fi -+ rc_status -v -+ done -+} -+ - case "$1" in - start) - [ ! -d /var/lib/iscsi ] && mkdir -p /var/lib/iscsi -@@ -75,10 +147,15 @@ case "$1" in - rc_status -v - fi - if [ "$RETVAL" == "0" ]; then -+ iscsi_discover_all_targets -+ RETVAL=$? -+ fi -+ if [ "$RETVAL" == "0" ]; then - iscsi_login_all_nodes - fi - ;; - stop) -+ iscsi_umount_all_luns - if iscsi_logout_all_nodes ; then - killproc -KILL $DAEMON - RETVAL=$? -@@ -88,11 +165,18 @@ case "$1" in - echo -n "Stopping iSCSI initiator service: " - if [ "$RETVAL" == "0" ]; then - rm -f $PID_FILE -+ status=0 - modprobe -r iscsi_tcp -+ if [ "$?" -ne "0" -a "$?" -ne "1" ]; then -+ status=1 -+ fi - modprobe -q -r ib_iser -- rc_failed 0 -+ if [ "$?" -ne "0" -a "$?" -ne "1" ]; then -+ status=1 -+ fi -+ rc_failed $status - else -- rc_failed 1 -+ rc_failed $RETVAL - fi - rc_status -v - ;; -diff --git a/etc/iscsid.conf b/etc/iscsid.conf -index 94dd758..f2691ee 100644 ---- a/etc/iscsid.conf -+++ b/etc/iscsid.conf -@@ -65,7 +65,10 @@ node.startup = manual - # ******** - # Timeouts - # ******** -- -+# -+# See the iSCSI REAME's Advanced Configuration section for tips -+# on setting timeouts when using multipath or doing root over iSCSI. -+# - # To specify the length of time to wait for session re-establishment - # before failing SCSI commands back to the application when running - # the Linux SCSI Layer error handler, edit the line. -@@ -80,22 +83,29 @@ node.conn[0].timeo.login_timeout = 15 - # The value is in seconds and the default is 15 seconds. - node.conn[0].timeo.logout_timeout = 15 - --# To specify the intervale to send iSCSI Nop-outs as pings --# to the target edit this the line. --# The value is in seconds and the default is 10 seconds. --node.conn[0].timeo.noop_out_interval = 10 -+# Time interval to wait for on connection before sending a ping. -+node.conn[0].timeo.noop_out_interval = 5 - - # To specify the time to wait for a Nop-out response before failing - # the connection, edit this line. Failing the connection will - # cause IO to be failed back to the SCSI layer. If using dm-multipath - # this will cause the IO to be failed to the multipath layer. -+node.conn[0].timeo.noop_out_timeout = 5 -+ -+# To specify the time to wait for abort response before -+# failing the operation and trying a logical unit reset edit the line. - # The value is in seconds and the default is 15 seconds. --node.conn[0].timeo.noop_out_timeout = 15 -+node.session.err_timeo.abort_timeout = 15 - -+# To specify the time to wait for a logical unit response -+# before failing the operation and trying session re-establishment -+# edit the line. -+# The value is in seconds and the default is 30 seconds. -+node.session.err_timeo.lu_reset_timeout = 20 - - #****** - # Retry --#******* -+#****** - - # To speficy the number of times iscsiadm should retry a login - # to the target when we first login, modify the following line. -@@ -106,6 +116,19 @@ node.conn[0].timeo.noop_out_timeout = 15 - # during startup or if the network is not ready at that time. - node.session.initial_login_retry_max = 4 - -+################################ -+# session and device queue depth -+################################ -+ -+# To control how many commands the session will queue set -+# node.session.cmds_max to an integer between 2 and 2048 that is also -+# a power of 2. The default is 128. -+node.session.cmds_max = 128 -+ -+# To control the device's queue depth set node.session.queue_depth -+# to a value between 1 and 128. The default is 32. -+node.session.queue_depth = 32 -+ - #*************** - # iSCSI settings - #*************** -@@ -185,6 +208,21 @@ discovery.sendtargets.iscsi.MaxRecvDataSegmentLength = 32768 - #node.conn[0].iscsi.HeaderDigest = None - #node.conn[0].iscsi.DataDigest = None - # --# The default is to never use DataDigests and to allow the target to control --# the setting of the HeaderDigest checking with the initiator requesting --# a preference of disabling the checking. -+# The default is to never use DataDigests or HeaderDigests. -+# -+ -+ -+#************ -+# Workarounds -+#************ -+ -+# Some targets like IET prefer after an initiator has sent a task -+# management function like an ABORT TASK or LOGICAL UNIT RESET, that -+# it does not respond to PDUs like R2Ts. To enable this behavior uncomment -+# the following line (The default behavior is Yes): -+node.session.iscsi.FastAbort = Yes -+ -+# Some targets like Equalogic prefer that after an initiator has sent -+# a task management function like an ABORT TASK or LOGICAL UNIT RESET, that -+# it continue to respond to R2Ts. To enable this uncomment this line -+# node.session.iscsi.FastAbort = No -diff --git a/include/fw_context.h b/include/fw_context.h -new file mode 100644 -index 0000000..47ac6ae ---- /dev/null -+++ b/include/fw_context.h -@@ -0,0 +1,46 @@ -+/* -+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -+ * -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * "Prasanna Mumbai" -+ * -+ */ -+#ifndef FWPARAM_CONTEXT_H_ -+#define FWPARAM_CONTEXT_H_ -+ -+struct boot_context { -+#define IQNSZ (223+1) -+ int target_port; -+ char initiatorname[IQNSZ]; -+ char targetname[IQNSZ]; -+ char target_ipaddr[32]; -+ char chap_name[127]; -+ char chap_password[16]; -+ char chap_name_in[127]; -+ char chap_password_in[16]; -+ char iface[42]; -+ char mac[18]; -+ char ipaddr[18]; -+ char mask[18]; -+ char lun[17]; -+ char vlan[15]; -+ char isid[10]; -+}; -+ -+extern int fw_get_entry(struct boot_context *context, const char *filepath); -+extern void fw_print_entry(struct boot_context *context); -+ -+#endif /* FWPARAM_CONTEXT_H_ */ -diff --git a/include/iscsi_if.h b/include/iscsi_if.h -index 8458425..19df961 100644 ---- a/include/iscsi_if.h -+++ b/include/iscsi_if.h -@@ -21,7 +21,7 @@ - #ifndef ISCSI_IF_H - #define ISCSI_IF_H - --#include -+#include "iscsi_proto.h" - - #define UEVENT_BASE 10 - #define KEVENT_BASE 100 -@@ -49,12 +49,16 @@ enum iscsi_uevent_e { - - ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15, - ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16, -+ ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17, - - /* up events */ - ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, - ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, - ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3, - ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, -+ ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5, -+ ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6, -+ ISCSI_KEVENT_TRANS_ERROR = KEVENT_BASE + 7, - }; - - enum iscsi_tgt_dscvr { -@@ -156,6 +160,10 @@ struct iscsi_uevent { - uint32_t sid; - uint32_t cid; - } c_conn_ret; -+ struct msg_unbind_session { -+ uint32_t sid; -+ uint32_t host_no; -+ } unbind_session; - struct msg_recv_req { - uint32_t sid; - uint32_t cid; -@@ -236,36 +244,54 @@ enum iscsi_param { - ISCSI_PARAM_PASSWORD, - ISCSI_PARAM_PASSWORD_IN, - -+ ISCSI_PARAM_FAST_ABORT, -+ ISCSI_PARAM_ABORT_TMO, -+ ISCSI_PARAM_LU_RESET_TMO, -+ ISCSI_PARAM_HOST_RESET_TMO, -+ -+ ISCSI_PARAM_PING_TMO, -+ ISCSI_PARAM_RECV_TMO, -+ -+ ISCSI_PARAM_IFACE_NAME, -+ ISCSI_PARAM_ISID, - /* must always be last */ - ISCSI_PARAM_MAX, - }; - --#define ISCSI_MAX_RECV_DLENGTH (1 << ISCSI_PARAM_MAX_RECV_DLENGTH) --#define ISCSI_MAX_XMIT_DLENGTH (1 << ISCSI_PARAM_MAX_XMIT_DLENGTH) --#define ISCSI_HDRDGST_EN (1 << ISCSI_PARAM_HDRDGST_EN) --#define ISCSI_DATADGST_EN (1 << ISCSI_PARAM_DATADGST_EN) --#define ISCSI_INITIAL_R2T_EN (1 << ISCSI_PARAM_INITIAL_R2T_EN) --#define ISCSI_MAX_R2T (1 << ISCSI_PARAM_MAX_R2T) --#define ISCSI_IMM_DATA_EN (1 << ISCSI_PARAM_IMM_DATA_EN) --#define ISCSI_FIRST_BURST (1 << ISCSI_PARAM_FIRST_BURST) --#define ISCSI_MAX_BURST (1 << ISCSI_PARAM_MAX_BURST) --#define ISCSI_PDU_INORDER_EN (1 << ISCSI_PARAM_PDU_INORDER_EN) --#define ISCSI_DATASEQ_INORDER_EN (1 << ISCSI_PARAM_DATASEQ_INORDER_EN) --#define ISCSI_ERL (1 << ISCSI_PARAM_ERL) --#define ISCSI_IFMARKER_EN (1 << ISCSI_PARAM_IFMARKER_EN) --#define ISCSI_OFMARKER_EN (1 << ISCSI_PARAM_OFMARKER_EN) --#define ISCSI_EXP_STATSN (1 << ISCSI_PARAM_EXP_STATSN) --#define ISCSI_TARGET_NAME (1 << ISCSI_PARAM_TARGET_NAME) --#define ISCSI_TPGT (1 << ISCSI_PARAM_TPGT) --#define ISCSI_PERSISTENT_ADDRESS (1 << ISCSI_PARAM_PERSISTENT_ADDRESS) --#define ISCSI_PERSISTENT_PORT (1 << ISCSI_PARAM_PERSISTENT_PORT) --#define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO) --#define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT) --#define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS) --#define ISCSI_USERNAME (1 << ISCSI_PARAM_USERNAME) --#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN) --#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD) --#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN) -+#define ISCSI_MAX_RECV_DLENGTH (1ULL << ISCSI_PARAM_MAX_RECV_DLENGTH) -+#define ISCSI_MAX_XMIT_DLENGTH (1ULL << ISCSI_PARAM_MAX_XMIT_DLENGTH) -+#define ISCSI_HDRDGST_EN (1ULL << ISCSI_PARAM_HDRDGST_EN) -+#define ISCSI_DATADGST_EN (1ULL << ISCSI_PARAM_DATADGST_EN) -+#define ISCSI_INITIAL_R2T_EN (1ULL << ISCSI_PARAM_INITIAL_R2T_EN) -+#define ISCSI_MAX_R2T (1ULL << ISCSI_PARAM_MAX_R2T) -+#define ISCSI_IMM_DATA_EN (1ULL << ISCSI_PARAM_IMM_DATA_EN) -+#define ISCSI_FIRST_BURST (1ULL << ISCSI_PARAM_FIRST_BURST) -+#define ISCSI_MAX_BURST (1ULL << ISCSI_PARAM_MAX_BURST) -+#define ISCSI_PDU_INORDER_EN (1ULL << ISCSI_PARAM_PDU_INORDER_EN) -+#define ISCSI_DATASEQ_INORDER_EN (1ULL << ISCSI_PARAM_DATASEQ_INORDER_EN) -+#define ISCSI_ERL (1ULL << ISCSI_PARAM_ERL) -+#define ISCSI_IFMARKER_EN (1ULL << ISCSI_PARAM_IFMARKER_EN) -+#define ISCSI_OFMARKER_EN (1ULL << ISCSI_PARAM_OFMARKER_EN) -+#define ISCSI_EXP_STATSN (1ULL << ISCSI_PARAM_EXP_STATSN) -+#define ISCSI_TARGET_NAME (1ULL << ISCSI_PARAM_TARGET_NAME) -+#define ISCSI_TPGT (1ULL << ISCSI_PARAM_TPGT) -+#define ISCSI_PERSISTENT_ADDRESS (1ULL << ISCSI_PARAM_PERSISTENT_ADDRESS) -+#define ISCSI_PERSISTENT_PORT (1ULL << ISCSI_PARAM_PERSISTENT_PORT) -+#define ISCSI_SESS_RECOVERY_TMO (1ULL << ISCSI_PARAM_SESS_RECOVERY_TMO) -+#define ISCSI_CONN_PORT (1ULL << ISCSI_PARAM_CONN_PORT) -+#define ISCSI_CONN_ADDRESS (1ULL << ISCSI_PARAM_CONN_ADDRESS) -+#define ISCSI_USERNAME (1ULL << ISCSI_PARAM_USERNAME) -+#define ISCSI_USERNAME_IN (1ULL << ISCSI_PARAM_USERNAME_IN) -+#define ISCSI_PASSWORD (1ULL << ISCSI_PARAM_PASSWORD) -+#define ISCSI_PASSWORD_IN (1ULL << ISCSI_PARAM_PASSWORD_IN) -+#define ISCSI_FAST_ABORT (1ULL << ISCSI_PARAM_FAST_ABORT) -+#define ISCSI_ABORT_TMO (1ULL << ISCSI_PARAM_ABORT_TMO) -+#define ISCSI_LU_RESET_TMO (1ULL << ISCSI_PARAM_LU_RESET_TMO) -+#define ISCSI_HOST_RESET_TMO (1ULL << ISCSI_PARAM_HOST_RESET_TMO) -+#define ISCSI_PING_TMO (1ULL << ISCSI_PARAM_PING_TMO) -+#define ISCSI_RECV_TMO (1ULL << ISCSI_PARAM_RECV_TMO) -+#define ISCSI_IFACE_NAME (1ULL << ISCSI_PARAM_IFACE_NAME) -+#define ISCSI_ISID (1ULL << ISCSI_PARAM_ISID) - - /* iSCSI HBA params */ - enum iscsi_host_param { -@@ -276,10 +302,10 @@ enum iscsi_host_param { - ISCSI_HOST_PARAM_MAX, - }; - --#define ISCSI_HOST_HWADDRESS (1 << ISCSI_HOST_PARAM_HWADDRESS) --#define ISCSI_HOST_INITIATOR_NAME (1 << ISCSI_HOST_PARAM_INITIATOR_NAME) --#define ISCSI_HOST_NETDEV_NAME (1 << ISCSI_HOST_PARAM_NETDEV_NAME) --#define ISCSI_HOST_IPADDRESS (1 << ISCSI_HOST_PARAM_IPADDRESS) -+#define ISCSI_HOST_HWADDRESS (1ULL << ISCSI_HOST_PARAM_HWADDRESS) -+#define ISCSI_HOST_INITIATOR_NAME (1ULL << ISCSI_HOST_PARAM_INITIATOR_NAME) -+#define ISCSI_HOST_NETDEV_NAME (1ULL << ISCSI_HOST_PARAM_NETDEV_NAME) -+#define ISCSI_HOST_IPADDRESS (1ULL << ISCSI_HOST_PARAM_IPADDRESS) - - #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle) - #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr) -@@ -311,6 +337,7 @@ enum iscsi_host_param { - * These flags describes reason of stop_conn() call - */ - #define STOP_CONN_TERM 0x1 -+#define STOP_CONN_SUSPEND 0x2 - #define STOP_CONN_RECOVER 0x3 - - #define ISCSI_STATS_CUSTOM_MAX 32 -diff --git a/include/iscsi_proto.h b/include/iscsi_proto.h -index 059fd74..916c02c 100644 ---- a/include/iscsi_proto.h -+++ b/include/iscsi_proto.h -@@ -21,13 +21,15 @@ - #ifndef ISCSI_PROTO_H - #define ISCSI_PROTO_H - -+#include -+ - #define ISCSI_DRAFT20_VERSION 0x00 - - /* default iSCSI listen port for incoming connections */ - #define ISCSI_LISTEN_PORT 3260 - - /* Padding word length */ --#define PAD_WORD_LEN 4 -+#define ISCSI_PAD_LEN 4 - - /* - * useful common(control and data pathes) macro -@@ -58,8 +60,8 @@ - /* initiator tags; opaque for target */ - typedef uint32_t __bitwise__ itt_t; - /* below makes sense only for initiator that created this tag */ --#define build_itt(itt, id, age) ((__force itt_t)\ -- ((itt) | ((id) << ISCSI_CID_SHIFT) | ((age) << ISCSI_AGE_SHIFT))) -+#define build_itt(itt, age) ((__force itt_t)\ -+ ((itt) | ((age) << ISCSI_AGE_SHIFT))) - #define get_itt(itt) ((__force uint32_t)(itt_t)(itt) & ISCSI_ITT_MASK) - #define RESERVED_ITT ((__force itt_t)0xffffffff) - -@@ -162,6 +164,14 @@ struct iscsi_rlength_ahdr { - __be32 read_length; - }; - -+/* Extended CDB AHS */ -+struct iscsi_ecdb_ahdr { -+ __be16 ahslength; /* CDB length - 15, including reserved byte */ -+ uint8_t ahstype; -+ uint8_t reserved; -+ uint8_t ecdb[260 - 16]; /* 4-byte aligned extended CDB spillover */ -+}; -+ - /* SCSI Response Header */ - struct iscsi_cmd_rsp { - uint8_t opcode; -@@ -615,6 +625,8 @@ struct iscsi_reject { - #define ISCSI_MIN_MAX_BURST_LEN 512 - #define ISCSI_MAX_MAX_BURST_LEN 16777215 - -+#define ISCSI_DEF_TIME2WAIT 2 -+ - /************************* RFC 3720 End *****************************/ - - #endif /* ISCSI_PROTO_H */ -diff --git a/kernel/2.6.13-compat.patch b/kernel/2.6.13-compat.patch -deleted file mode 100644 -index e319924..0000000 ---- a/kernel/2.6.13-compat.patch -+++ /dev/null -@@ -1,97 +0,0 @@ --diff -Naurp open-iscsi.diffbase/kernel/iscsi_compat.h open-iscsi.test/kernel/iscsi_compat.h ----- open-iscsi.diffbase/kernel/iscsi_compat.h 1969-12-31 18:00:00.000000000 -0600 --+++ open-iscsi.test/kernel/iscsi_compat.h 2006-01-16 16:25:56.000000000 -0600 --@@ -0,0 +1,55 @@ --+/* --+ * compat crap for older kernels. --+ * do not send to linux-scsi --+ */ --+#include --+#include --+#include --+ --+#ifndef DEFINE_MUTEX --+ --+/* mutex changes from 2.6.16-rc1 and up */ --+#define DEFINE_MUTEX DECLARE_MUTEX --+#define mutex_lock down --+#define mutex_unlock up --+#define mutex semaphore --+#define mutex_init init_MUTEX --+#endif --+ --+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12) --+ --+void int_to_scsilun(unsigned int lun, struct scsi_lun *scsilun) --+{ --+ int i; --+ --+ memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun)); --+ --+ for (i = 0; i < sizeof(lun); i += 2) { --+ scsilun->scsi_lun[i] = (lun >> 8) & 0xFF; --+ scsilun->scsi_lun[i+1] = lun & 0xFF; --+ lun = lun >> 16; --+ } --+} --+ --+#define __nlmsg_put(skb, daemon_pid, seq, type, len, flags) \ --+ __nlmsg_put(skb, daemon_pid, 0, 0, len) --+ --+#endif --+ --+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) --+ --+#define netlink_kernel_create(uint, groups, input, mod) \ --+ netlink_kernel_create(uint, input) --+ --+#define gfp_t unsigned --+ --+void *kzalloc(size_t size, gfp_t flags) --+{ --+ void *ret = kmalloc(size, flags); --+ if (ret) --+ memset(ret, 0, size); --+ return ret; --+} --+ --+#endif --+ --diff -Naurp open-iscsi.diffbase/kernel/iscsi_tcp.c open-iscsi.test/kernel/iscsi_tcp.c ----- open-iscsi.diffbase/kernel/iscsi_tcp.c 2006-01-16 15:38:52.000000000 -0600 --+++ open-iscsi.test/kernel/iscsi_tcp.c 2006-01-16 16:10:42.000000000 -0600 --@@ -33,7 +33,6 @@ -- #include -- #include -- #include ---#include -- #include -- #include -- #include --@@ -44,6 +43,7 @@ -- #include -- #include "scsi_transport_iscsi.h" -- --+#include "iscsi_compat.h" -- #include "iscsi_tcp.h" -- -- MODULE_AUTHOR("Dmitry Yusupov , " --diff -Naurp open-iscsi.diffbase/kernel/scsi_transport_iscsi.c open-iscsi.test/kernel/scsi_transport_iscsi.c ----- open-iscsi.diffbase/kernel/scsi_transport_iscsi.c 2006-01-16 15:38:52.000000000 -0600 --+++ open-iscsi.test/kernel/scsi_transport_iscsi.c 2006-01-16 15:54:47.000000000 -0600 --@@ -22,7 +22,6 @@ -- */ -- #include -- #include ---#include -- #include -- #include -- #include --@@ -30,6 +29,7 @@ -- #include -- #include "scsi_transport_iscsi.h" -- #include "iscsi_if.h" --+#include "iscsi_compat.h" -- -- #define ISCSI_SESSION_ATTRS 8 -- #define ISCSI_CONN_ATTRS 6 -diff --git a/kernel/2.6.14-19_compat.patch b/kernel/2.6.14-19_compat.patch -new file mode 100644 -index 0000000..4d66655 ---- /dev/null -+++ b/kernel/2.6.14-19_compat.patch -@@ -0,0 +1,589 @@ -+diff --git a/iscsi_compat.h b/iscsi_compat.h -+new file mode 100644 -+index 0000000..965157a -+--- /dev/null -++++ b/iscsi_compat.h -+@@ -0,0 +1,192 @@ -++#include -++#include -++#include -++#include -++ -++#ifndef ISCSI_COMPAT -++#define ISCSI_COMPAT -++ -++#ifndef SCAN_WILD_CARD -++#define SCAN_WILD_CARD ~0 -++#endif -++ -++#ifndef NIPQUAD_FMT -++#define NIPQUAD_FMT "%u.%u.%u.%u" -++#endif -++ -++#ifndef NIP6_FMT -++#define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" -++#endif -++ -++#ifndef DEFINE_MUTEX -++ -++/* mutex changes from 2.6.16-rc1 and up */ -++#define DEFINE_MUTEX DECLARE_MUTEX -++#define mutex_lock down -++#define mutex_unlock up -++#define mutex semaphore -++#define mutex_init init_MUTEX -++#endif -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12) -++ -++void int_to_scsilun(unsigned int lun, struct scsi_lun *scsilun) -++{ -++ int i; -++ -++ memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun)); -++ -++ for (i = 0; i < sizeof(lun); i += 2) { -++ scsilun->scsi_lun[i] = (lun >> 8) & 0xFF; -++ scsilun->scsi_lun[i+1] = lun & 0xFF; -++ lun = lun >> 16; -++ } -++} -++ -++#define __nlmsg_put(skb, daemon_pid, seq, type, len, flags) \ -++ __nlmsg_put(skb, daemon_pid, 0, 0, len) -++ -++#endif -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) -++ -++#define netlink_kernel_create(net, uint, groups, input, cb_mutex, mod) \ -++ netlink_kernel_create(uint, input) -++ -++#define gfp_t unsigned -++ -++void *kzalloc(size_t size, gfp_t flags) -++{ -++ void *ret = kmalloc(size, flags); -++ if (ret) -++ memset(ret, 0, size); -++} -++ -++#endif -++ -++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -++ -++#include "linux/crypto.h" -++ -++#define CRYPTO_ALG_ASYNC 0x00000080 -++struct hash_desc -++{ -++ struct crypto_tfm *tfm; -++ u32 flags; -++}; -++ -++static inline int crypto_hash_init(struct hash_desc *desc) -++{ -++ crypto_digest_init(desc->tfm); -++ return 0; -++} -++ -++static inline int crypto_hash_digest(struct hash_desc *desc, -++ struct scatterlist *sg, -++ unsigned int nbytes, u8 *out) -++{ -++ crypto_digest_digest(desc->tfm, sg, 1, out); -++ return nbytes; -++} -++ -++static inline int crypto_hash_update(struct hash_desc *desc, -++ struct scatterlist *sg, -++ unsigned int nbytes) -++{ -++ crypto_digest_update(desc->tfm, sg, 1); -++ return nbytes; -++} -++ -++static inline int crypto_hash_final(struct hash_desc *desc, u8 *out) -++{ -++ crypto_digest_final(desc->tfm, out); -++ return 0; -++} -++ -++static inline struct crypto_tfm *crypto_alloc_hash(const char *alg_name, -++ u32 type, u32 mask) -++{ -++ struct crypto_tfm *ret = crypto_alloc_tfm(alg_name ,type); -++ return ret ? ret : ERR_PTR(-ENOMEM); -++} -++ -++static inline void crypto_free_hash(struct crypto_tfm *tfm) -++{ -++ crypto_free_tfm(tfm); -++} -++ -++int kernel_getsockname(struct socket *sock, struct sockaddr *addr, -++ int *addrlen) -++{ -++ return sock->ops->getname(sock, addr, addrlen, 0); -++} -++ -++int kernel_getpeername(struct socket *sock, struct sockaddr *addr, -++ int *addrlen) -++{ -++ return sock->ops->getname(sock, addr, addrlen, 1); -++} -++ -++#endif -++ -++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -++ -++static inline int is_power_of_2(unsigned long n) -++{ -++ return (n != 0 && ((n & (n - 1)) == 0)); -++} -++#endif -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21) -++#define netlink_kernel_create(net, uint, groups, input, cb_mutex, mod) \ -++ netlink_kernel_create(uint, groups, input, mod) -++ -++#endif -++ -++static inline struct scatterlist *sg_next(struct scatterlist *sg) -++{ -++ if (!sg) { -++ BUG(); -++ return NULL; -++ } -++ return sg + 1; -++} -++ -++#define for_each_sg(sglist, sg, nr, __i) \ -++ for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) -++ -++#define sg_page(_sg) _sg->page -++ -++static inline void sg_set_page(struct scatterlist *sg, struct page *page, -++ unsigned int len, unsigned int offset) -++{ -++ sg->page = page; -++ sg->offset = offset; -++ sg->length = len; -++} -++ -++static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) -++{ -++ memset(sgl, 0, sizeof(*sgl) * nents); -++} -++ -++#define scsi_sg_count(cmd) ((cmd)->use_sg) -++#define scsi_sglist(cmd) ((struct scatterlist *)(cmd)->request_buffer) -++#define scsi_bufflen(cmd) ((cmd)->request_bufflen) -++ -++static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid) -++{ -++ cmd->resid = resid; -++} -++ -++static inline int scsi_get_resid(struct scsi_cmnd *cmd) -++{ -++ return cmd->resid; -++} -++ -++static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) -++{ -++ return (struct nlmsghdr *)skb->data; -++} -++ -++#endif -+diff --git a/iscsi_tcp.c b/iscsi_tcp.c -+index 0d21d87..16908b6 100644 -+--- a/iscsi_tcp.c -++++ b/iscsi_tcp.c -+@@ -43,6 +43,7 @@ -+ #include "scsi_transport_iscsi.h" -+ -+ #include "iscsi_tcp.h" -++#include "iscsi_compat.h" -+ -+ MODULE_AUTHOR("Dmitry Yusupov , " -+ "Alex Aizman "); -+@@ -422,6 +423,17 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment, -+ -+ debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n", -+ offset, size); -++ -++ /* -++ * older kernels could send use_sg=0 for commands like sgio -++ * or scsi-ml commands. -++ */ -++ if (!sg_count) { -++ iscsi_segment_init_linear(segment, (void *)sg_list + offset, -++ size, done, hash); -++ return 0; -++ } -++ -+ __iscsi_segment_init(segment, size, done, hash); -+ for_each_sg(sg_list, sg, sg_count, i) { -+ debug_scsi("sg %d, len %u offset %u\n", i, sg->length, -+diff --git a/iscsi_tcp.h b/iscsi_tcp.h -+index 950d75f..662ddab 100644 -+--- a/iscsi_tcp.h -++++ b/iscsi_tcp.h -+@@ -24,6 +24,7 @@ -+ -+ #include "libiscsi.h" -+ -++#include "iscsi_compat.h" -+ struct crypto_hash; -+ struct socket; -+ struct iscsi_tcp_conn; -+diff --git a/libiscsi.c b/libiscsi.c -+index 31b6477..e89b92a 100644 -+--- a/libiscsi.c -++++ b/libiscsi.c -+@@ -24,7 +24,10 @@ -+ #include -+ #include -+ #include -++#include -++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19) -+ #include -++#endif -+ #include -+ #include -+ #include -+@@ -971,10 +974,9 @@ again: -+ return rc; -+ } -+ -+-static void iscsi_xmitworker(struct work_struct *work) -++static void iscsi_xmitworker(void *data) -+ { -+- struct iscsi_conn *conn = -+- container_of(work, struct iscsi_conn, xmitwork); -++ struct iscsi_conn *conn = data; -+ int rc; -+ /* -+ * serialize Xmit worker on a per-connection basis. -+@@ -1732,7 +1734,9 @@ iscsi_session_setup(struct iscsi_transport *iscsit, -+ shost->max_cmd_len = iscsit->max_cmd_len; -+ shost->transportt = scsit; -+ shost->transportt->create_work_queue = 1; -++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16) -+ shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; -++#endif -+ *hostno = shost->host_no; -+ -+ session = iscsi_hostdata(shost->hostdata); -+@@ -1882,7 +1886,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) -+ INIT_LIST_HEAD(&conn->mgmtqueue); -+ INIT_LIST_HEAD(&conn->xmitqueue); -+ INIT_LIST_HEAD(&conn->requeue); -+- INIT_WORK(&conn->xmitwork, iscsi_xmitworker); -++ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); -+ -+ /* allocate login_mtask used for the login/text sequences */ -+ spin_lock_bh(&session->lock); -+diff --git a/libiscsi.h b/libiscsi.h -+index 6f10518..61be101 100644 -+--- a/libiscsi.h -++++ b/libiscsi.h -+@@ -24,12 +24,14 @@ -+ #define LIBISCSI_H -+ -+ #include -++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ #include -+-#include -+-#include -++#endif -+ #include "iscsi_proto.h" -+ #include "iscsi_if.h" -+ -++#include "iscsi_compat.h" -++ -+ struct scsi_transport_template; -+ struct scsi_device; -+ struct Scsi_Host; -+diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c -+index e8f8cf1..3c7878d 100644 -+--- a/scsi_transport_iscsi.c -++++ b/scsi_transport_iscsi.c -+@@ -21,7 +21,10 @@ -+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ */ -+ #include -++#include -++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ #include -++#endif -+ #include -+ #include -+ #include -+@@ -29,6 +32,7 @@ -+ #include -+ #include "scsi_transport_iscsi.h" -+ #include "iscsi_if.h" -++#include "iscsi_compat.h" -+ -+ #define ISCSI_SESSION_ATTRS 19 -+ #define ISCSI_CONN_ATTRS 13 -+@@ -322,10 +326,9 @@ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, -+ return 0; -+ } -+ -+-static void iscsi_scan_session(struct work_struct *work) -++static void iscsi_scan_session(void *data) -+ { -+- struct iscsi_cls_session *session = -+- container_of(work, struct iscsi_cls_session, scan_work); -++ struct iscsi_cls_session *session = data; -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ unsigned long flags; -+@@ -343,11 +346,9 @@ done: -+ atomic_dec(&ihost->nr_scans); -+ } -+ -+-static void session_recovery_timedout(struct work_struct *work) -++static void session_recovery_timedout(void *data) -+ { -+- struct iscsi_cls_session *session = -+- container_of(work, struct iscsi_cls_session, -+- recovery_work.work); -++ struct iscsi_cls_session *session = data; -+ unsigned long flags; -+ -+ iscsi_cls_session_printk(KERN_INFO, session, -+@@ -373,11 +374,9 @@ static void session_recovery_timedout(struct work_struct *work) -+ scsi_target_unblock(&session->dev); -+ } -+ -+-static void __iscsi_unblock_session(struct work_struct *work) -++static void __iscsi_unblock_session(void *data) -+ { -+- struct iscsi_cls_session *session = -+- container_of(work, struct iscsi_cls_session, -+- unblock_work); -++ struct iscsi_cls_session *session = data; -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ unsigned long flags; -+@@ -397,10 +396,12 @@ static void __iscsi_unblock_session(struct work_struct *work) -+ * the async scanning code (drivers like iscsi_tcp do login and -+ * scanning from userspace). -+ */ -++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19) -+ if (shost->hostt->scan_finished) { -+ if (queue_work(ihost->scan_workq, &session->scan_work)) -+ atomic_inc(&ihost->nr_scans); -+ } -++#endif -+ } -+ -+ /** -+@@ -420,11 +421,9 @@ void iscsi_unblock_session(struct iscsi_cls_session *session) -+ } -+ EXPORT_SYMBOL_GPL(iscsi_unblock_session); -+ -+-static void __iscsi_block_session(struct work_struct *work) -++static void __iscsi_block_session(void *data) -+ { -+- struct iscsi_cls_session *session = -+- container_of(work, struct iscsi_cls_session, -+- block_work); -++ struct iscsi_cls_session *session = data; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&session->lock, flags); -+@@ -441,11 +440,9 @@ void iscsi_block_session(struct iscsi_cls_session *session) -+ } -+ EXPORT_SYMBOL_GPL(iscsi_block_session); -+ -+-static void __iscsi_unbind_session(struct work_struct *work) -++static void __iscsi_unbind_session(void *data) -+ { -+- struct iscsi_cls_session *session = -+- container_of(work, struct iscsi_cls_session, -+- unbind_work); -++ struct iscsi_cls_session *session = data; -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ -+@@ -484,13 +481,13 @@ iscsi_alloc_session(struct Scsi_Host *shost, -+ session->transport = transport; -+ session->recovery_tmo = 120; -+ session->state = ISCSI_SESSION_FREE; -+- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); -++ INIT_WORK(&session->recovery_work, session_recovery_timedout, session); -+ INIT_LIST_HEAD(&session->host_list); -+ INIT_LIST_HEAD(&session->sess_list); -+- INIT_WORK(&session->unblock_work, __iscsi_unblock_session); -+- INIT_WORK(&session->block_work, __iscsi_block_session); -+- INIT_WORK(&session->unbind_work, __iscsi_unbind_session); -+- INIT_WORK(&session->scan_work, iscsi_scan_session); -++ INIT_WORK(&session->unblock_work, __iscsi_unblock_session, session); -++ INIT_WORK(&session->block_work, __iscsi_block_session, session); -++ INIT_WORK(&session->unbind_work, __iscsi_unbind_session, session); -++ INIT_WORK(&session->scan_work, iscsi_scan_session, session); -+ spin_lock_init(&session->lock); -+ -+ /* this is released in the dev's release function */ -+@@ -619,7 +616,7 @@ void iscsi_remove_session(struct iscsi_cls_session *session) -+ scsi_target_unblock(&session->dev); -+ /* flush running scans then delete devices */ -+ flush_workqueue(ihost->scan_workq); -+- __iscsi_unbind_session(&session->unbind_work); -++ __iscsi_unbind_session(session); -+ -+ /* hw iscsi may not have removed all connections from session */ -+ err = device_for_each_child(&session->dev, NULL, -+@@ -1294,45 +1291,56 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) -+ * Malformed skbs with wrong lengths or invalid creds are not processed. -+ */ -+ static void -+-iscsi_if_rx(struct sk_buff *skb) -++iscsi_if_rx(struct sock *sk, int len) -+ { -++ struct sk_buff *skb; -++ -+ mutex_lock(&rx_queue_mutex); -+- while (skb->len >= NLMSG_SPACE(0)) { -+- int err; -+- uint32_t rlen; -+- struct nlmsghdr *nlh; -+- struct iscsi_uevent *ev; -+- -+- nlh = nlmsg_hdr(skb); -+- if (nlh->nlmsg_len < sizeof(*nlh) || -+- skb->len < nlh->nlmsg_len) { -+- break; -++ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { -++ if (NETLINK_CREDS(skb)->uid) { -++ skb_pull(skb, skb->len); -++ goto free_skb; -+ } -+ -+- ev = NLMSG_DATA(nlh); -+- rlen = NLMSG_ALIGN(nlh->nlmsg_len); -+- if (rlen > skb->len) -+- rlen = skb->len; -++ while (skb->len >= NLMSG_SPACE(0)) { -++ int err; -++ uint32_t rlen; -++ struct nlmsghdr *nlh; -++ struct iscsi_uevent *ev; -+ -+- err = iscsi_if_recv_msg(skb, nlh); -+- if (err) { -+- ev->type = ISCSI_KEVENT_IF_ERROR; -+- ev->iferror = err; -+- } -+- do { -+- /* -+- * special case for GET_STATS: -+- * on success - sending reply and stats from -+- * inside of if_recv_msg(), -+- * on error - fall through. -+- */ -+- if (ev->type == ISCSI_UEVENT_GET_STATS && !err) -++ nlh = nlmsg_hdr(skb); -++ if (nlh->nlmsg_len < sizeof(*nlh) || -++ skb->len < nlh->nlmsg_len) { -+ break; -+- err = iscsi_if_send_reply( -+- NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, -+- nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); -+- } while (err < 0 && err != -ECONNREFUSED); -+- skb_pull(skb, rlen); -++ } -++ -++ ev = NLMSG_DATA(nlh); -++ rlen = NLMSG_ALIGN(nlh->nlmsg_len); -++ if (rlen > skb->len) -++ rlen = skb->len; -++ -++ err = iscsi_if_recv_msg(skb, nlh); -++ if (err) { -++ ev->type = ISCSI_KEVENT_IF_ERROR; -++ ev->iferror = err; -++ } -++ do { -++ /* -++ * special case for GET_STATS: -++ * on success - sending reply and stats from -++ * inside of if_recv_msg(), -++ * on error - fall through. -++ */ -++ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) -++ break; -++ err = iscsi_if_send_reply( -++ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, -++ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); -++ } while (err < 0 && err != -ECONNREFUSED); -++ skb_pull(skb, rlen); -++ } -++free_skb: -++ kfree_skb(skb); -+ } -+ mutex_unlock(&rx_queue_mutex); -+ } -+@@ -1576,7 +1584,10 @@ iscsi_register_transport(struct iscsi_transport *tt) -+ INIT_LIST_HEAD(&priv->list); -+ priv->daemon_pid = -1; -+ priv->iscsi_transport = tt; -++ -++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ priv->t.user_scan = iscsi_user_scan; -++#endif -+ -+ priv->cdev.class = &iscsi_transport_class; -+ snprintf(priv->cdev.class_id, BUS_ID_SIZE, "%s", tt->name); -+@@ -1738,7 +1749,7 @@ static __init int iscsi_transport_init(void) -+ return 0; -+ -+ release_nls: -+- netlink_kernel_release(nls); -++ sock_release(nls->sk_socket); -+ unregister_session_class: -+ transport_class_unregister(&iscsi_session_class); -+ unregister_conn_class: -+@@ -1753,7 +1764,7 @@ unregister_transport_class: -+ static void __exit iscsi_transport_exit(void) -+ { -+ destroy_workqueue(iscsi_eh_timer_workq); -+- netlink_kernel_release(nls); -++ sock_release(nls->sk_socket); -+ transport_class_unregister(&iscsi_connection_class); -+ transport_class_unregister(&iscsi_session_class); -+ transport_class_unregister(&iscsi_host_class); -+diff --git a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h -+index 3492abe..faa435c 100644 -+--- a/scsi_transport_iscsi.h -++++ b/scsi_transport_iscsi.h -+@@ -25,8 +25,12 @@ -+ -+ #include -+ #include -++#include -++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ #include -++#endif -+ #include "iscsi_if.h" -++#include "iscsi_compat.h" -+ -+ struct scsi_transport_template; -+ struct iscsi_transport; -+@@ -184,7 +188,7 @@ struct iscsi_cls_session { -+ -+ /* recovery fields */ -+ int recovery_tmo; -+- struct delayed_work recovery_work; -++ struct work_struct recovery_work; -+ -+ int target_id; -+ -+-- -+1.5.4.1 -+ -diff --git a/kernel/2.6.14-and-2.6.15-compat.patch b/kernel/2.6.14-and-2.6.15-compat.patch -deleted file mode 100644 -index e319924..0000000 ---- a/kernel/2.6.14-and-2.6.15-compat.patch -+++ /dev/null -@@ -1,97 +0,0 @@ --diff -Naurp open-iscsi.diffbase/kernel/iscsi_compat.h open-iscsi.test/kernel/iscsi_compat.h ----- open-iscsi.diffbase/kernel/iscsi_compat.h 1969-12-31 18:00:00.000000000 -0600 --+++ open-iscsi.test/kernel/iscsi_compat.h 2006-01-16 16:25:56.000000000 -0600 --@@ -0,0 +1,55 @@ --+/* --+ * compat crap for older kernels. --+ * do not send to linux-scsi --+ */ --+#include --+#include --+#include --+ --+#ifndef DEFINE_MUTEX --+ --+/* mutex changes from 2.6.16-rc1 and up */ --+#define DEFINE_MUTEX DECLARE_MUTEX --+#define mutex_lock down --+#define mutex_unlock up --+#define mutex semaphore --+#define mutex_init init_MUTEX --+#endif --+ --+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12) --+ --+void int_to_scsilun(unsigned int lun, struct scsi_lun *scsilun) --+{ --+ int i; --+ --+ memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun)); --+ --+ for (i = 0; i < sizeof(lun); i += 2) { --+ scsilun->scsi_lun[i] = (lun >> 8) & 0xFF; --+ scsilun->scsi_lun[i+1] = lun & 0xFF; --+ lun = lun >> 16; --+ } --+} --+ --+#define __nlmsg_put(skb, daemon_pid, seq, type, len, flags) \ --+ __nlmsg_put(skb, daemon_pid, 0, 0, len) --+ --+#endif --+ --+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) --+ --+#define netlink_kernel_create(uint, groups, input, mod) \ --+ netlink_kernel_create(uint, input) --+ --+#define gfp_t unsigned --+ --+void *kzalloc(size_t size, gfp_t flags) --+{ --+ void *ret = kmalloc(size, flags); --+ if (ret) --+ memset(ret, 0, size); --+ return ret; --+} --+ --+#endif --+ --diff -Naurp open-iscsi.diffbase/kernel/iscsi_tcp.c open-iscsi.test/kernel/iscsi_tcp.c ----- open-iscsi.diffbase/kernel/iscsi_tcp.c 2006-01-16 15:38:52.000000000 -0600 --+++ open-iscsi.test/kernel/iscsi_tcp.c 2006-01-16 16:10:42.000000000 -0600 --@@ -33,7 +33,6 @@ -- #include -- #include -- #include ---#include -- #include -- #include -- #include --@@ -44,6 +43,7 @@ -- #include -- #include "scsi_transport_iscsi.h" -- --+#include "iscsi_compat.h" -- #include "iscsi_tcp.h" -- -- MODULE_AUTHOR("Dmitry Yusupov , " --diff -Naurp open-iscsi.diffbase/kernel/scsi_transport_iscsi.c open-iscsi.test/kernel/scsi_transport_iscsi.c ----- open-iscsi.diffbase/kernel/scsi_transport_iscsi.c 2006-01-16 15:38:52.000000000 -0600 --+++ open-iscsi.test/kernel/scsi_transport_iscsi.c 2006-01-16 15:54:47.000000000 -0600 --@@ -22,7 +22,6 @@ -- */ -- #include -- #include ---#include -- #include -- #include -- #include --@@ -30,6 +29,7 @@ -- #include -- #include "scsi_transport_iscsi.h" -- #include "iscsi_if.h" --+#include "iscsi_compat.h" -- -- #define ISCSI_SESSION_ATTRS 8 -- #define ISCSI_CONN_ATTRS 6 -diff --git a/kernel/2.6.16-18_compat.patch b/kernel/2.6.16-18_compat.patch -deleted file mode 100644 -index df293ef..0000000 ---- a/kernel/2.6.16-18_compat.patch -+++ /dev/null -@@ -1,148 +0,0 @@ --diff -Naurp kernel/iscsi_2.6.20_compat.h kernel.diff/iscsi_2.6.20_compat.h ----- kernel/iscsi_2.6.20_compat.h 1969-12-31 18:00:00.000000000 -0600 --+++ kernel.diff/iscsi_2.6.20_compat.h 2007-05-07 16:31:04.000000000 -0500 --@@ -0,0 +1,59 @@ --+ --+#define CRYPTO_ALG_ASYNC 0x00000080 --+struct hash_desc --+{ --+ struct crypto_tfm *tfm; --+ u32 flags; --+}; --+ --+static inline int crypto_hash_init(struct hash_desc *desc) --+{ --+ crypto_digest_init(desc->tfm); --+ return 0; --+} --+ --+static inline int crypto_hash_digest(struct hash_desc *desc, --+ struct scatterlist *sg, --+ unsigned int nbytes, u8 *out) --+{ --+ crypto_digest_digest(desc->tfm, sg, (nbytes+(PAGE_SIZE-1)) / PAGE_SIZE, out); --+ return nbytes; --+} --+ --+static inline int crypto_hash_update(struct hash_desc *desc, --+ struct scatterlist *sg, --+ unsigned int nbytes) --+{ --+ crypto_digest_update(desc->tfm, sg, (nbytes+(PAGE_SIZE-1)) / PAGE_SIZE); --+ return nbytes; --+} --+ --+static inline int crypto_hash_final(struct hash_desc *desc, u8 *out) --+{ --+ crypto_digest_final(desc->tfm, out); --+ return 0; --+} --+ --+static inline struct crypto_tfm *crypto_alloc_hash(const char *alg_name, --+ u32 type, u32 mask) --+{ --+ struct crypto_tfm *ret = crypto_alloc_tfm(alg_name ,type); --+ return ret ? ret : ERR_PTR(-ENOMEM); --+} --+ --+static inline void crypto_free_hash(struct crypto_tfm *tfm) --+{ --+ crypto_free_tfm(tfm); --+} --+ --+int kernel_getsockname(struct socket *sock, struct sockaddr *addr, --+ int *addrlen) --+{ --+ return sock->ops->getname(sock, addr, addrlen, 0); --+} --+ --+int kernel_getpeername(struct socket *sock, struct sockaddr *addr, --+ int *addrlen) --+{ --+ return sock->ops->getname(sock, addr, addrlen, 1); --+} --diff -Nurp a/iscsi_tcp.h b/iscsi_tcp.h ----- a/iscsi_tcp.h 2007-01-10 12:38:25.000000000 +0200 --+++ b/iscsi_tcp.h 2007-01-10 12:38:25.000000000 +0200 --@@ -49,6 +49,7 @@ -- #define ISCSI_SG_TABLESIZE SG_ALL -- #define ISCSI_TCP_MAX_CMD_LEN 16 -- --+#include "iscsi_2.6.20_compat.h" -- struct crypto_hash; -- struct socket; -- --diff -Nurp a/libiscsi.c b/libiscsi.c ----- a/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 --+++ b/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 --@@ -716,10 +716,9 @@ again: -- return rc; -- } -- ---static void iscsi_xmitworker(struct work_struct *work) --+static void iscsi_xmitworker(void *data) -- { --- struct iscsi_conn *conn = --- container_of(work, struct iscsi_conn, xmitwork); --+ struct iscsi_conn *conn = data; -- int rc; -- /* -- * serialize Xmit worker on a per-connection basis. --@@ -1509,7 +1508,7 @@ iscsi_conn_setup(struct iscsi_cls_sessio -- if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) -- goto mgmtqueue_alloc_fail; -- --- INIT_WORK(&conn->xmitwork, iscsi_xmitworker); --+ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); -- -- /* allocate login_mtask used for the login/text sequences */ -- spin_lock_bh(&session->lock); --diff -Nurp a/libiscsi.h b/libiscsi.h ----- a/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 --+++ b/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 --@@ -25,8 +25,6 @@ -- -- #include -- #include ---#include ---#include -- #include "iscsi_proto.h" -- #include "iscsi_if.h" -- --diff -Nurp a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c ----- a/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 --+++ b/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 --@@ -234,11 +234,9 @@ static int iscsi_user_scan(struct Scsi_H -- return 0; -- } -- ---static void session_recovery_timedout(struct work_struct *work) --+static void session_recovery_timedout(void *data) -- { --- struct iscsi_cls_session *session = --- container_of(work, struct iscsi_cls_session, --- recovery_work.work); --+ struct iscsi_cls_session *session = data; -- -- dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " -- "out after %d secs\n", session->recovery_tmo); --@@ -278,7 +276,7 @@ iscsi_alloc_session(struct Scsi_Host *sh -- -- session->transport = transport; -- session->recovery_tmo = 120; --- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); --+ INIT_WORK(&session->recovery_work, session_recovery_timedout, session); -- INIT_LIST_HEAD(&session->host_list); -- INIT_LIST_HEAD(&session->sess_list); -- --diff -Nurp a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h ----- a/scsi_transport_iscsi.h 2007-01-10 12:38:26.000000000 +0200 --+++ b/scsi_transport_iscsi.h 2007-01-10 12:38:25.000000000 +0200 --@@ -176,7 +176,7 @@ struct iscsi_cls_session { -- -- /* recovery fields */ -- int recovery_tmo; --- struct delayed_work recovery_work; --+ struct work_struct recovery_work; -- -- int target_id; -diff --git a/kernel/2.6.19_compat.patch b/kernel/2.6.19_compat.patch -deleted file mode 100644 -index d743d08..0000000 ---- a/kernel/2.6.19_compat.patch -+++ /dev/null -@@ -1,74 +0,0 @@ --diff -Nurp a/libiscsi.c b/libiscsi.c ----- a/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 --+++ b/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 --@@ -716,10 +716,9 @@ again: -- return rc; -- } -- ---static void iscsi_xmitworker(struct work_struct *work) --+static void iscsi_xmitworker(void *data) -- { --- struct iscsi_conn *conn = --- container_of(work, struct iscsi_conn, xmitwork); --+ struct iscsi_conn *conn = data; -- int rc; -- /* -- * serialize Xmit worker on a per-connection basis. --@@ -1509,7 +1508,7 @@ iscsi_conn_setup(struct iscsi_cls_sessio -- if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) -- goto mgmtqueue_alloc_fail; -- --- INIT_WORK(&conn->xmitwork, iscsi_xmitworker); --+ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); -- -- /* allocate login_mtask used for the login/text sequences */ -- spin_lock_bh(&session->lock); --diff -Nurp a/libiscsi.h b/libiscsi.h ----- a/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 --+++ b/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 --@@ -25,8 +25,6 @@ -- -- #include -- #include ---#include ---#include -- #include "iscsi_proto.h" -- #include "iscsi_if.h" -- --diff -Nurp a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c ----- a/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 --+++ b/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 --@@ -234,11 +234,9 @@ static int iscsi_user_scan(struct Scsi_H -- return 0; -- } -- ---static void session_recovery_timedout(struct work_struct *work) --+static void session_recovery_timedout(void *data) -- { --- struct iscsi_cls_session *session = --- container_of(work, struct iscsi_cls_session, --- recovery_work.work); --+ struct iscsi_cls_session *session = data; -- -- dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " -- "out after %d secs\n", session->recovery_tmo); --@@ -278,7 +276,7 @@ iscsi_alloc_session(struct Scsi_Host *sh -- -- session->transport = transport; -- session->recovery_tmo = 120; --- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); --+ INIT_WORK(&session->recovery_work, session_recovery_timedout, session); -- INIT_LIST_HEAD(&session->host_list); -- INIT_LIST_HEAD(&session->sess_list); -- --diff -Nurp a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h ----- a/scsi_transport_iscsi.h 2007-01-10 12:38:26.000000000 +0200 --+++ b/scsi_transport_iscsi.h 2007-01-10 12:38:25.000000000 +0200 --@@ -176,7 +176,7 @@ struct iscsi_cls_session { -- -- /* recovery fields */ -- int recovery_tmo; --- struct delayed_work recovery_work; --+ struct work_struct recovery_work; -- -- int target_id; -diff --git a/kernel/2.6.20-21_compat.patch b/kernel/2.6.20-21_compat.patch -new file mode 100644 -index 0000000..9c3e3b3 ---- /dev/null -+++ b/kernel/2.6.20-21_compat.patch -@@ -0,0 +1,278 @@ -+diff --git a/iscsi_2.6.22_compat.h b/iscsi_2.6.22_compat.h -+new file mode 100644 -+index 0000000..2ba7deb -+--- /dev/null -++++ b/iscsi_2.6.22_compat.h -+@@ -0,0 +1,85 @@ -++#include -++#include -++ -++#ifndef ISCSI_2622_COMPAT_H -++#define ISCSI_2622_COMPAT_H -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,20) -++ -++static inline __attribute__((const)) -++bool is_power_of_2(unsigned long n) -++{ -++ return (n != 0 && ((n & (n - 1)) == 0)); -++} -++#endif -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21) -++ -++#define netlink_kernel_create(net, uint, groups, input, cb_mutex, mod) \ -++ netlink_kernel_create(uint, groups, input, mod) -++ -++static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb) -++{ -++ return (struct nlmsghdr *)skb->data; -++} -++ -++#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) -++ -++#define netlink_kernel_create(net, uint, groups, input, cb_mutex, mod) \ -++ netlink_kernel_create(uint, groups, input, cb_mutex, mod) -++ -++#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) -++ -++#define netlink_kernel_create(net, uint, groups, input, cb_mutex, mod) \ -++ netlink_kernel_create(uint, groups, input, cb_mutex, mod) -++ -++#endif -++ -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) -++ -++static inline struct scatterlist *sg_next(struct scatterlist *sg) -++{ -++ if (!sg) { -++ BUG(); -++ return NULL; -++ } -++ return sg + 1; -++} -++ -++#define for_each_sg(sglist, sg, nr, __i) \ -++ for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg)) -++ -++#define sg_page(_sg) _sg->page -++ -++static inline void sg_set_page(struct scatterlist *sg, struct page *page, -++ unsigned int len, unsigned int offset) -++{ -++ sg->page = page; -++ sg->offset = offset; -++ sg->length = len; -++} -++ -++static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) -++{ -++ memset(sgl, 0, sizeof(*sgl) * nents); -++} -++#endif -++ -++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) -++#define scsi_sg_count(cmd) ((cmd)->use_sg) -++#define scsi_sglist(cmd) ((struct scatterlist *)(cmd)->request_buffer) -++#define scsi_bufflen(cmd) ((cmd)->request_bufflen) -++ -++static inline void scsi_set_resid(struct scsi_cmnd *cmd, int resid) -++{ -++ cmd->resid = resid; -++} -++ -++static inline int scsi_get_resid(struct scsi_cmnd *cmd) -++{ -++ return cmd->resid; -++} -++#endif -++ -++#endif -+diff --git a/iscsi_tcp.c b/iscsi_tcp.c -+index 0d21d87..b24e533 100644 -+--- a/iscsi_tcp.c -++++ b/iscsi_tcp.c -+@@ -422,6 +422,17 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment, -+ -+ debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n", -+ offset, size); -++ -++ /* -++ * older kernels could send use_sg=0 for commands like sgio -++ * or scsi-ml commands. -++ */ -++ if (!sg_count) { -++ iscsi_segment_init_linear(segment, (void *)sg_list + offset, -++ size, done, hash); -++ return 0; -++ } -++ -+ __iscsi_segment_init(segment, size, done, hash); -+ for_each_sg(sg_list, sg, sg_count, i) { -+ debug_scsi("sg %d, len %u offset %u\n", i, sg->length, -+@@ -1936,6 +1947,9 @@ static struct scsi_host_template iscsi_sht = { -+ .eh_device_reset_handler= iscsi_eh_device_reset, -+ .eh_host_reset_handler = iscsi_eh_host_reset, -+ .use_clustering = DISABLE_CLUSTERING, -++#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,24) -++ .use_sg_chaining = ENABLE_SG_CHAINING, -++#endif -+ .slave_configure = iscsi_tcp_slave_configure, -+ .proc_name = "iscsi_tcp", -+ .this_id = -1, -+diff --git a/iscsi_tcp.h b/iscsi_tcp.h -+index 950d75f..a7bc56f 100644 -+--- a/iscsi_tcp.h -++++ b/iscsi_tcp.h -+@@ -24,6 +24,7 @@ -+ -+ #include "libiscsi.h" -+ -++#include "iscsi_2.6.22_compat.h" -+ struct crypto_hash; -+ struct socket; -+ struct iscsi_tcp_conn; -+diff --git a/libiscsi.h b/libiscsi.h -+index 6f10518..995852f 100644 -+--- a/libiscsi.h -++++ b/libiscsi.h -+@@ -30,6 +30,8 @@ -+ #include "iscsi_proto.h" -+ #include "iscsi_if.h" -+ -++#include "iscsi_2.6.22_compat.h" -++ -+ struct scsi_transport_template; -+ struct scsi_device; -+ struct Scsi_Host; -+diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c -+index e8f8cf1..0ab82de 100644 -+--- a/scsi_transport_iscsi.c -++++ b/scsi_transport_iscsi.c -+@@ -29,6 +29,7 @@ -+ #include -+ #include "scsi_transport_iscsi.h" -+ #include "iscsi_if.h" -++#include "iscsi_2.6.22_compat.h" -+ -+ #define ISCSI_SESSION_ATTRS 19 -+ #define ISCSI_CONN_ATTRS 13 -+@@ -1290,49 +1291,61 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) -+ } -+ -+ /* -+- * Get message from skb. Each message is processed by iscsi_if_recv_msg. -+- * Malformed skbs with wrong lengths or invalid creds are not processed. -++ * Get message from skb (based on rtnetlink_rcv_skb). Each message is -++ * processed by iscsi_if_recv_msg. Malformed skbs with wrong lengths or -++ * invalid creds are discarded silently. -+ */ -+ static void -+-iscsi_if_rx(struct sk_buff *skb) -++iscsi_if_rx(struct sock *sk, int len) -+ { -++ struct sk_buff *skb; -++ -+ mutex_lock(&rx_queue_mutex); -+- while (skb->len >= NLMSG_SPACE(0)) { -+- int err; -+- uint32_t rlen; -+- struct nlmsghdr *nlh; -+- struct iscsi_uevent *ev; -+- -+- nlh = nlmsg_hdr(skb); -+- if (nlh->nlmsg_len < sizeof(*nlh) || -+- skb->len < nlh->nlmsg_len) { -+- break; -++ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { -++ if (NETLINK_CREDS(skb)->uid) { -++ skb_pull(skb, skb->len); -++ goto free_skb; -+ } -+ -+- ev = NLMSG_DATA(nlh); -+- rlen = NLMSG_ALIGN(nlh->nlmsg_len); -+- if (rlen > skb->len) -+- rlen = skb->len; -++ while (skb->len >= NLMSG_SPACE(0)) { -++ int err; -++ uint32_t rlen; -++ struct nlmsghdr *nlh; -++ struct iscsi_uevent *ev; -+ -+- err = iscsi_if_recv_msg(skb, nlh); -+- if (err) { -+- ev->type = ISCSI_KEVENT_IF_ERROR; -+- ev->iferror = err; -+- } -+- do { -+- /* -+- * special case for GET_STATS: -+- * on success - sending reply and stats from -+- * inside of if_recv_msg(), -+- * on error - fall through. -+- */ -+- if (ev->type == ISCSI_UEVENT_GET_STATS && !err) -++ nlh = nlmsg_hdr(skb); -++ if (nlh->nlmsg_len < sizeof(*nlh) || -++ skb->len < nlh->nlmsg_len) { -+ break; -+- err = iscsi_if_send_reply( -+- NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, -+- nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); -+- } while (err < 0 && err != -ECONNREFUSED); -+- skb_pull(skb, rlen); -++ } -++ -++ ev = NLMSG_DATA(nlh); -++ rlen = NLMSG_ALIGN(nlh->nlmsg_len); -++ if (rlen > skb->len) -++ rlen = skb->len; -++ -++ err = iscsi_if_recv_msg(skb, nlh); -++ if (err) { -++ ev->type = ISCSI_KEVENT_IF_ERROR; -++ ev->iferror = err; -++ } -++ do { -++ /* -++ * special case for GET_STATS: -++ * on success - sending reply and stats from -++ * inside of if_recv_msg(), -++ * on error - fall through. -++ */ -++ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) -++ break; -++ err = iscsi_if_send_reply( -++ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, -++ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); -++ } while (err < 0 && err != -ECONNREFUSED); -++ skb_pull(skb, rlen); -++ } -++free_skb: -++ kfree_skb(skb); -+ } -+ mutex_unlock(&rx_queue_mutex); -+ } -+@@ -1738,7 +1751,7 @@ static __init int iscsi_transport_init(void) -+ return 0; -+ -+ release_nls: -+- netlink_kernel_release(nls); -++ sock_release(nls->sk_socket); -+ unregister_session_class: -+ transport_class_unregister(&iscsi_session_class); -+ unregister_conn_class: -+@@ -1753,7 +1766,7 @@ unregister_transport_class: -+ static void __exit iscsi_transport_exit(void) -+ { -+ destroy_workqueue(iscsi_eh_timer_workq); -+- netlink_kernel_release(nls); -++ sock_release(nls->sk_socket); -+ transport_class_unregister(&iscsi_connection_class); -+ transport_class_unregister(&iscsi_session_class); -+ transport_class_unregister(&iscsi_host_class); -+-- -+1.5.4.1 -+ -diff --git a/kernel/2.6.24_compat.patch b/kernel/2.6.24_compat.patch -new file mode 100644 -index 0000000..4cbb173 ---- /dev/null -+++ b/kernel/2.6.24_compat.patch -@@ -0,0 +1,44 @@ -+diff --git a/iscsi_tcp.c b/iscsi_tcp.c -+index 0d21d87..ed5cdfa 100644 -+--- a/iscsi_tcp.c -++++ b/iscsi_tcp.c -+@@ -26,6 +26,7 @@ -+ * Zhenyu Wang -+ */ -+ -++#include -+ #include -+ #include -+ #include -+@@ -1936,6 +1937,9 @@ static struct scsi_host_template iscsi_sht = { -+ .eh_device_reset_handler= iscsi_eh_device_reset, -+ .eh_host_reset_handler = iscsi_eh_host_reset, -+ .use_clustering = DISABLE_CLUSTERING, -++#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,24) -++ .use_sg_chaining = ENABLE_SG_CHAINING, -++#endif -+ .slave_configure = iscsi_tcp_slave_configure, -+ .proc_name = "iscsi_tcp", -+ .this_id = -1, -+diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c -+index e8f8cf1..7c19201 100644 -+--- a/scsi_transport_iscsi.c -++++ b/scsi_transport_iscsi.c -+@@ -1738,7 +1738,7 @@ static __init int iscsi_transport_init(void) -+ return 0; -+ -+ release_nls: -+- netlink_kernel_release(nls); -++ sock_release(nls->sk_socket); -+ unregister_session_class: -+ transport_class_unregister(&iscsi_session_class); -+ unregister_conn_class: -+@@ -1753,7 +1753,7 @@ unregister_transport_class: -+ static void __exit iscsi_transport_exit(void) -+ { -+ destroy_workqueue(iscsi_eh_timer_workq); -+- netlink_kernel_release(nls); -++ sock_release(nls->sk_socket); -+ transport_class_unregister(&iscsi_connection_class); -+ transport_class_unregister(&iscsi_session_class); -+ transport_class_unregister(&iscsi_host_class); -diff --git a/kernel/Makefile b/kernel/Makefile -index 199241b..7281a60 100644 ---- a/kernel/Makefile -+++ b/kernel/Makefile -@@ -48,11 +48,10 @@ all: kernel_check - # form. - - #some constants --13_patch=2.6.13_compat.patch --14to15_patch=2.6.14-and-2.6.15-compat.patch --16to18_patch=2.6.16-18_compat.patch --19_patch=2.6.19_compat.patch --all_patches=13_patch 14to15_patch 16to18_patch 19_patch -+14to19_patch=2.6.14-19_compat.patch -+20to21_patch=2.6.20-21_compat.patch -+24_patch=2.6.24_compat.patch -+all_patches=14to21_patch 20to21_patch 24_patch - cur_patched=cur_patched - - ## fun stuff for maintaining multiple versions -@@ -66,67 +65,63 @@ KSUBLEVEL = $(shell cat $(KSRC)/Makefile | awk -F= '/^SUBLEVEL =/ {print $$2}' | - KERNEL_TARGET=linux_2_6_$(KSUBLEVEL) - kernel_check: $(KERNEL_TARGET) - -+linux_2_6_14: has_14to19_patch - --linux_2_6_13: has_13_patch -+linux_2_6_15: has_14to19_patch - --linux_2_6_14: has_14to15_patch -+linux_2_6_16: has_14to19_patch - --linux_2_6_15: has_14to15_patch -+linux_2_6_17: has_14to19_patch - --linux_2_6_16: has_16to18_patch -+linux_2_6_18: has_14to19_patch - --linux_2_6_17: has_16to18_patch -+linux_2_6_19: has_14to19_patch - --linux_2_6_18: has_16to18_patch -+linux_2_6_20: has_20to21_patch - --linux_2_6_19: has_19_patch -+linux_2_6_21: has_20to21_patch - --linux_2_6_20: $(unpatch_code) -+linux_2_6_22: has_20to21_patch - --linux_2_6_21: $(unpatch_code) -+linux_2_6_23: has_20to21_patch -+ -+linux_2_6_24: has_24_patch -+ -+linux_2_6_25: $(unpatch_code) - - do_unpatch_code: -- echo "Un-patching source code for use with linux-2.6.20 and up ..." -+ echo "Un-patching source code for use with linux-2.6.14 and up ..." - patch -R -E -p1 < $(cur_patched) - rm -f `readlink $(cur_patched)` - rm -f $(cur_patched) - - # these below targets must be the same as the variable name prefixed by has_ - # otherwise below compat_patch: target will not work --has_13_patch: $(13_patch) -- echo "Patching source code for linux-2.6.13 ..." -+has_14to19_patch: $(14to19_patch) -+ echo "Patching source code for linux-2.6.14-19 ..." - if [ -e $(cur_patched) ]; then \ - make -C . clean; \ - fi -- patch -p1 < $(13_patch) -- cp $(13_patch) $@ -+ patch -p1 < $(14to19_patch) -+ cp $(14to19_patch) $@ - ln -s $@ $(cur_patched) - --has_14to15_patch: $(14to15_patch) -- echo "Patching source code for linux-2.6.14-15 ..." -+has_20to21_patch: $(20to21_patch) -+ echo "Patching source code for linux-2.6.20-21 ..." - if [ -e $(cur_patched) ]; then \ - make -C . clean; \ - fi -- patch -p1 < $(14to15_patch) -- cp $(14to15_patch) $@ -+ patch -p1 < $(20to21_patch) -+ cp $(20to21_patch) $@ - ln -s $@ $(cur_patched) - --has_16to18_patch: $(16to18_patch) -- echo "Patching source code for linux-2.6.16-18 ..." -+has_24_patch: $(24_patch) -+ echo "Patching source code for linux-2.6.24 ..." - if [ -e $(cur_patched) ]; then \ - make -C . clean; \ - fi -- patch -p1 < $(16to18_patch) -- cp $(16to18_patch) $@ -- ln -s $@ $(cur_patched) -- --has_19_patch: $(19_patch) -- echo "Patching source code for linux-2.6.19 ..." -- if [ -e $(cur_patched) ]; then \ -- make -C . clean; \ -- fi -- patch -p1 < $(19_patch) -- cp $(19_patch) $@ -+ patch -p1 < $(24_patch) -+ cp $(24_patch) $@ - ln -s $@ $(cur_patched) - - # ============ END code for kernel_check and source patching ================= -@@ -173,4 +168,9 @@ $(ko): all - install_kernel: $(ko) - $(KBUILD_BASE) modules_install INSTALL_MOD_DIR=$(INSTALL_MOD_DIR) INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) - -+dpkg_divert: -+ for module in $(ko) ; do \ -+ dpkg-divert --rename /lib/modules/$(KERNELRELEASE)/$(INSTALL_MOD_DIR)/$$module ; \ -+ done -+ - # vim: ft=make tw=72 sw=4 ts=4: -diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c -index 7f7a919..daabd46 100644 ---- a/kernel/iscsi_tcp.c -+++ b/kernel/iscsi_tcp.c -@@ -48,7 +48,7 @@ MODULE_AUTHOR("Dmitry Yusupov , " - "Alex Aizman "); - MODULE_DESCRIPTION("iSCSI/TCP data-path"); - MODULE_LICENSE("GPL"); --/* #define DEBUG_TCP */ -+#undef DEBUG_TCP - #define DEBUG_ASSERT - - #ifdef DEBUG_TCP -@@ -67,118 +67,429 @@ MODULE_LICENSE("GPL"); - static unsigned int iscsi_max_lun = 512; - module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); - -+static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment); -+ -+/* -+ * Scatterlist handling: inside the iscsi_segment, we -+ * remember an index into the scatterlist, and set data/size -+ * to the current scatterlist entry. For highmem pages, we -+ * kmap as needed. -+ * -+ * Note that the page is unmapped when we return from -+ * TCP's data_ready handler, so we may end up mapping and -+ * unmapping the same page repeatedly. The whole reason -+ * for this is that we shouldn't keep the page mapped -+ * outside the softirq. -+ */ -+ -+/** -+ * iscsi_tcp_segment_init_sg - init indicated scatterlist entry -+ * @segment: the buffer object -+ * @sg: scatterlist -+ * @offset: byte offset into that sg entry -+ * -+ * This function sets up the segment so that subsequent -+ * data is copied to the indicated sg entry, at the given -+ * offset. -+ */ - static inline void --iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size) -+iscsi_tcp_segment_init_sg(struct iscsi_segment *segment, -+ struct scatterlist *sg, unsigned int offset) - { -- ibuf->sg.page = virt_to_page(vbuf); -- ibuf->sg.offset = offset_in_page(vbuf); -- ibuf->sg.length = size; -- ibuf->sent = 0; -- ibuf->use_sendmsg = 1; -+ segment->sg = sg; -+ segment->sg_offset = offset; -+ segment->size = min(sg->length - offset, -+ segment->total_size - segment->total_copied); -+ segment->data = NULL; - } - -+/** -+ * iscsi_tcp_segment_map - map the current S/G page -+ * @segment: iscsi_segment -+ * @recv: 1 if called from recv path -+ * -+ * We only need to possibly kmap data if scatter lists are being used, -+ * because the iscsi passthrough and internal IO paths will never use high -+ * mem pages. -+ */ - static inline void --iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg) -+iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv) - { -- ibuf->sg.page = sg->page; -- ibuf->sg.offset = sg->offset; -- ibuf->sg.length = sg->length; -+ struct scatterlist *sg; -+ -+ if (segment->data != NULL || !segment->sg) -+ return; -+ -+ sg = segment->sg; -+ BUG_ON(segment->sg_mapped); -+ BUG_ON(sg->length == 0); -+ - /* -- * Fastpath: sg element fits into single page -+ * If the page count is greater than one it is ok to send -+ * to the network layer's zero copy send path. If not we -+ * have to go the slow sendmsg path. We always map for the -+ * recv path. - */ -- if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page)) -- ibuf->use_sendmsg = 0; -- else -- ibuf->use_sendmsg = 1; -- ibuf->sent = 0; -+ if (page_count(sg_page(sg)) >= 1 && !recv) -+ return; -+ -+ debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit", -+ segment); -+ segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0); -+ segment->data = segment->sg_mapped + sg->offset + segment->sg_offset; - } - --static inline int --iscsi_buf_left(struct iscsi_buf *ibuf) -+static inline void -+iscsi_tcp_segment_unmap(struct iscsi_segment *segment) - { -- int rc; -+ debug_tcp("iscsi_tcp_segment_unmap %p\n", segment); - -- rc = ibuf->sg.length - ibuf->sent; -- BUG_ON(rc < 0); -- return rc; -+ if (segment->sg_mapped) { -+ debug_tcp("iscsi_tcp_segment_unmap valid\n"); -+ kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0); -+ segment->sg_mapped = NULL; -+ segment->data = NULL; -+ } - } - -+/* -+ * Splice the digest buffer into the buffer -+ */ - static inline void --iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, -- u8* crc) -+iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest) - { -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- -- crypto_hash_digest(&tcp_conn->tx_hash, &buf->sg, buf->sg.length, crc); -- buf->sg.length += sizeof(u32); -+ segment->data = digest; -+ segment->digest_len = ISCSI_DIGEST_SIZE; -+ segment->total_size += ISCSI_DIGEST_SIZE; -+ segment->size = ISCSI_DIGEST_SIZE; -+ segment->copied = 0; -+ segment->sg = NULL; -+ segment->hash = NULL; - } - -+/** -+ * iscsi_tcp_segment_done - check whether the segment is complete -+ * @segment: iscsi segment to check -+ * @recv: set to one of this is called from the recv path -+ * @copied: number of bytes copied -+ * -+ * Check if we're done receiving this segment. If the receive -+ * buffer is full but we expect more data, move on to the -+ * next entry in the scatterlist. -+ * -+ * If the amount of data we received isn't a multiple of 4, -+ * we will transparently receive the pad bytes, too. -+ * -+ * This function must be re-entrant. -+ */ - static inline int --iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn) -+iscsi_tcp_segment_done(struct iscsi_segment *segment, int recv, unsigned copied) - { -- struct sk_buff *skb = tcp_conn->in.skb; -- -- tcp_conn->in.zero_copy_hdr = 0; -+ static unsigned char padbuf[ISCSI_PAD_LEN]; -+ struct scatterlist sg; -+ unsigned int pad; - -- if (tcp_conn->in.copy >= tcp_conn->hdr_size && -- tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) { -+ debug_tcp("copied %u %u size %u %s\n", segment->copied, copied, -+ segment->size, recv ? "recv" : "xmit"); -+ if (segment->hash && copied) { - /* -- * Zero-copy PDU Header: using connection context -- * to store header pointer. -+ * If a segment is kmapd we must unmap it before sending -+ * to the crypto layer since that will try to kmap it again. - */ -- if (skb_shinfo(skb)->frag_list == NULL && -- !skb_shinfo(skb)->nr_frags) { -- tcp_conn->in.hdr = (struct iscsi_hdr *) -- ((char*)skb->data + tcp_conn->in.offset); -- tcp_conn->in.zero_copy_hdr = 1; -+ iscsi_tcp_segment_unmap(segment); -+ -+ if (!segment->data) { -+ sg_init_table(&sg, 1); -+ sg_set_page(&sg, sg_page(segment->sg), copied, -+ segment->copied + segment->sg_offset + -+ segment->sg->offset); -+ } else -+ sg_init_one(&sg, segment->data + segment->copied, -+ copied); -+ crypto_hash_update(segment->hash, &sg, copied); -+ } -+ -+ segment->copied += copied; -+ if (segment->copied < segment->size) { -+ iscsi_tcp_segment_map(segment, recv); -+ return 0; -+ } -+ -+ segment->total_copied += segment->copied; -+ segment->copied = 0; -+ segment->size = 0; -+ -+ /* Unmap the current scatterlist page, if there is one. */ -+ iscsi_tcp_segment_unmap(segment); -+ -+ /* Do we have more scatterlist entries? */ -+ debug_tcp("total copied %u total size %u\n", segment->total_copied, -+ segment->total_size); -+ if (segment->total_copied < segment->total_size) { -+ /* Proceed to the next entry in the scatterlist. */ -+ iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg), -+ 0); -+ iscsi_tcp_segment_map(segment, recv); -+ BUG_ON(segment->size == 0); -+ return 0; -+ } -+ -+ /* Do we need to handle padding? */ -+ pad = iscsi_padding(segment->total_copied); -+ if (pad != 0) { -+ debug_tcp("consume %d pad bytes\n", pad); -+ segment->total_size += pad; -+ segment->size = pad; -+ segment->data = padbuf; -+ return 0; -+ } -+ -+ /* -+ * Set us up for transferring the data digest. hdr digest -+ * is completely handled in hdr done function. -+ */ -+ if (segment->hash) { -+ crypto_hash_final(segment->hash, segment->digest); -+ iscsi_tcp_segment_splice_digest(segment, -+ recv ? segment->recv_digest : segment->digest); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+/** -+ * iscsi_tcp_xmit_segment - transmit segment -+ * @tcp_conn: the iSCSI TCP connection -+ * @segment: the buffer to transmnit -+ * -+ * This function transmits as much of the buffer as -+ * the network layer will accept, and returns the number of -+ * bytes transmitted. -+ * -+ * If CRC hashing is enabled, the function will compute the -+ * hash as it goes. When the entire segment has been transmitted, -+ * it will retrieve the hash value and send it as well. -+ */ -+static int -+iscsi_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment) -+{ -+ struct socket *sk = tcp_conn->sock; -+ unsigned int copied = 0; -+ int r = 0; -+ -+ while (!iscsi_tcp_segment_done(segment, 0, r)) { -+ struct scatterlist *sg; -+ unsigned int offset, copy; -+ int flags = 0; -+ -+ r = 0; -+ offset = segment->copied; -+ copy = segment->size - offset; -+ -+ if (segment->total_copied + segment->size < segment->total_size) -+ flags |= MSG_MORE; -+ -+ /* Use sendpage if we can; else fall back to sendmsg */ -+ if (!segment->data) { -+ sg = segment->sg; -+ offset += segment->sg_offset + sg->offset; -+ r = tcp_conn->sendpage(sk, sg_page(sg), offset, copy, -+ flags); - } else { -- /* ignoring return code since we checked -- * in.copy before */ -- skb_copy_bits(skb, tcp_conn->in.offset, -- &tcp_conn->hdr, tcp_conn->hdr_size); -- tcp_conn->in.hdr = &tcp_conn->hdr; -+ struct msghdr msg = { .msg_flags = flags }; -+ struct kvec iov = { -+ .iov_base = segment->data + offset, -+ .iov_len = copy -+ }; -+ -+ r = kernel_sendmsg(sk, &msg, &iov, 1, copy); - } -- tcp_conn->in.offset += tcp_conn->hdr_size; -- tcp_conn->in.copy -= tcp_conn->hdr_size; -- } else { -- int hdr_remains; -- int copylen; - -- /* -- * PDU header scattered across SKB's, -- * copying it... This'll happen quite rarely. -- */ -+ if (r < 0) { -+ iscsi_tcp_segment_unmap(segment); -+ if (copied || r == -EAGAIN) -+ break; -+ return r; -+ } -+ copied += r; -+ } -+ return copied; -+} - -- if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) -- tcp_conn->in.hdr_offset = 0; -+/** -+ * iscsi_tcp_segment_recv - copy data to segment -+ * @tcp_conn: the iSCSI TCP connection -+ * @segment: the buffer to copy to -+ * @ptr: data pointer -+ * @len: amount of data available -+ * -+ * This function copies up to @len bytes to the -+ * given buffer, and returns the number of bytes -+ * consumed, which can actually be less than @len. -+ * -+ * If hash digest is enabled, the function will update the -+ * hash while copying. -+ * Combining these two operations doesn't buy us a lot (yet), -+ * but in the future we could implement combined copy+crc, -+ * just way we do for network layer checksums. -+ */ -+static int -+iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment, const void *ptr, -+ unsigned int len) -+{ -+ unsigned int copy = 0, copied = 0; - -- hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset; -- BUG_ON(hdr_remains <= 0); -+ while (!iscsi_tcp_segment_done(segment, 1, copy)) { -+ if (copied == len) { -+ debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n", -+ len); -+ break; -+ } - -- copylen = min(tcp_conn->in.copy, hdr_remains); -- skb_copy_bits(skb, tcp_conn->in.offset, -- (char*)&tcp_conn->hdr + tcp_conn->in.hdr_offset, -- copylen); -+ copy = min(len - copied, segment->size - segment->copied); -+ debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy); -+ memcpy(segment->data + segment->copied, ptr + copied, copy); -+ copied += copy; -+ } -+ return copied; -+} - -- debug_tcp("PDU gather offset %d bytes %d in.offset %d " -- "in.copy %d\n", tcp_conn->in.hdr_offset, copylen, -- tcp_conn->in.offset, tcp_conn->in.copy); -+static inline void -+iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen, -+ unsigned char digest[ISCSI_DIGEST_SIZE]) -+{ -+ struct scatterlist sg; -+ -+ sg_init_one(&sg, hdr, hdrlen); -+ crypto_hash_digest(hash, &sg, hdrlen, digest); -+} - -- tcp_conn->in.offset += copylen; -- tcp_conn->in.copy -= copylen; -- if (copylen < hdr_remains) { -- tcp_conn->in_progress = IN_PROGRESS_HEADER_GATHER; -- tcp_conn->in.hdr_offset += copylen; -- return -EAGAIN; -+static inline int -+iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment) -+{ -+ if (!segment->digest_len) -+ return 1; -+ -+ if (memcmp(segment->recv_digest, segment->digest, -+ segment->digest_len)) { -+ debug_scsi("digest mismatch\n"); -+ return 0; -+ } -+ -+ return 1; -+} -+ -+/* -+ * Helper function to set up segment buffer -+ */ -+static inline void -+__iscsi_segment_init(struct iscsi_segment *segment, size_t size, -+ iscsi_segment_done_fn_t *done, struct hash_desc *hash) -+{ -+ memset(segment, 0, sizeof(*segment)); -+ segment->total_size = size; -+ segment->done = done; -+ -+ if (hash) { -+ segment->hash = hash; -+ crypto_hash_init(hash); -+ } -+} -+ -+static inline void -+iscsi_segment_init_linear(struct iscsi_segment *segment, void *data, -+ size_t size, iscsi_segment_done_fn_t *done, -+ struct hash_desc *hash) -+{ -+ __iscsi_segment_init(segment, size, done, hash); -+ segment->data = data; -+ segment->size = size; -+} -+ -+static inline int -+iscsi_segment_seek_sg(struct iscsi_segment *segment, -+ struct scatterlist *sg_list, unsigned int sg_count, -+ unsigned int offset, size_t size, -+ iscsi_segment_done_fn_t *done, struct hash_desc *hash) -+{ -+ struct scatterlist *sg; -+ unsigned int i; -+ -+ debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n", -+ offset, size); -+ __iscsi_segment_init(segment, size, done, hash); -+ for_each_sg(sg_list, sg, sg_count, i) { -+ debug_scsi("sg %d, len %u offset %u\n", i, sg->length, -+ sg->offset); -+ if (offset < sg->length) { -+ iscsi_tcp_segment_init_sg(segment, sg, offset); -+ return 0; - } -- tcp_conn->in.hdr = &tcp_conn->hdr; -- tcp_conn->discontiguous_hdr_cnt++; -- tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; -+ offset -= sg->length; - } - -+ return ISCSI_ERR_DATA_OFFSET; -+} -+ -+/** -+ * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception -+ * @tcp_conn: iscsi connection to prep for -+ * -+ * This function always passes NULL for the hash argument, because when this -+ * function is called we do not yet know the final size of the header and want -+ * to delay the digest processing until we know that. -+ */ -+static void -+iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn) -+{ -+ debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn, -+ tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : ""); -+ iscsi_segment_init_linear(&tcp_conn->in.segment, -+ tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr), -+ iscsi_tcp_hdr_recv_done, NULL); -+} -+ -+/* -+ * Handle incoming reply to any other type of command -+ */ -+static int -+iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment) -+{ -+ struct iscsi_conn *conn = tcp_conn->iscsi_conn; -+ int rc = 0; -+ -+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) -+ return ISCSI_ERR_DATA_DGST; -+ -+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, -+ conn->data, tcp_conn->in.datalen); -+ if (rc) -+ return rc; -+ -+ iscsi_tcp_hdr_recv_prep(tcp_conn); - return 0; - } - -+static void -+iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) -+{ -+ struct iscsi_conn *conn = tcp_conn->iscsi_conn; -+ struct hash_desc *rx_hash = NULL; -+ -+ if (conn->datadgst_en) -+ rx_hash = &tcp_conn->rx_hash; -+ -+ iscsi_segment_init_linear(&tcp_conn->in.segment, -+ conn->data, tcp_conn->in.datalen, -+ iscsi_tcp_data_recv_done, rx_hash); -+} -+ - /* - * must be called with session lock - */ -@@ -187,7 +498,6 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - { - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - struct iscsi_r2t_info *r2t; -- struct scsi_cmnd *sc; - - /* flush ctask's r2t queues */ - while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) { -@@ -196,12 +506,12 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n"); - } - -- sc = ctask->sc; -- if (unlikely(!sc)) -- return; -- -- tcp_ctask->xmstate = XMSTATE_IDLE; -- tcp_ctask->r2t = NULL; -+ r2t = tcp_ctask->r2t; -+ if (r2t != NULL) { -+ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, -+ sizeof(void*)); -+ tcp_ctask->r2t = NULL; -+ } - } - - /** -@@ -220,11 +530,6 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - int datasn = be32_to_cpu(rhdr->datasn); - - iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr); -- /* -- * setup Data-In byte counter (gets decremented..) -- */ -- ctask->data_count = tcp_conn->in.datalen; -- - if (tcp_conn->in.datalen == 0) - return 0; - -@@ -237,30 +542,28 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - tcp_ctask->exp_datasn++; - - tcp_ctask->data_offset = be32_to_cpu(rhdr->offset); -- if (tcp_ctask->data_offset + tcp_conn->in.datalen > sc->request_bufflen) { -+ if (tcp_ctask->data_offset + tcp_conn->in.datalen > scsi_bufflen(sc)) { - debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n", - __FUNCTION__, tcp_ctask->data_offset, -- tcp_conn->in.datalen, sc->request_bufflen); -+ tcp_conn->in.datalen, scsi_bufflen(sc)); - return ISCSI_ERR_DATA_OFFSET; - } - - if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) { -+ sc->result = (DID_OK << 16) | rhdr->cmd_status; - conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; -- if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) { -+ if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | -+ ISCSI_FLAG_DATA_OVERFLOW)) { - int res_count = be32_to_cpu(rhdr->residual_count); - - if (res_count > 0 && -- res_count <= sc->request_bufflen) { -- sc->resid = res_count; -- sc->result = (DID_OK << 16) | rhdr->cmd_status; -- } else -+ (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || -+ res_count <= scsi_bufflen(sc))) -+ scsi_set_resid(sc, res_count); -+ else - sc->result = (DID_BAD_TARGET << 16) | - rhdr->cmd_status; -- } else if (rhdr->flags & ISCSI_FLAG_DATA_OVERFLOW) { -- sc->resid = be32_to_cpu(rhdr->residual_count); -- sc->result = (DID_OK << 16) | rhdr->cmd_status; -- } else -- sc->result = (DID_OK << 16) | rhdr->cmd_status; -+ } - } - - conn->datain_pdus_cnt++; -@@ -284,7 +587,6 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, - struct iscsi_r2t_info *r2t) - { - struct iscsi_data *hdr; -- struct scsi_cmnd *sc = ctask->sc; - - hdr = &r2t->dtask.hdr; - memset(hdr, 0, sizeof(struct iscsi_data)); -@@ -308,43 +610,6 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, - conn->dataout_pdus_cnt++; - - r2t->sent = 0; -- -- iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr, -- sizeof(struct iscsi_hdr)); -- -- if (sc->use_sg) { -- int i, sg_count = 0; -- struct scatterlist *sg = sc->request_buffer; -- -- r2t->sg = NULL; -- for (i = 0; i < sc->use_sg; i++, sg += 1) { -- /* FIXME: prefetch ? */ -- if (sg_count + sg->length > r2t->data_offset) { -- int page_offset; -- -- /* sg page found! */ -- -- /* offset within this page */ -- page_offset = r2t->data_offset - sg_count; -- -- /* fill in this buffer */ -- iscsi_buf_init_sg(&r2t->sendbuf, sg); -- r2t->sendbuf.sg.offset += page_offset; -- r2t->sendbuf.sg.length -= page_offset; -- -- /* xmit logic will continue with next one */ -- r2t->sg = sg + 1; -- break; -- } -- sg_count += sg->length; -- } -- BUG_ON(r2t->sg == NULL); -- } else { -- iscsi_buf_init_iov(&r2t->sendbuf, -- (char*)sc->request_buffer + r2t->data_offset, -- r2t->data_count); -- r2t->sg = NULL; -- } - } - - /** -@@ -364,8 +629,9 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - int rc; - - if (tcp_conn->in.datalen) { -- printk(KERN_ERR "iscsi_tcp: invalid R2t with datalen %d\n", -- tcp_conn->in.datalen); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "invalid R2t with datalen %d\n", -+ tcp_conn->in.datalen); - return ISCSI_ERR_DATALEN; - } - -@@ -376,14 +642,12 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - } - - /* fill-in new R2T associated with the task */ -- spin_lock(&session->lock); - iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr); - -- if (!ctask->sc || ctask->mtask || -- session->state != ISCSI_STATE_LOGGED_IN) { -- printk(KERN_INFO "iscsi_tcp: dropping R2T itt %d in " -- "recovery...\n", ctask->itt); -- spin_unlock(&session->lock); -+ if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) { -+ iscsi_conn_printk(KERN_INFO, conn, -+ "dropping R2T itt %d in recovery.\n", -+ ctask->itt); - return 0; - } - -@@ -393,8 +657,10 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - r2t->exp_statsn = rhdr->statsn; - r2t->data_length = be32_to_cpu(rhdr->data_length); - if (r2t->data_length == 0) { -- printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n"); -- spin_unlock(&session->lock); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "invalid R2T with zero data len\n"); -+ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, -+ sizeof(void*)); - return ISCSI_ERR_DATALEN; - } - -@@ -404,11 +670,13 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - r2t->data_length, session->max_burst); - - r2t->data_offset = be32_to_cpu(rhdr->data_offset); -- if (r2t->data_offset + r2t->data_length > ctask->sc->request_bufflen) { -- spin_unlock(&session->lock); -- printk(KERN_ERR "iscsi_tcp: invalid R2T with data len %u at " -- "offset %u and total length %d\n", r2t->data_length, -- r2t->data_offset, ctask->sc->request_bufflen); -+ if (r2t->data_offset + r2t->data_length > scsi_bufflen(ctask->sc)) { -+ iscsi_conn_printk(KERN_ERR, conn, -+ "invalid R2T with data len %u at offset %u " -+ "and total length %d\n", r2t->data_length, -+ r2t->data_offset, scsi_bufflen(ctask->sc)); -+ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, -+ sizeof(void*)); - return ISCSI_ERR_DATALEN; - } - -@@ -419,106 +687,131 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - - tcp_ctask->exp_datasn = r2tsn + 1; - __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*)); -- tcp_ctask->xmstate |= XMSTATE_SOL_HDR_INIT; -- list_move_tail(&ctask->running, &conn->xmitqueue); -- -- scsi_queue_work(session->host, &conn->xmitwork); - conn->r2t_pdus_cnt++; -- spin_unlock(&session->lock); - -+ iscsi_requeue_ctask(ctask); - return 0; - } - -+/* -+ * Handle incoming reply to DataIn command -+ */ - static int --iscsi_tcp_hdr_recv(struct iscsi_conn *conn) -+iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment) -+{ -+ struct iscsi_conn *conn = tcp_conn->iscsi_conn; -+ struct iscsi_hdr *hdr = tcp_conn->in.hdr; -+ int rc; -+ -+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) -+ return ISCSI_ERR_DATA_DGST; -+ -+ /* check for non-exceptional status */ -+ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { -+ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); -+ if (rc) -+ return rc; -+ } -+ -+ iscsi_tcp_hdr_recv_prep(tcp_conn); -+ return 0; -+} -+ -+/** -+ * iscsi_tcp_hdr_dissect - process PDU header -+ * @conn: iSCSI connection -+ * @hdr: PDU header -+ * -+ * This function analyzes the header of the PDU received, -+ * and performs several sanity checks. If the PDU is accompanied -+ * by data, the receive buffer is set up to copy the incoming data -+ * to the correct location. -+ */ -+static int -+iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) - { - int rc = 0, opcode, ahslen; -- struct iscsi_hdr *hdr; - struct iscsi_session *session = conn->session; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- uint32_t cdgst, rdgst = 0, itt; -- -- hdr = tcp_conn->in.hdr; -+ struct iscsi_cmd_task *ctask; -+ uint32_t itt; - - /* verify PDU length */ - tcp_conn->in.datalen = ntoh24(hdr->dlength); - if (tcp_conn->in.datalen > conn->max_recv_dlength) { -- printk(KERN_ERR "iscsi_tcp: datalen %d > %d\n", -- tcp_conn->in.datalen, conn->max_recv_dlength); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "iscsi_tcp: datalen %d > %d\n", -+ tcp_conn->in.datalen, conn->max_recv_dlength); - return ISCSI_ERR_DATALEN; - } -- tcp_conn->data_copied = 0; - -- /* read AHS */ -+ /* Additional header segments. So far, we don't -+ * process additional headers. -+ */ - ahslen = hdr->hlength << 2; -- tcp_conn->in.offset += ahslen; -- tcp_conn->in.copy -= ahslen; -- if (tcp_conn->in.copy < 0) { -- printk(KERN_ERR "iscsi_tcp: can't handle AHS with length " -- "%d bytes\n", ahslen); -- return ISCSI_ERR_AHSLEN; -- } -- -- /* calculate read padding */ -- tcp_conn->in.padding = tcp_conn->in.datalen & (ISCSI_PAD_LEN-1); -- if (tcp_conn->in.padding) { -- tcp_conn->in.padding = ISCSI_PAD_LEN - tcp_conn->in.padding; -- debug_scsi("read padding %d bytes\n", tcp_conn->in.padding); -- } -- -- if (conn->hdrdgst_en) { -- struct scatterlist sg; -- -- sg_init_one(&sg, (u8 *)hdr, -- sizeof(struct iscsi_hdr) + ahslen); -- crypto_hash_digest(&tcp_conn->rx_hash, &sg, sg.length, -- (u8 *)&cdgst); -- rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) + -- ahslen); -- if (cdgst != rdgst) { -- printk(KERN_ERR "iscsi_tcp: hdrdgst error " -- "recv 0x%x calc 0x%x\n", rdgst, cdgst); -- return ISCSI_ERR_HDR_DGST; -- } -- } - - opcode = hdr->opcode & ISCSI_OPCODE_MASK; - /* verify itt (itt encoding: age+cid+itt) */ - rc = iscsi_verify_itt(conn, hdr, &itt); -- if (rc == ISCSI_ERR_NO_SCSI_CMD) { -- tcp_conn->in.datalen = 0; /* force drop */ -- return 0; -- } else if (rc) -+ if (rc) - return rc; - -- debug_tcp("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n", -- opcode, tcp_conn->in.offset, tcp_conn->in.copy, -- ahslen, tcp_conn->in.datalen); -+ debug_tcp("opcode 0x%x ahslen %d datalen %d\n", -+ opcode, ahslen, tcp_conn->in.datalen); - - switch(opcode) { - case ISCSI_OP_SCSI_DATA_IN: -- tcp_conn->in.ctask = session->cmds[itt]; -- rc = iscsi_data_rsp(conn, tcp_conn->in.ctask); -+ ctask = session->cmds[itt]; -+ spin_lock(&conn->session->lock); -+ rc = iscsi_data_rsp(conn, ctask); -+ spin_unlock(&conn->session->lock); - if (rc) - return rc; -+ if (tcp_conn->in.datalen) { -+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -+ struct hash_desc *rx_hash = NULL; -+ -+ /* -+ * Setup copy of Data-In into the Scsi_Cmnd -+ * Scatterlist case: -+ * We set up the iscsi_segment to point to the next -+ * scatterlist entry to copy to. As we go along, -+ * we move on to the next scatterlist entry and -+ * update the digest per-entry. -+ */ -+ if (conn->datadgst_en) -+ rx_hash = &tcp_conn->rx_hash; -+ -+ debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, " -+ "datalen=%d)\n", tcp_conn, -+ tcp_ctask->data_offset, -+ tcp_conn->in.datalen); -+ return iscsi_segment_seek_sg(&tcp_conn->in.segment, -+ scsi_sglist(ctask->sc), -+ scsi_sg_count(ctask->sc), -+ tcp_ctask->data_offset, -+ tcp_conn->in.datalen, -+ iscsi_tcp_process_data_in, -+ rx_hash); -+ } - /* fall through */ - case ISCSI_OP_SCSI_CMD_RSP: -- tcp_conn->in.ctask = session->cmds[itt]; -- if (tcp_conn->in.datalen) -- goto copy_hdr; -- -- spin_lock(&session->lock); -- rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); -- spin_unlock(&session->lock); -+ if (tcp_conn->in.datalen) { -+ iscsi_tcp_data_recv_prep(tcp_conn); -+ return 0; -+ } -+ rc = iscsi_complete_pdu(conn, hdr, NULL, 0); - break; - case ISCSI_OP_R2T: -- tcp_conn->in.ctask = session->cmds[itt]; -+ ctask = session->cmds[itt]; - if (ahslen) - rc = ISCSI_ERR_AHSLEN; -- else if (tcp_conn->in.ctask->sc->sc_data_direction == -- DMA_TO_DEVICE) -- rc = iscsi_r2t_rsp(conn, tcp_conn->in.ctask); -- else -+ else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) { -+ spin_lock(&session->lock); -+ rc = iscsi_r2t_rsp(conn, ctask); -+ spin_unlock(&session->lock); -+ } else - rc = ISCSI_ERR_PROTO; - break; - case ISCSI_OP_LOGIN_RSP: -@@ -530,18 +823,24 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) - * than 8K, but there are no targets that currently do this. - * For now we fail until we find a vendor that needs it - */ -- if (ISCSI_DEF_MAX_RECV_SEG_LEN < -- tcp_conn->in.datalen) { -- printk(KERN_ERR "iscsi_tcp: received buffer of len %u " -- "but conn buffer is only %u (opcode %0x)\n", -- tcp_conn->in.datalen, -- ISCSI_DEF_MAX_RECV_SEG_LEN, opcode); -+ if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) { -+ iscsi_conn_printk(KERN_ERR, conn, -+ "iscsi_tcp: received buffer of " -+ "len %u but conn buffer is only %u " -+ "(opcode %0x)\n", -+ tcp_conn->in.datalen, -+ ISCSI_DEF_MAX_RECV_SEG_LEN, opcode); - rc = ISCSI_ERR_PROTO; - break; - } - -- if (tcp_conn->in.datalen) -- goto copy_hdr; -+ /* If there's data coming in with the response, -+ * receive it to the connection's buffer. -+ */ -+ if (tcp_conn->in.datalen) { -+ iscsi_tcp_data_recv_prep(tcp_conn); -+ return 0; -+ } - /* fall through */ - case ISCSI_OP_LOGOUT_RSP: - case ISCSI_OP_NOOP_IN: -@@ -553,480 +852,161 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn) - break; - } - -- return rc; -- --copy_hdr: -- /* -- * if we did zero copy for the header but we will need multiple -- * skbs to complete the command then we have to copy the header -- * for later use -- */ -- if (tcp_conn->in.zero_copy_hdr && tcp_conn->in.copy <= -- (tcp_conn->in.datalen + tcp_conn->in.padding + -- (conn->datadgst_en ? 4 : 0))) { -- debug_tcp("Copying header for later use. in.copy %d in.datalen" -- " %d\n", tcp_conn->in.copy, tcp_conn->in.datalen); -- memcpy(&tcp_conn->hdr, tcp_conn->in.hdr, -- sizeof(struct iscsi_hdr)); -- tcp_conn->in.hdr = &tcp_conn->hdr; -- tcp_conn->in.zero_copy_hdr = 0; -- } -- return 0; --} -- --/** -- * iscsi_ctask_copy - copy skb bits to the destanation cmd task -- * @conn: iscsi tcp connection -- * @ctask: scsi command task -- * @buf: buffer to copy to -- * @buf_size: size of buffer -- * @offset: offset within the buffer -- * -- * Notes: -- * The function calls skb_copy_bits() and updates per-connection and -- * per-cmd byte counters. -- * -- * Read counters (in bytes): -- * -- * conn->in.offset offset within in progress SKB -- * conn->in.copy left to copy from in progress SKB -- * including padding -- * conn->in.copied copied already from in progress SKB -- * conn->data_copied copied already from in progress buffer -- * ctask->sent total bytes sent up to the MidLayer -- * ctask->data_count left to copy from in progress Data-In -- * buf_left left to copy from in progress buffer -- **/ --static inline int --iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask, -- void *buf, int buf_size, int offset) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- int buf_left = buf_size - (tcp_conn->data_copied + offset); -- unsigned size = min(tcp_conn->in.copy, buf_left); -- int rc; -- -- size = min(size, ctask->data_count); -- -- debug_tcp("ctask_copy %d bytes at offset %d copied %d\n", -- size, tcp_conn->in.offset, tcp_conn->in.copied); -- -- BUG_ON(size <= 0); -- BUG_ON(tcp_ctask->sent + size > ctask->sc->request_bufflen); -- -- rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, -- (char*)buf + (offset + tcp_conn->data_copied), size); -- /* must fit into skb->len */ -- BUG_ON(rc); -- -- tcp_conn->in.offset += size; -- tcp_conn->in.copy -= size; -- tcp_conn->in.copied += size; -- tcp_conn->data_copied += size; -- tcp_ctask->sent += size; -- ctask->data_count -= size; -- -- BUG_ON(tcp_conn->in.copy < 0); -- BUG_ON(ctask->data_count < 0); -- -- if (buf_size != (tcp_conn->data_copied + offset)) { -- if (!ctask->data_count) { -- BUG_ON(buf_size - tcp_conn->data_copied < 0); -- /* done with this PDU */ -- return buf_size - tcp_conn->data_copied; -- } -- return -EAGAIN; -+ if (rc == 0) { -+ /* Anything that comes with data should have -+ * been handled above. */ -+ if (tcp_conn->in.datalen) -+ return ISCSI_ERR_PROTO; -+ iscsi_tcp_hdr_recv_prep(tcp_conn); - } - -- /* done with this buffer or with both - PDU and buffer */ -- tcp_conn->data_copied = 0; -- return 0; -+ return rc; - } - - /** -- * iscsi_tcp_copy - copy skb bits to the destanation buffer -- * @conn: iscsi tcp connection -+ * iscsi_tcp_hdr_recv_done - process PDU header - * -- * Notes: -- * The function calls skb_copy_bits() and updates per-connection -- * byte counters. -- **/ --static inline int --iscsi_tcp_copy(struct iscsi_conn *conn, int buf_size) --{ -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- int buf_left = buf_size - tcp_conn->data_copied; -- int size = min(tcp_conn->in.copy, buf_left); -- int rc; -- -- debug_tcp("tcp_copy %d bytes at offset %d copied %d\n", -- size, tcp_conn->in.offset, tcp_conn->data_copied); -- BUG_ON(size <= 0); -- -- rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, -- (char*)conn->data + tcp_conn->data_copied, size); -- BUG_ON(rc); -- -- tcp_conn->in.offset += size; -- tcp_conn->in.copy -= size; -- tcp_conn->in.copied += size; -- tcp_conn->data_copied += size; -- -- if (buf_size != tcp_conn->data_copied) -- return -EAGAIN; -- -- return 0; --} -- --static inline void --partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg, -- int offset, int length) --{ -- struct scatterlist temp; -- -- memcpy(&temp, sg, sizeof(struct scatterlist)); -- temp.offset = offset; -- temp.length = length; -- crypto_hash_update(desc, &temp, length); --} -- --static void --iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len) --{ -- struct scatterlist tmp; -- -- sg_init_one(&tmp, buf, len); -- crypto_hash_update(&tcp_conn->rx_hash, &tmp, len); --} -- --static int iscsi_scsi_data_in(struct iscsi_conn *conn) -+ * This is the callback invoked when the PDU header has -+ * been received. If the header is followed by additional -+ * header segments, we go back for more data. -+ */ -+static int -+iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment) - { -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- struct iscsi_cmd_task *ctask = tcp_conn->in.ctask; -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- struct scsi_cmnd *sc = ctask->sc; -- struct scatterlist *sg; -- int i, offset, rc = 0; -- -- BUG_ON((void*)ctask != sc->SCp.ptr); -+ struct iscsi_conn *conn = tcp_conn->iscsi_conn; -+ struct iscsi_hdr *hdr; - -- /* -- * copying Data-In into the Scsi_Cmnd -+ /* Check if there are additional header segments -+ * *prior* to computing the digest, because we -+ * may need to go back to the caller for more. - */ -- if (!sc->use_sg) { -- i = ctask->data_count; -- rc = iscsi_ctask_copy(tcp_conn, ctask, sc->request_buffer, -- sc->request_bufflen, -- tcp_ctask->data_offset); -- if (rc == -EAGAIN) -- return rc; -- if (conn->datadgst_en) -- iscsi_recv_digest_update(tcp_conn, sc->request_buffer, -- i); -- rc = 0; -- goto done; -+ hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf; -+ if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) { -+ /* Bump the header length - the caller will -+ * just loop around and get the AHS for us, and -+ * call again. */ -+ unsigned int ahslen = hdr->hlength << 2; -+ -+ /* Make sure we don't overflow */ -+ if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf)) -+ return ISCSI_ERR_AHSLEN; -+ -+ segment->total_size += ahslen; -+ segment->size += ahslen; -+ return 0; - } - -- offset = tcp_ctask->data_offset; -- sg = sc->request_buffer; -- -- if (tcp_ctask->data_offset) -- for (i = 0; i < tcp_ctask->sg_count; i++) -- offset -= sg[i].length; -- /* we've passed through partial sg*/ -- if (offset < 0) -- offset = 0; -- -- for (i = tcp_ctask->sg_count; i < sc->use_sg; i++) { -- char *dest; -- -- dest = kmap_atomic(sg[i].page, KM_SOFTIRQ0); -- rc = iscsi_ctask_copy(tcp_conn, ctask, dest + sg[i].offset, -- sg[i].length, offset); -- kunmap_atomic(dest, KM_SOFTIRQ0); -- if (rc == -EAGAIN) -- /* continue with the next SKB/PDU */ -- return rc; -- if (!rc) { -- if (conn->datadgst_en) { -- if (!offset) -- crypto_hash_update( -- &tcp_conn->rx_hash, -- &sg[i], sg[i].length); -- else -- partial_sg_digest_update( -- &tcp_conn->rx_hash, -- &sg[i], -- sg[i].offset + offset, -- sg[i].length - offset); -- } -- offset = 0; -- tcp_ctask->sg_count++; -- } -- -- if (!ctask->data_count) { -- if (rc && conn->datadgst_en) -- /* -- * data-in is complete, but buffer not... -- */ -- partial_sg_digest_update(&tcp_conn->rx_hash, -- &sg[i], -- sg[i].offset, -- sg[i].length-rc); -- rc = 0; -- break; -+ /* We're done processing the header. See if we're doing -+ * header digests; if so, set up the recv_digest buffer -+ * and go back for more. */ -+ if (conn->hdrdgst_en) { -+ if (segment->digest_len == 0) { -+ iscsi_tcp_segment_splice_digest(segment, -+ segment->recv_digest); -+ return 0; - } -+ iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr, -+ segment->total_copied - ISCSI_DIGEST_SIZE, -+ segment->digest); - -- if (!tcp_conn->in.copy) -- return -EAGAIN; -- } -- BUG_ON(ctask->data_count); -- --done: -- /* check for non-exceptional status */ -- if (tcp_conn->in.hdr->flags & ISCSI_FLAG_DATA_STATUS) { -- debug_scsi("done [sc %lx res %d itt 0x%x flags 0x%x]\n", -- (long)sc, sc->result, ctask->itt, -- tcp_conn->in.hdr->flags); -- spin_lock(&conn->session->lock); -- __iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); -- spin_unlock(&conn->session->lock); -+ if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) -+ return ISCSI_ERR_HDR_DGST; - } - -- return rc; --} -- --static int --iscsi_data_recv(struct iscsi_conn *conn) --{ -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- int rc = 0, opcode; -- -- opcode = tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK; -- switch (opcode) { -- case ISCSI_OP_SCSI_DATA_IN: -- rc = iscsi_scsi_data_in(conn); -- break; -- case ISCSI_OP_SCSI_CMD_RSP: -- case ISCSI_OP_TEXT_RSP: -- case ISCSI_OP_LOGIN_RSP: -- case ISCSI_OP_ASYNC_EVENT: -- case ISCSI_OP_REJECT: -- /* -- * Collect data segment to the connection's data -- * placeholder -- */ -- if (iscsi_tcp_copy(conn, tcp_conn->in.datalen)) { -- rc = -EAGAIN; -- goto exit; -- } -- -- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, conn->data, -- tcp_conn->in.datalen); -- if (!rc && conn->datadgst_en && opcode != ISCSI_OP_LOGIN_RSP) -- iscsi_recv_digest_update(tcp_conn, conn->data, -- tcp_conn->in.datalen); -- break; -- default: -- BUG_ON(1); -- } --exit: -- return rc; -+ tcp_conn->in.hdr = hdr; -+ return iscsi_tcp_hdr_dissect(conn, hdr); - } - - /** -- * iscsi_tcp_data_recv - TCP receive in sendfile fashion -+ * iscsi_tcp_recv - TCP receive in sendfile fashion - * @rd_desc: read descriptor - * @skb: socket buffer - * @offset: offset in skb - * @len: skb->len - offset - **/ - static int --iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, -- unsigned int offset, size_t len) -+iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, -+ unsigned int offset, size_t len) - { -- int rc; - struct iscsi_conn *conn = rd_desc->arg.data; - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- int processed; -- char pad[ISCSI_PAD_LEN]; -- struct scatterlist sg; -- -- /* -- * Save current SKB and its offset in the corresponding -- * connection context. -- */ -- tcp_conn->in.copy = skb->len - offset; -- tcp_conn->in.offset = offset; -- tcp_conn->in.skb = skb; -- tcp_conn->in.len = tcp_conn->in.copy; -- BUG_ON(tcp_conn->in.copy <= 0); -- debug_tcp("in %d bytes\n", tcp_conn->in.copy); -+ struct iscsi_segment *segment = &tcp_conn->in.segment; -+ struct skb_seq_state seq; -+ unsigned int consumed = 0; -+ int rc = 0; - --more: -- tcp_conn->in.copied = 0; -- rc = 0; -+ debug_tcp("in %d bytes\n", skb->len - offset); - - if (unlikely(conn->suspend_rx)) { - debug_tcp("conn %d Rx suspended!\n", conn->id); - return 0; - } - -- if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER || -- tcp_conn->in_progress == IN_PROGRESS_HEADER_GATHER) { -- rc = iscsi_hdr_extract(tcp_conn); -- if (rc) { -- if (rc == -EAGAIN) -- goto nomore; -- else { -- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -- return 0; -- } -- } -+ skb_prepare_seq_read(skb, offset, skb->len, &seq); -+ while (1) { -+ unsigned int avail; -+ const u8 *ptr; - -- /* -- * Verify and process incoming PDU header. -- */ -- rc = iscsi_tcp_hdr_recv(conn); -- if (!rc && tcp_conn->in.datalen) { -- if (conn->datadgst_en) -- crypto_hash_init(&tcp_conn->rx_hash); -- tcp_conn->in_progress = IN_PROGRESS_DATA_RECV; -- } else if (rc) { -- iscsi_conn_failure(conn, rc); -- return 0; -+ avail = skb_seq_read(consumed, &ptr, &seq); -+ if (avail == 0) { -+ debug_tcp("no more data avail. Consumed %d\n", -+ consumed); -+ break; - } -- } -- -- if (tcp_conn->in_progress == IN_PROGRESS_DDIGEST_RECV && -- tcp_conn->in.copy) { -- uint32_t recv_digest; -- -- debug_tcp("extra data_recv offset %d copy %d\n", -- tcp_conn->in.offset, tcp_conn->in.copy); -- -- if (!tcp_conn->data_copied) { -- if (tcp_conn->in.padding) { -- debug_tcp("padding -> %d\n", -- tcp_conn->in.padding); -- memset(pad, 0, tcp_conn->in.padding); -- sg_init_one(&sg, pad, tcp_conn->in.padding); -- crypto_hash_update(&tcp_conn->rx_hash, -- &sg, sg.length); -+ BUG_ON(segment->copied >= segment->size); -+ -+ debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail); -+ rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail); -+ BUG_ON(rc == 0); -+ consumed += rc; -+ -+ if (segment->total_copied >= segment->total_size) { -+ debug_tcp("segment done\n"); -+ rc = segment->done(tcp_conn, segment); -+ if (rc != 0) { -+ skb_abort_seq_read(&seq); -+ goto error; - } -- crypto_hash_final(&tcp_conn->rx_hash, -- (u8 *) &tcp_conn->in.datadgst); -- debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst); -- } -- -- rc = iscsi_tcp_copy(conn, sizeof(uint32_t)); -- if (rc) { -- if (rc == -EAGAIN) -- goto again; -- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -- return 0; -- } - -- memcpy(&recv_digest, conn->data, sizeof(uint32_t)); -- if (recv_digest != tcp_conn->in.datadgst) { -- debug_tcp("iscsi_tcp: data digest error!" -- "0x%x != 0x%x\n", recv_digest, -- tcp_conn->in.datadgst); -- iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST); -- return 0; -- } else { -- debug_tcp("iscsi_tcp: data digest match!" -- "0x%x == 0x%x\n", recv_digest, -- tcp_conn->in.datadgst); -- tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; -- } -- } -- -- if (tcp_conn->in_progress == IN_PROGRESS_DATA_RECV && -- tcp_conn->in.copy) { -- debug_tcp("data_recv offset %d copy %d\n", -- tcp_conn->in.offset, tcp_conn->in.copy); -- -- rc = iscsi_data_recv(conn); -- if (rc) { -- if (rc == -EAGAIN) -- goto again; -- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -- return 0; -+ /* The done() functions sets up the -+ * next segment. */ - } -- -- if (tcp_conn->in.padding) -- tcp_conn->in_progress = IN_PROGRESS_PAD_RECV; -- else if (conn->datadgst_en) -- tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; -- else -- tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; -- tcp_conn->data_copied = 0; -- } -- -- if (tcp_conn->in_progress == IN_PROGRESS_PAD_RECV && -- tcp_conn->in.copy) { -- int copylen = min(tcp_conn->in.padding - tcp_conn->data_copied, -- tcp_conn->in.copy); -- -- tcp_conn->in.copy -= copylen; -- tcp_conn->in.offset += copylen; -- tcp_conn->data_copied += copylen; -- -- if (tcp_conn->data_copied != tcp_conn->in.padding) -- tcp_conn->in_progress = IN_PROGRESS_PAD_RECV; -- else if (conn->datadgst_en) -- tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; -- else -- tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; -- tcp_conn->data_copied = 0; -- } -- -- debug_tcp("f, processed %d from out of %d padding %d\n", -- tcp_conn->in.offset - offset, (int)len, tcp_conn->in.padding); -- BUG_ON(tcp_conn->in.offset - offset > len); -- -- if (tcp_conn->in.offset - offset != len) { -- debug_tcp("continue to process %d bytes\n", -- (int)len - (tcp_conn->in.offset - offset)); -- goto more; - } -+ skb_abort_seq_read(&seq); -+ conn->rxdata_octets += consumed; -+ return consumed; - --nomore: -- processed = tcp_conn->in.offset - offset; -- BUG_ON(processed == 0); -- return processed; -- --again: -- processed = tcp_conn->in.offset - offset; -- debug_tcp("c, processed %d from out of %d rd_desc_cnt %d\n", -- processed, (int)len, (int)rd_desc->count); -- BUG_ON(processed == 0); -- BUG_ON(processed > len); -- -- conn->rxdata_octets += processed; -- return processed; -+error: -+ debug_tcp("Error receiving PDU, errno=%d\n", rc); -+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -+ return 0; - } - - static void - iscsi_tcp_data_ready(struct sock *sk, int flag) - { - struct iscsi_conn *conn = sk->sk_user_data; -+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - read_descriptor_t rd_desc; - - read_lock(&sk->sk_callback_lock); - - /* -- * Use rd_desc to pass 'conn' to iscsi_tcp_data_recv. -+ * Use rd_desc to pass 'conn' to iscsi_tcp_recv. - * We set count to 1 because we want the network layer to -- * hand us all the skbs that are available. iscsi_tcp_data_recv -+ * hand us all the skbs that are available. iscsi_tcp_recv - * handled pdus that cross buffers or pdus that still need data. - */ - rd_desc.arg.data = conn; - rd_desc.count = 1; -- tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv); -+ tcp_read_sock(sk, &rd_desc, iscsi_tcp_recv); - - read_unlock(&sk->sk_callback_lock); -+ -+ /* If we had to (atomically) map a highmem page, -+ * unmap it now. */ -+ iscsi_tcp_segment_unmap(&tcp_conn->in.segment); - } - - static void -@@ -1106,121 +1086,173 @@ iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn) - } - - /** -- * iscsi_send - generic send routine -- * @sk: kernel's socket -- * @buf: buffer to write from -- * @size: actual size to write -- * @flags: socket's flags -- */ --static inline int --iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags) -+ * iscsi_xmit - TCP transmit -+ **/ -+static int -+iscsi_xmit(struct iscsi_conn *conn) - { - struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- struct socket *sk = tcp_conn->sock; -- int offset = buf->sg.offset + buf->sent, res; -+ struct iscsi_segment *segment = &tcp_conn->out.segment; -+ unsigned int consumed = 0; -+ int rc = 0; - -- /* -- * if we got use_sg=0 or are sending something we kmallocd -- * then we did not have to do kmap (kmap returns page_address) -- * -- * if we got use_sg > 0, but had to drop down, we do not -- * set clustering so this should only happen for that -- * slab case. -- */ -- if (buf->use_sendmsg) -- res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags); -- else -- res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, flags); -- -- if (res >= 0) { -- conn->txdata_octets += res; -- buf->sent += res; -- return res; -+ while (1) { -+ rc = iscsi_tcp_xmit_segment(tcp_conn, segment); -+ if (rc < 0) -+ goto error; -+ if (rc == 0) -+ break; -+ -+ consumed += rc; -+ -+ if (segment->total_copied >= segment->total_size) { -+ if (segment->done != NULL) { -+ rc = segment->done(tcp_conn, segment); -+ if (rc < 0) -+ goto error; -+ } -+ } - } - -- tcp_conn->sendpage_failures_cnt++; -- if (res == -EAGAIN) -- res = -ENOBUFS; -- else -- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -- return res; -+ debug_tcp("xmit %d bytes\n", consumed); -+ -+ conn->txdata_octets += consumed; -+ return consumed; -+ -+error: -+ /* Transmit error. We could initiate error recovery -+ * here. */ -+ debug_tcp("Error sending PDU, errno=%d\n", rc); -+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -+ return rc; - } - - /** -- * iscsi_sendhdr - send PDU Header via tcp_sendpage() -- * @conn: iscsi connection -- * @buf: buffer to write from -- * @datalen: lenght of data to be sent after the header -- * -- * Notes: -- * (Tx, Fast Path) -- **/ -+ * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit -+ */ - static inline int --iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen) -+iscsi_tcp_xmit_qlen(struct iscsi_conn *conn) - { -- int flags = 0; /* MSG_DONTWAIT; */ -- int res, size; -- -- size = buf->sg.length - buf->sent; -- BUG_ON(buf->sent + size > buf->sg.length); -- if (buf->sent + size != buf->sg.length || datalen) -- flags |= MSG_MORE; -- -- res = iscsi_send(conn, buf, size, flags); -- debug_tcp("sendhdr %d bytes, sent %d res %d\n", size, buf->sent, res); -- if (res >= 0) { -- if (size != res) -- return -EAGAIN; -- return 0; -- } -+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -+ struct iscsi_segment *segment = &tcp_conn->out.segment; - -- return res; -+ return segment->total_copied - segment->total_size; - } - --/** -- * iscsi_sendpage - send one page of iSCSI Data-Out. -- * @conn: iscsi connection -- * @buf: buffer to write from -- * @count: remaining data -- * @sent: number of bytes sent -- * -- * Notes: -- * (Tx, Fast Path) -- **/ - static inline int --iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf, -- int *count, int *sent) -+iscsi_tcp_flush(struct iscsi_conn *conn) - { -- int flags = 0; /* MSG_DONTWAIT; */ -- int res, size; -- -- size = buf->sg.length - buf->sent; -- BUG_ON(buf->sent + size > buf->sg.length); -- if (size > *count) -- size = *count; -- if (buf->sent + size != buf->sg.length || *count != size) -- flags |= MSG_MORE; -- -- res = iscsi_send(conn, buf, size, flags); -- debug_tcp("sendpage: %d bytes, sent %d left %d sent %d res %d\n", -- size, buf->sent, *count, *sent, res); -- if (res >= 0) { -- *count -= res; -- *sent += res; -- if (size != res) -+ int rc; -+ -+ while (iscsi_tcp_xmit_qlen(conn)) { -+ rc = iscsi_xmit(conn); -+ if (rc == 0) - return -EAGAIN; -- return 0; -+ if (rc < 0) -+ return rc; - } - -- return res; -+ return 0; - } - --static inline void --iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn, -- struct iscsi_tcp_cmd_task *tcp_ctask) -+/* -+ * This is called when we're done sending the header. -+ * Simply copy the data_segment to the send segment, and return. -+ */ -+static int -+iscsi_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn, -+ struct iscsi_segment *segment) -+{ -+ tcp_conn->out.segment = tcp_conn->out.data_segment; -+ debug_tcp("Header done. Next segment size %u total_size %u\n", -+ tcp_conn->out.segment.size, tcp_conn->out.segment.total_size); -+ return 0; -+} -+ -+static void -+iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen) -+{ -+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -+ -+ debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn, -+ conn->hdrdgst_en? ", digest enabled" : ""); -+ -+ /* Clear the data segment - needs to be filled in by the -+ * caller using iscsi_tcp_send_data_prep() */ -+ memset(&tcp_conn->out.data_segment, 0, sizeof(struct iscsi_segment)); -+ -+ /* If header digest is enabled, compute the CRC and -+ * place the digest into the same buffer. We make -+ * sure that both iscsi_tcp_ctask and mtask have -+ * sufficient room. -+ */ -+ if (conn->hdrdgst_en) { -+ iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen, -+ hdr + hdrlen); -+ hdrlen += ISCSI_DIGEST_SIZE; -+ } -+ -+ /* Remember header pointer for later, when we need -+ * to decide whether there's a payload to go along -+ * with the header. */ -+ tcp_conn->out.hdr = hdr; -+ -+ iscsi_segment_init_linear(&tcp_conn->out.segment, hdr, hdrlen, -+ iscsi_tcp_send_hdr_done, NULL); -+} -+ -+/* -+ * Prepare the send buffer for the payload data. -+ * Padding and checksumming will all be taken care -+ * of by the iscsi_segment routines. -+ */ -+static int -+iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg, -+ unsigned int count, unsigned int offset, -+ unsigned int len) - { -- crypto_hash_init(&tcp_conn->tx_hash); -- tcp_ctask->digest_count = 4; -+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -+ struct hash_desc *tx_hash = NULL; -+ unsigned int hdr_spec_len; -+ -+ debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__, -+ tcp_conn, offset, len, -+ conn->datadgst_en? ", digest enabled" : ""); -+ -+ /* Make sure the datalen matches what the caller -+ said he would send. */ -+ hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength); -+ WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len)); -+ -+ if (conn->datadgst_en) -+ tx_hash = &tcp_conn->tx_hash; -+ -+ return iscsi_segment_seek_sg(&tcp_conn->out.data_segment, -+ sg, count, offset, len, -+ NULL, tx_hash); -+} -+ -+static void -+iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data, -+ size_t len) -+{ -+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -+ struct hash_desc *tx_hash = NULL; -+ unsigned int hdr_spec_len; -+ -+ debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len, -+ conn->datadgst_en? ", digest enabled" : ""); -+ -+ /* Make sure the datalen matches what the caller -+ said he would send. */ -+ hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength); -+ WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len)); -+ -+ if (conn->datadgst_en) -+ tx_hash = &tcp_conn->tx_hash; -+ -+ iscsi_segment_init_linear(&tcp_conn->out.data_segment, -+ data, len, NULL, tx_hash); - } - - /** -@@ -1236,13 +1268,17 @@ iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn, - * - * Called under connection lock. - **/ --static void -+static int - iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, -- struct iscsi_r2t_info *r2t, int left) -+ struct iscsi_r2t_info *r2t) - { - struct iscsi_data *hdr; -- struct scsi_cmnd *sc = ctask->sc; -- int new_offset; -+ int new_offset, left; -+ -+ BUG_ON(r2t->data_length - r2t->sent < 0); -+ left = r2t->data_length - r2t->sent; -+ if (left == 0) -+ return 0; - - hdr = &r2t->dtask.hdr; - memset(hdr, 0, sizeof(struct iscsi_data)); -@@ -1263,50 +1299,46 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, - r2t->data_count = left; - hdr->flags = ISCSI_FLAG_CMD_FINAL; - } -- conn->dataout_pdus_cnt++; -- -- iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr, -- sizeof(struct iscsi_hdr)); -- -- if (iscsi_buf_left(&r2t->sendbuf)) -- return; -- -- if (sc->use_sg) { -- iscsi_buf_init_sg(&r2t->sendbuf, r2t->sg); -- r2t->sg += 1; -- } else { -- iscsi_buf_init_iov(&r2t->sendbuf, -- (char*)sc->request_buffer + new_offset, -- r2t->data_count); -- r2t->sg = NULL; -- } --} -- --static void iscsi_set_padding(struct iscsi_tcp_cmd_task *tcp_ctask, -- unsigned long len) --{ -- tcp_ctask->pad_count = len & (ISCSI_PAD_LEN - 1); -- if (!tcp_ctask->pad_count) -- return; - -- tcp_ctask->pad_count = ISCSI_PAD_LEN - tcp_ctask->pad_count; -- debug_scsi("write padding %d bytes\n", tcp_ctask->pad_count); -- tcp_ctask->xmstate |= XMSTATE_W_PAD; -+ conn->dataout_pdus_cnt++; -+ return 1; - } - - /** -- * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands -+ * iscsi_tcp_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands - * @conn: iscsi connection - * @ctask: scsi command task - * @sc: scsi command - **/ --static void --iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask) -+static int -+iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask) - { - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -+ struct iscsi_conn *conn = ctask->conn; -+ struct scsi_cmnd *sc = ctask->sc; -+ int err; - - BUG_ON(__kfifo_len(tcp_ctask->r2tqueue)); -- tcp_ctask->xmstate = XMSTATE_CMD_HDR_INIT; -+ tcp_ctask->sent = 0; -+ tcp_ctask->exp_datasn = 0; -+ -+ /* Prepare PDU, optionally w/ immediate data */ -+ debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n", -+ conn->id, ctask->itt, ctask->imm_count, -+ ctask->unsol_count); -+ iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len); -+ -+ if (!ctask->imm_count) -+ return 0; -+ -+ /* If we have immediate data, attach a payload */ -+ err = iscsi_tcp_send_data_prep(conn, scsi_sglist(sc), scsi_sg_count(sc), -+ 0, ctask->imm_count); -+ if (err) -+ return err; -+ tcp_ctask->sent += ctask->imm_count; -+ ctask->imm_count = 0; -+ return 0; - } - - /** -@@ -1318,493 +1350,130 @@ iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask) - * The function can return -EAGAIN in which case caller must - * call it again later, or recover. '0' return code means successful - * xmit. -- * -- * Management xmit state machine consists of these states: -- * XMSTATE_IMM_HDR_INIT - calculate digest of PDU Header -- * XMSTATE_IMM_HDR - PDU Header xmit in progress -- * XMSTATE_IMM_DATA - PDU Data xmit in progress -- * XMSTATE_IDLE - management PDU is done - **/ - static int - iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask) - { -- struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; - int rc; - -- debug_scsi("mtask deq [cid %d state %x itt 0x%x]\n", -- conn->id, tcp_mtask->xmstate, mtask->itt); -- -- if (tcp_mtask->xmstate & XMSTATE_IMM_HDR_INIT) { -- iscsi_buf_init_iov(&tcp_mtask->headbuf, (char*)mtask->hdr, -- sizeof(struct iscsi_hdr)); -- -- if (mtask->data_count) { -- tcp_mtask->xmstate |= XMSTATE_IMM_DATA; -- iscsi_buf_init_iov(&tcp_mtask->sendbuf, -- (char*)mtask->data, -- mtask->data_count); -- } -- -- if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE && -- conn->stop_stage != STOP_CONN_RECOVER && -- conn->hdrdgst_en) -- iscsi_hdr_digest(conn, &tcp_mtask->headbuf, -- (u8*)tcp_mtask->hdrext); -- -- tcp_mtask->sent = 0; -- tcp_mtask->xmstate &= ~XMSTATE_IMM_HDR_INIT; -- tcp_mtask->xmstate |= XMSTATE_IMM_HDR; -- } -- -- if (tcp_mtask->xmstate & XMSTATE_IMM_HDR) { -- rc = iscsi_sendhdr(conn, &tcp_mtask->headbuf, -- mtask->data_count); -- if (rc) -- return rc; -- tcp_mtask->xmstate &= ~XMSTATE_IMM_HDR; -- } -- -- if (tcp_mtask->xmstate & XMSTATE_IMM_DATA) { -- BUG_ON(!mtask->data_count); -- tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA; -- /* FIXME: implement. -- * Virtual buffer could be spreaded across multiple pages... -- */ -- do { -- int rc; -- -- rc = iscsi_sendpage(conn, &tcp_mtask->sendbuf, -- &mtask->data_count, &tcp_mtask->sent); -- if (rc) { -- tcp_mtask->xmstate |= XMSTATE_IMM_DATA; -- return rc; -- } -- } while (mtask->data_count); -- } -+ /* Flush any pending data first. */ -+ rc = iscsi_tcp_flush(conn); -+ if (rc < 0) -+ return rc; - -- BUG_ON(tcp_mtask->xmstate != XMSTATE_IDLE); - if (mtask->hdr->itt == RESERVED_ITT) { - struct iscsi_session *session = conn->session; - - spin_lock_bh(&session->lock); -- list_del(&conn->mtask->running); -- __kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask, -- sizeof(void*)); -+ iscsi_free_mgmt_task(conn, mtask); - spin_unlock_bh(&session->lock); - } -+ - return 0; - } - -+/* -+ * iscsi_tcp_ctask_xmit - xmit normal PDU task -+ * @conn: iscsi connection -+ * @ctask: iscsi command task -+ * -+ * We're expected to return 0 when everything was transmitted succesfully, -+ * -EAGAIN if there's still data in the queue, or != 0 for any other kind -+ * of error. -+ */ - static int --iscsi_send_cmd_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) -+iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) - { -- struct scsi_cmnd *sc = ctask->sc; - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -+ struct scsi_cmnd *sc = ctask->sc; - int rc = 0; - -- if (tcp_ctask->xmstate & XMSTATE_CMD_HDR_INIT) { -- tcp_ctask->sent = 0; -- tcp_ctask->sg_count = 0; -- tcp_ctask->exp_datasn = 0; -- -- if (sc->sc_data_direction == DMA_TO_DEVICE) { -- if (sc->use_sg) { -- struct scatterlist *sg = sc->request_buffer; -- -- iscsi_buf_init_sg(&tcp_ctask->sendbuf, sg); -- tcp_ctask->sg = sg + 1; -- tcp_ctask->bad_sg = sg + sc->use_sg; -- } else { -- iscsi_buf_init_iov(&tcp_ctask->sendbuf, -- sc->request_buffer, -- sc->request_bufflen); -- tcp_ctask->sg = NULL; -- tcp_ctask->bad_sg = NULL; -- } -- -- debug_scsi("cmd [itt 0x%x total %d imm_data %d " -- "unsol count %d, unsol offset %d]\n", -- ctask->itt, sc->request_bufflen, -- ctask->imm_count, ctask->unsol_count, -- ctask->unsol_offset); -- } -- -- iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr, -- sizeof(struct iscsi_hdr)); -- -- if (conn->hdrdgst_en) -- iscsi_hdr_digest(conn, &tcp_ctask->headbuf, -- (u8*)tcp_ctask->hdrext); -- tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_INIT; -- tcp_ctask->xmstate |= XMSTATE_CMD_HDR_XMIT; -- } -- -- if (tcp_ctask->xmstate & XMSTATE_CMD_HDR_XMIT) { -- rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count); -- if (rc) -- return rc; -- tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_XMIT; -- -- if (sc->sc_data_direction != DMA_TO_DEVICE) -- return 0; -- -- if (ctask->imm_count) { -- tcp_ctask->xmstate |= XMSTATE_IMM_DATA; -- iscsi_set_padding(tcp_ctask, ctask->imm_count); -- -- if (ctask->conn->datadgst_en) { -- iscsi_data_digest_init(ctask->conn->dd_data, -- tcp_ctask); -- tcp_ctask->immdigest = 0; -- } -- } -- -- if (ctask->unsol_count) -- tcp_ctask->xmstate |= -- XMSTATE_UNS_HDR | XMSTATE_UNS_INIT; -- } -- return rc; --} -- --static int --iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- int sent = 0, rc; -- -- if (tcp_ctask->xmstate & XMSTATE_W_PAD) { -- iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad, -- tcp_ctask->pad_count); -- if (conn->datadgst_en) -- crypto_hash_update(&tcp_conn->tx_hash, -- &tcp_ctask->sendbuf.sg, -- tcp_ctask->sendbuf.sg.length); -- } else if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_PAD)) -- return 0; -- -- tcp_ctask->xmstate &= ~XMSTATE_W_PAD; -- tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_PAD; -- debug_scsi("sending %d pad bytes for itt 0x%x\n", -- tcp_ctask->pad_count, ctask->itt); -- rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count, -- &sent); -- if (rc) { -- debug_scsi("padding send failed %d\n", rc); -- tcp_ctask->xmstate |= XMSTATE_W_RESEND_PAD; -- } -- return rc; --} -- --static int --iscsi_send_digest(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, -- struct iscsi_buf *buf, uint32_t *digest) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask; -- struct iscsi_tcp_conn *tcp_conn; -- int rc, sent = 0; -- -- if (!conn->datadgst_en) -- return 0; -- -- tcp_ctask = ctask->dd_data; -- tcp_conn = conn->dd_data; -- -- if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_DATA_DIGEST)) { -- crypto_hash_final(&tcp_conn->tx_hash, (u8*)digest); -- iscsi_buf_init_iov(buf, (char*)digest, 4); -- } -- tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_DATA_DIGEST; -- -- rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent); -- if (!rc) -- debug_scsi("sent digest 0x%x for itt 0x%x\n", *digest, -- ctask->itt); -- else { -- debug_scsi("sending digest 0x%x failed for itt 0x%x!\n", -- *digest, ctask->itt); -- tcp_ctask->xmstate |= XMSTATE_W_RESEND_DATA_DIGEST; -- } -- return rc; --} -- --static int --iscsi_send_data(struct iscsi_cmd_task *ctask, struct iscsi_buf *sendbuf, -- struct scatterlist **sg, int *sent, int *count, -- struct iscsi_buf *digestbuf, uint32_t *digest) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- struct iscsi_conn *conn = ctask->conn; -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; -- int rc, buf_sent, offset; -- -- while (*count) { -- buf_sent = 0; -- offset = sendbuf->sent; -- -- rc = iscsi_sendpage(conn, sendbuf, count, &buf_sent); -- *sent = *sent + buf_sent; -- if (buf_sent && conn->datadgst_en) -- partial_sg_digest_update(&tcp_conn->tx_hash, -- &sendbuf->sg, sendbuf->sg.offset + offset, -- buf_sent); -- if (!iscsi_buf_left(sendbuf) && *sg != tcp_ctask->bad_sg) { -- iscsi_buf_init_sg(sendbuf, *sg); -- *sg = *sg + 1; -- } -- -- if (rc) -- return rc; -- } -- -- rc = iscsi_send_padding(conn, ctask); -- if (rc) -+flush: -+ /* Flush any pending data first. */ -+ rc = iscsi_tcp_flush(conn); -+ if (rc < 0) - return rc; - -- return iscsi_send_digest(conn, ctask, digestbuf, digest); --} -- --static int --iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- struct iscsi_data_task *dtask; -- int rc; -- -- tcp_ctask->xmstate |= XMSTATE_UNS_DATA; -- if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) { -- dtask = &tcp_ctask->unsol_dtask; -- -- iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr); -- iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr, -- sizeof(struct iscsi_hdr)); -- if (conn->hdrdgst_en) -- iscsi_hdr_digest(conn, &tcp_ctask->headbuf, -- (u8*)dtask->hdrext); -- -- tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT; -- iscsi_set_padding(tcp_ctask, ctask->data_count); -- } -- -- rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->data_count); -- if (rc) { -- tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; -- tcp_ctask->xmstate |= XMSTATE_UNS_HDR; -- return rc; -- } -+ /* Are we done already? */ -+ if (sc->sc_data_direction != DMA_TO_DEVICE) -+ return 0; - -- if (conn->datadgst_en) { -- dtask = &tcp_ctask->unsol_dtask; -- iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); -- dtask->digest = 0; -- } -+ if (ctask->unsol_count != 0) { -+ struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr; - -- debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n", -- ctask->itt, ctask->unsol_count, tcp_ctask->sent); -- return 0; --} -+ /* Prepare a header for the unsolicited PDU. -+ * The amount of data we want to send will be -+ * in ctask->data_count. -+ * FIXME: return the data count instead. -+ */ -+ iscsi_prep_unsolicit_data_pdu(ctask, hdr); - --static int --iscsi_send_unsol_pdu(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- int rc; -+ debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n", -+ ctask->itt, tcp_ctask->sent, ctask->data_count); - -- if (tcp_ctask->xmstate & XMSTATE_UNS_HDR) { -- BUG_ON(!ctask->unsol_count); -- tcp_ctask->xmstate &= ~XMSTATE_UNS_HDR; --send_hdr: -- rc = iscsi_send_unsol_hdr(conn, ctask); -+ iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr)); -+ rc = iscsi_tcp_send_data_prep(conn, scsi_sglist(sc), -+ scsi_sg_count(sc), -+ tcp_ctask->sent, -+ ctask->data_count); - if (rc) -- return rc; -- } -- -- if (tcp_ctask->xmstate & XMSTATE_UNS_DATA) { -- struct iscsi_data_task *dtask = &tcp_ctask->unsol_dtask; -- int start = tcp_ctask->sent; -+ goto fail; -+ tcp_ctask->sent += ctask->data_count; -+ ctask->unsol_count -= ctask->data_count; -+ goto flush; -+ } else { -+ struct iscsi_session *session = conn->session; -+ struct iscsi_r2t_info *r2t; - -- rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg, -- &tcp_ctask->sent, &ctask->data_count, -- &dtask->digestbuf, &dtask->digest); -- ctask->unsol_count -= tcp_ctask->sent - start; -- if (rc) -- return rc; -- tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; -- /* -- * Done with the Data-Out. Next, check if we need -- * to send another unsolicited Data-Out. -+ /* All unsolicited PDUs sent. Check for solicited PDUs. - */ -- if (ctask->unsol_count) { -- debug_scsi("sending more uns\n"); -- tcp_ctask->xmstate |= XMSTATE_UNS_INIT; -- goto send_hdr; -+ spin_lock_bh(&session->lock); -+ r2t = tcp_ctask->r2t; -+ if (r2t != NULL) { -+ /* Continue with this R2T? */ -+ if (!iscsi_solicit_data_cont(conn, ctask, r2t)) { -+ debug_scsi(" done with r2t %p\n", r2t); -+ -+ __kfifo_put(tcp_ctask->r2tpool.queue, -+ (void*)&r2t, sizeof(void*)); -+ tcp_ctask->r2t = r2t = NULL; -+ } - } -- } -- return 0; --} - --static int iscsi_send_sol_pdu(struct iscsi_conn *conn, -- struct iscsi_cmd_task *ctask) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- struct iscsi_session *session = conn->session; -- struct iscsi_r2t_info *r2t; -- struct iscsi_data_task *dtask; -- int left, rc; -- -- if (tcp_ctask->xmstate & XMSTATE_SOL_HDR_INIT) { -- if (!tcp_ctask->r2t) { -- spin_lock_bh(&session->lock); -+ if (r2t == NULL) { - __kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t, - sizeof(void*)); -- spin_unlock_bh(&session->lock); -+ r2t = tcp_ctask->r2t; - } --send_hdr: -- r2t = tcp_ctask->r2t; -- dtask = &r2t->dtask; -- -- if (conn->hdrdgst_en) -- iscsi_hdr_digest(conn, &r2t->headbuf, -- (u8*)dtask->hdrext); -- tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR_INIT; -- tcp_ctask->xmstate |= XMSTATE_SOL_HDR; -- } -- -- if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) { -- r2t = tcp_ctask->r2t; -- dtask = &r2t->dtask; -- -- rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count); -- if (rc) -- return rc; -- tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; -- tcp_ctask->xmstate |= XMSTATE_SOL_DATA; -+ spin_unlock_bh(&session->lock); - -- if (conn->datadgst_en) { -- iscsi_data_digest_init(conn->dd_data, tcp_ctask); -- dtask->digest = 0; -+ /* Waiting for more R2Ts to arrive. */ -+ if (r2t == NULL) { -+ debug_tcp("no R2Ts yet\n"); -+ return 0; - } - -- iscsi_set_padding(tcp_ctask, r2t->data_count); -- debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n", -- r2t->solicit_datasn - 1, ctask->itt, r2t->data_count, -- r2t->sent); -- } -+ debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n", -+ r2t, r2t->solicit_datasn - 1, ctask->itt, -+ r2t->data_offset + r2t->sent, r2t->data_count); - -- if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) { -- r2t = tcp_ctask->r2t; -- dtask = &r2t->dtask; -+ iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr, -+ sizeof(struct iscsi_hdr)); - -- rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg, -- &r2t->sent, &r2t->data_count, -- &dtask->digestbuf, &dtask->digest); -+ rc = iscsi_tcp_send_data_prep(conn, scsi_sglist(sc), -+ scsi_sg_count(sc), -+ r2t->data_offset + r2t->sent, -+ r2t->data_count); - if (rc) -- return rc; -- tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; -- -- /* -- * Done with this Data-Out. Next, check if we have -- * to send another Data-Out for this R2T. -- */ -- BUG_ON(r2t->data_length - r2t->sent < 0); -- left = r2t->data_length - r2t->sent; -- if (left) { -- iscsi_solicit_data_cont(conn, ctask, r2t, left); -- goto send_hdr; -- } -- -- /* -- * Done with this R2T. Check if there are more -- * outstanding R2Ts ready to be processed. -- */ -- spin_lock_bh(&session->lock); -- tcp_ctask->r2t = NULL; -- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, -- sizeof(void*)); -- if (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, -- sizeof(void*))) { -- tcp_ctask->r2t = r2t; -- spin_unlock_bh(&session->lock); -- goto send_hdr; -- } -- spin_unlock_bh(&session->lock); -+ goto fail; -+ tcp_ctask->sent += r2t->data_count; -+ r2t->sent += r2t->data_count; -+ goto flush; - } - return 0; --} -- --/** -- * iscsi_tcp_ctask_xmit - xmit normal PDU task -- * @conn: iscsi connection -- * @ctask: iscsi command task -- * -- * Notes: -- * The function can return -EAGAIN in which case caller must -- * call it again later, or recover. '0' return code means successful -- * xmit. -- * The function is devided to logical helpers (above) for the different -- * xmit stages. -- * -- *iscsi_send_cmd_hdr() -- * XMSTATE_CMD_HDR_INIT - prepare Header and Data buffers Calculate -- * Header Digest -- * XMSTATE_CMD_HDR_XMIT - Transmit header in progress -- * -- *iscsi_send_padding -- * XMSTATE_W_PAD - Prepare and send pading -- * XMSTATE_W_RESEND_PAD - retry send pading -- * -- *iscsi_send_digest -- * XMSTATE_W_RESEND_DATA_DIGEST - Finalize and send Data Digest -- * XMSTATE_W_RESEND_DATA_DIGEST - retry sending digest -- * -- *iscsi_send_unsol_hdr -- * XMSTATE_UNS_INIT - prepare un-solicit data header and digest -- * XMSTATE_UNS_HDR - send un-solicit header -- * -- *iscsi_send_unsol_pdu -- * XMSTATE_UNS_DATA - send un-solicit data in progress -- * -- *iscsi_send_sol_pdu -- * XMSTATE_SOL_HDR_INIT - solicit data header and digest initialize -- * XMSTATE_SOL_HDR - send solicit header -- * XMSTATE_SOL_DATA - send solicit data -- * -- *iscsi_tcp_ctask_xmit -- * XMSTATE_IMM_DATA - xmit managment data (??) -- **/ --static int --iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) --{ -- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; -- int rc = 0; -- -- debug_scsi("ctask deq [cid %d xmstate %x itt 0x%x]\n", -- conn->id, tcp_ctask->xmstate, ctask->itt); -- -- rc = iscsi_send_cmd_hdr(conn, ctask); -- if (rc) -- return rc; -- if (ctask->sc->sc_data_direction != DMA_TO_DEVICE) -- return 0; -- -- if (tcp_ctask->xmstate & XMSTATE_IMM_DATA) { -- rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg, -- &tcp_ctask->sent, &ctask->imm_count, -- &tcp_ctask->immbuf, &tcp_ctask->immdigest); -- if (rc) -- return rc; -- tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA; -- } -- -- rc = iscsi_send_unsol_pdu(conn, ctask); -- if (rc) -- return rc; -- -- rc = iscsi_send_sol_pdu(conn, ctask); -- if (rc) -- return rc; -- -- return rc; -+fail: -+ iscsi_conn_failure(conn, rc); -+ return -EIO; - } - - static struct iscsi_cls_conn * -@@ -1830,37 +1499,29 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) - - conn->dd_data = tcp_conn; - tcp_conn->iscsi_conn = conn; -- tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; -- /* initial operational parameters */ -- tcp_conn->hdr_size = sizeof(struct iscsi_hdr); - - tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, - CRYPTO_ALG_ASYNC); - tcp_conn->tx_hash.flags = 0; -- if (IS_ERR(tcp_conn->tx_hash.tfm)) { -- printk(KERN_ERR "Could not create connection due to crc32c " -- "loading error %ld. Make sure the crc32c module is " -- "built as a module or into the kernel\n", -- PTR_ERR(tcp_conn->tx_hash.tfm)); -+ if (IS_ERR(tcp_conn->tx_hash.tfm)) - goto free_tcp_conn; -- } - - tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0, - CRYPTO_ALG_ASYNC); - tcp_conn->rx_hash.flags = 0; -- if (IS_ERR(tcp_conn->rx_hash.tfm)) { -- printk(KERN_ERR "Could not create connection due to crc32c " -- "loading error %ld. Make sure the crc32c module is " -- "built as a module or into the kernel\n", -- PTR_ERR(tcp_conn->rx_hash.tfm)); -+ if (IS_ERR(tcp_conn->rx_hash.tfm)) - goto free_tx_tfm; -- } - - return cls_conn; - - free_tx_tfm: - crypto_free_hash(tcp_conn->tx_hash.tfm); - free_tcp_conn: -+ iscsi_conn_printk(KERN_ERR, conn, -+ "Could not create connection due to crc32c " -+ "loading error. Make sure the crc32c " -+ "module is built as a module or into the " -+ "kernel\n"); - kfree(tcp_conn); - tcp_conn_alloc_fail: - iscsi_conn_teardown(cls_conn); -@@ -1909,11 +1570,9 @@ static void - iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) - { - struct iscsi_conn *conn = cls_conn->dd_data; -- struct iscsi_tcp_conn *tcp_conn = conn->dd_data; - - iscsi_conn_stop(cls_conn, flag); - iscsi_tcp_release_conn(conn); -- tcp_conn->hdr_size = sizeof(struct iscsi_hdr); - } - - static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock, -@@ -1926,7 +1585,7 @@ static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock, - struct sockaddr_in *sin; - int rc = 0, len; - -- addr = kmalloc(GFP_KERNEL, sizeof(*addr)); -+ addr = kmalloc(sizeof(*addr), GFP_KERNEL); - if (!addr) - return -ENOMEM; - -@@ -1970,7 +1629,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session, - /* lookup for existing socket */ - sock = sockfd_lookup((int)transport_eph, &err); - if (!sock) { -- printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "sockfd_lookup failed %d\n", err); - return -EEXIST; - } - /* -@@ -2013,7 +1673,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session, - /* - * set receive state machine into initial state - */ -- tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; -+ iscsi_tcp_hdr_recv_prep(tcp_conn); - return 0; - - free_socket: -@@ -2023,10 +1683,17 @@ free_socket: - - /* called with host lock */ - static void --iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask) -+iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask) - { -- struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; -- tcp_mtask->xmstate = XMSTATE_IMM_HDR_INIT; -+ debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt); -+ -+ /* Prepare PDU, optionally w/ immediate data */ -+ iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr)); -+ -+ /* If we have immediate data, attach a payload */ -+ if (mtask->data_count) -+ iscsi_tcp_send_linear_data_prepare(conn, mtask->data, -+ mtask->data_count); - } - - static int -@@ -2049,8 +1716,7 @@ iscsi_r2tpool_alloc(struct iscsi_session *session) - */ - - /* R2T pool */ -- if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, -- (void***)&tcp_ctask->r2ts, -+ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL, - sizeof(struct iscsi_r2t_info))) { - goto r2t_alloc_fail; - } -@@ -2059,8 +1725,7 @@ iscsi_r2tpool_alloc(struct iscsi_session *session) - tcp_ctask->r2tqueue = kfifo_alloc( - session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL); - if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) { -- iscsi_pool_free(&tcp_ctask->r2tpool, -- (void**)tcp_ctask->r2ts); -+ iscsi_pool_free(&tcp_ctask->r2tpool); - goto r2t_alloc_fail; - } - } -@@ -2073,8 +1738,7 @@ r2t_alloc_fail: - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - - kfifo_free(tcp_ctask->r2tqueue); -- iscsi_pool_free(&tcp_ctask->r2tpool, -- (void**)tcp_ctask->r2ts); -+ iscsi_pool_free(&tcp_ctask->r2tpool); - } - return -ENOMEM; - } -@@ -2089,8 +1753,7 @@ iscsi_r2tpool_free(struct iscsi_session *session) - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - - kfifo_free(tcp_ctask->r2tqueue); -- iscsi_pool_free(&tcp_ctask->r2tpool, -- (void**)tcp_ctask->r2ts); -+ iscsi_pool_free(&tcp_ctask->r2tpool); - } - } - -@@ -2106,9 +1769,6 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, - switch(param) { - case ISCSI_PARAM_HDRDGST_EN: - iscsi_set_param(cls_conn, param, buf, buflen); -- tcp_conn->hdr_size = sizeof(struct iscsi_hdr); -- if (conn->hdrdgst_en) -- tcp_conn->hdr_size += sizeof(__u32); - break; - case ISCSI_PARAM_DATADGST_EN: - iscsi_set_param(cls_conn, param, buf, buflen); -@@ -2117,12 +1777,12 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, - break; - case ISCSI_PARAM_MAX_R2T: - sscanf(buf, "%d", &value); -- if (session->max_r2t == roundup_pow_of_two(value)) -+ if (value <= 0 || !is_power_of_2(value)) -+ return -EINVAL; -+ if (session->max_r2t == value) - break; - iscsi_r2tpool_free(session); - iscsi_set_param(cls_conn, param, buf, buflen); -- if (session->max_r2t & (session->max_r2t - 1)) -- session->max_r2t = roundup_pow_of_two(session->max_r2t); - if (iscsi_r2tpool_alloc(session)) - return -ENOMEM; - break; -@@ -2229,14 +1889,15 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit, - struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; - struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; - -- ctask->hdr = &tcp_ctask->hdr; -+ ctask->hdr = &tcp_ctask->hdr.cmd_hdr; -+ ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE; - } - - for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { - struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i]; - struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; - -- mtask->hdr = &tcp_mtask->hdr; -+ mtask->hdr = (struct iscsi_hdr *) &tcp_mtask->hdr; - } - - if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session))) -@@ -2257,19 +1918,22 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session) - - static int iscsi_tcp_slave_configure(struct scsi_device *sdev) - { -+ blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY); - blk_queue_dma_alignment(sdev->request_queue, 0); - return 0; - } - - static struct scsi_host_template iscsi_sht = { -+ .module = THIS_MODULE, - .name = "iSCSI Initiator over TCP/IP", - .queuecommand = iscsi_queuecommand, - .change_queue_depth = iscsi_change_queue_depth, - .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1, -- .sg_tablesize = ISCSI_SG_TABLESIZE, -+ .sg_tablesize = 4096, - .max_sectors = 0xFFFF, - .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, - .eh_abort_handler = iscsi_eh_abort, -+ .eh_device_reset_handler= iscsi_eh_device_reset, - .eh_host_reset_handler = iscsi_eh_host_reset, - .use_clustering = DISABLE_CLUSTERING, - .slave_configure = iscsi_tcp_slave_configure, -@@ -2301,14 +1965,18 @@ static struct iscsi_transport iscsi_tcp_transport = { - ISCSI_PERSISTENT_ADDRESS | - ISCSI_TARGET_NAME | ISCSI_TPGT | - ISCSI_USERNAME | ISCSI_PASSWORD | -- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN, -+ ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | -+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | -+ ISCSI_LU_RESET_TMO | -+ ISCSI_PING_TMO | ISCSI_RECV_TMO | -+ ISCSI_IFACE_NAME, - .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | - ISCSI_HOST_INITIATOR_NAME | - ISCSI_HOST_NETDEV_NAME, - .host_template = &iscsi_sht, - .conndata_size = sizeof(struct iscsi_conn), - .max_conn = 1, -- .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN, -+ .max_cmd_len = 16, - /* session management */ - .create_session = iscsi_tcp_session_create, - .destroy_session = iscsi_tcp_session_destroy, -@@ -2327,8 +1995,8 @@ static struct iscsi_transport iscsi_tcp_transport = { - /* IO */ - .send_pdu = iscsi_conn_send_pdu, - .get_stats = iscsi_conn_get_stats, -- .init_cmd_task = iscsi_tcp_cmd_init, -- .init_mgmt_task = iscsi_tcp_mgmt_init, -+ .init_cmd_task = iscsi_tcp_ctask_init, -+ .init_mgmt_task = iscsi_tcp_mtask_init, - .xmit_cmd_task = iscsi_tcp_ctask_xmit, - .xmit_mgmt_task = iscsi_tcp_mtask_xmit, - .cleanup_cmd_task = iscsi_tcp_cleanup_ctask, -diff --git a/kernel/iscsi_tcp.h b/kernel/iscsi_tcp.h -index 4b86fe4..950d75f 100644 ---- a/kernel/iscsi_tcp.h -+++ b/kernel/iscsi_tcp.h -@@ -24,71 +24,61 @@ - - #include "libiscsi.h" - --/* Socket's Receive state machine */ --#define IN_PROGRESS_WAIT_HEADER 0x0 --#define IN_PROGRESS_HEADER_GATHER 0x1 --#define IN_PROGRESS_DATA_RECV 0x2 --#define IN_PROGRESS_DDIGEST_RECV 0x3 --#define IN_PROGRESS_PAD_RECV 0x4 -- --/* xmit state machine */ --#define XMSTATE_IDLE 0x0 --#define XMSTATE_CMD_HDR_INIT 0x1 --#define XMSTATE_CMD_HDR_XMIT 0x2 --#define XMSTATE_IMM_HDR 0x4 --#define XMSTATE_IMM_DATA 0x8 --#define XMSTATE_UNS_INIT 0x10 --#define XMSTATE_UNS_HDR 0x20 --#define XMSTATE_UNS_DATA 0x40 --#define XMSTATE_SOL_HDR 0x80 --#define XMSTATE_SOL_DATA 0x100 --#define XMSTATE_W_PAD 0x200 --#define XMSTATE_W_RESEND_PAD 0x400 --#define XMSTATE_W_RESEND_DATA_DIGEST 0x800 --#define XMSTATE_IMM_HDR_INIT 0x1000 --#define XMSTATE_SOL_HDR_INIT 0x2000 -- --#define ISCSI_PAD_LEN 4 --#define ISCSI_SG_TABLESIZE SG_ALL --#define ISCSI_TCP_MAX_CMD_LEN 16 -- - struct crypto_hash; - struct socket; -+struct iscsi_tcp_conn; -+struct iscsi_segment; -+ -+typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *, -+ struct iscsi_segment *); -+ -+struct iscsi_segment { -+ unsigned char *data; -+ unsigned int size; -+ unsigned int copied; -+ unsigned int total_size; -+ unsigned int total_copied; -+ -+ struct hash_desc *hash; -+ unsigned char recv_digest[ISCSI_DIGEST_SIZE]; -+ unsigned char digest[ISCSI_DIGEST_SIZE]; -+ unsigned int digest_len; -+ -+ struct scatterlist *sg; -+ void *sg_mapped; -+ unsigned int sg_offset; -+ -+ iscsi_segment_done_fn_t *done; -+}; - - /* Socket connection recieve helper */ - struct iscsi_tcp_recv { - struct iscsi_hdr *hdr; -- struct sk_buff *skb; -- int offset; -- int len; -- int hdr_offset; -- int copy; -- int copied; -- int padding; -- struct iscsi_cmd_task *ctask; /* current cmd in progress */ -+ struct iscsi_segment segment; -+ -+ /* Allocate buffer for BHS + AHS */ -+ uint32_t hdr_buf[64]; - - /* copied and flipped values */ - int datalen; -- int datadgst; -- char zero_copy_hdr; -+}; -+ -+/* Socket connection send helper */ -+struct iscsi_tcp_send { -+ struct iscsi_hdr *hdr; -+ struct iscsi_segment segment; -+ struct iscsi_segment data_segment; - }; - - struct iscsi_tcp_conn { - struct iscsi_conn *iscsi_conn; - struct socket *sock; -- struct iscsi_hdr hdr; /* header placeholder */ -- char hdrext[4*sizeof(__u16) + -- sizeof(__u32)]; -- int data_copied; - int stop_stage; /* conn_stop() flag: * - * stop to recover, * - * stop to terminate */ -- /* iSCSI connection-wide sequencing */ -- int hdr_size; /* PDU header size */ -- - /* control data */ - struct iscsi_tcp_recv in; /* TCP receive context */ -- int in_progress; /* connection state machine */ -+ struct iscsi_tcp_send out; /* TCP send context */ - - /* old values for socket callbacks */ - void (*old_data_ready)(struct sock *, int); -@@ -103,29 +93,19 @@ struct iscsi_tcp_conn { - uint32_t sendpage_failures_cnt; - uint32_t discontiguous_hdr_cnt; - -- ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); --}; -+ int error; - --struct iscsi_buf { -- struct scatterlist sg; -- unsigned int sent; -- char use_sendmsg; -+ ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); - }; - - struct iscsi_data_task { - struct iscsi_data hdr; /* PDU */ -- char hdrext[sizeof(__u32)]; /* Header-Digest */ -- struct iscsi_buf digestbuf; /* digest buffer */ -- uint32_t digest; /* data digest */ -+ char hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */ - }; - - struct iscsi_tcp_mgmt_task { - struct iscsi_hdr hdr; -- char hdrext[sizeof(__u32)]; /* Header-Digest */ -- int xmstate; /* mgmt xmit progress */ -- struct iscsi_buf headbuf; /* header buffer */ -- struct iscsi_buf sendbuf; /* in progress buffer */ -- int sent; -+ char hdrext[ISCSI_DIGEST_SIZE]; /* Header-Digest */ - }; - - struct iscsi_r2t_info { -@@ -133,38 +113,26 @@ struct iscsi_r2t_info { - __be32 exp_statsn; /* copied from R2T */ - uint32_t data_length; /* copied from R2T */ - uint32_t data_offset; /* copied from R2T */ -- struct iscsi_buf headbuf; /* Data-Out Header Buffer */ -- struct iscsi_buf sendbuf; /* Data-Out in progress buffer*/ - int sent; /* R2T sequence progress */ - int data_count; /* DATA-Out payload progress */ -- struct scatterlist *sg; /* per-R2T SG list */ - int solicit_datasn; -- struct iscsi_data_task dtask; /* which data task */ -+ struct iscsi_data_task dtask; /* Data-Out header buf */ - }; - - struct iscsi_tcp_cmd_task { -- struct iscsi_cmd hdr; -- char hdrext[4*sizeof(__u16)+ /* AHS */ -- sizeof(__u32)]; /* HeaderDigest */ -- char pad[ISCSI_PAD_LEN]; -- int pad_count; /* padded bytes */ -- struct iscsi_buf headbuf; /* header buf (xmit) */ -- struct iscsi_buf sendbuf; /* in progress buffer*/ -- int xmstate; /* xmit xtate machine */ -+ struct iscsi_hdr_buff { -+ struct iscsi_cmd cmd_hdr; -+ char hdrextbuf[ISCSI_MAX_AHS_SIZE + -+ ISCSI_DIGEST_SIZE]; -+ } hdr; -+ - int sent; -- struct scatterlist *sg; /* per-cmd SG list */ -- struct scatterlist *bad_sg; /* assert statement */ -- int sg_count; /* SG's to process */ -- uint32_t exp_datasn; /* expected target's R2TSN/DataSN */ -+ uint32_t exp_datasn; /* expected target's R2TSN/DataSN */ - int data_offset; -- struct iscsi_r2t_info *r2t; /* in progress R2T */ -- struct iscsi_queue r2tpool; -+ struct iscsi_r2t_info *r2t; /* in progress R2T */ -+ struct iscsi_pool r2tpool; - struct kfifo *r2tqueue; -- struct iscsi_r2t_info **r2ts; -- int digest_count; -- uint32_t immdigest; /* for imm data */ -- struct iscsi_buf immbuf; /* for imm data digest */ -- struct iscsi_data_task unsol_dtask; /* unsol data task */ -+ struct iscsi_data_task unsol_dtask; /* Data-Out header buf */ - }; - - #endif /* ISCSI_H */ -diff --git a/kernel/libiscsi.c b/kernel/libiscsi.c -index f2ef4c7..30ea61a 100644 ---- a/kernel/libiscsi.c -+++ b/kernel/libiscsi.c -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -86,7 +87,7 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) - * xmit thread - */ - if (!list_empty(&session->leadconn->xmitqueue) || -- __kfifo_len(session->leadconn->mgmtqueue)) -+ !list_empty(&session->leadconn->mgmtqueue)) - scsi_queue_work(session->host, - &session->leadconn->xmitwork); - } -@@ -122,6 +123,20 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, - } - EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); - -+static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len) -+{ -+ unsigned exp_len = ctask->hdr_len + len; -+ -+ if (exp_len > ctask->hdr_max) { -+ WARN_ON(1); -+ return -EINVAL; -+ } -+ -+ WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */ -+ ctask->hdr_len = exp_len; -+ return 0; -+} -+ - /** - * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu - * @ctask: iscsi cmd task -@@ -129,27 +144,32 @@ EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); - * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set - * fields like dlength or final based on how much data it sends - */ --static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) -+static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) - { - struct iscsi_conn *conn = ctask->conn; - struct iscsi_session *session = conn->session; - struct iscsi_cmd *hdr = ctask->hdr; - struct scsi_cmnd *sc = ctask->sc; -+ unsigned hdrlength; -+ int rc; - -- hdr->opcode = ISCSI_OP_SCSI_CMD; -- hdr->flags = ISCSI_ATTR_SIMPLE; -- int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); -- hdr->itt = build_itt(ctask->itt, conn->id, session->age); -- hdr->data_length = cpu_to_be32(sc->request_bufflen); -- hdr->cmdsn = cpu_to_be32(session->cmdsn); -- session->cmdsn++; -- hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); -- memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); -+ ctask->hdr_len = 0; -+ rc = iscsi_add_hdr(ctask, sizeof(*hdr)); -+ if (rc) -+ return rc; -+ hdr->opcode = ISCSI_OP_SCSI_CMD; -+ hdr->flags = ISCSI_ATTR_SIMPLE; -+ int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); -+ hdr->itt = build_itt(ctask->itt, session->age); -+ hdr->data_length = cpu_to_be32(scsi_bufflen(sc)); -+ hdr->cmdsn = cpu_to_be32(session->cmdsn); -+ session->cmdsn++; -+ hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); -+ memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); - if (sc->cmd_len < MAX_COMMAND_SIZE) - memset(&hdr->cdb[sc->cmd_len], 0, - MAX_COMMAND_SIZE - sc->cmd_len); - -- ctask->data_count = 0; - ctask->imm_count = 0; - if (sc->sc_data_direction == DMA_TO_DEVICE) { - hdr->flags |= ISCSI_FLAG_CMD_WRITE; -@@ -172,25 +192,25 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) - ctask->unsol_datasn = 0; - - if (session->imm_data_en) { -- if (sc->request_bufflen >= session->first_burst) -+ if (scsi_bufflen(sc) >= session->first_burst) - ctask->imm_count = min(session->first_burst, - conn->max_xmit_dlength); - else -- ctask->imm_count = min(sc->request_bufflen, -+ ctask->imm_count = min(scsi_bufflen(sc), - conn->max_xmit_dlength); -- hton24(ctask->hdr->dlength, ctask->imm_count); -+ hton24(hdr->dlength, ctask->imm_count); - } else -- zero_data(ctask->hdr->dlength); -+ zero_data(hdr->dlength); - - if (!session->initial_r2t_en) { - ctask->unsol_count = min((session->first_burst), -- (sc->request_bufflen)) - ctask->imm_count; -+ (scsi_bufflen(sc))) - ctask->imm_count; - ctask->unsol_offset = ctask->imm_count; - } - - if (!ctask->unsol_count) - /* No unsolicit Data-Out's */ -- ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL; -+ hdr->flags |= ISCSI_FLAG_CMD_FINAL; - } else { - hdr->flags |= ISCSI_FLAG_CMD_FINAL; - zero_data(hdr->dlength); -@@ -199,13 +219,25 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) - hdr->flags |= ISCSI_FLAG_CMD_READ; - } - -- conn->scsicmd_pdus_cnt++; -+ /* calculate size of additional header segments (AHSs) */ -+ hdrlength = ctask->hdr_len - sizeof(*hdr); - -- debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " -+ WARN_ON(hdrlength & (ISCSI_PAD_LEN-1)); -+ hdrlength /= ISCSI_PAD_LEN; -+ -+ WARN_ON(hdrlength >= 256); -+ hdr->hlength = hdrlength & 0xFF; -+ -+ if (conn->session->tt->init_cmd_task(conn->ctask)) -+ return EIO; -+ -+ conn->scsicmd_pdus_cnt++; -+ debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d " - "cmdsn %d win %d]\n", -- sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", -- conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen, -- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); -+ sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", -+ conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc), -+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); -+ return 0; - } - - /** -@@ -218,13 +250,16 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) - */ - static void iscsi_complete_command(struct iscsi_cmd_task *ctask) - { -- struct iscsi_session *session = ctask->conn->session; -+ struct iscsi_conn *conn = ctask->conn; -+ struct iscsi_session *session = conn->session; - struct scsi_cmnd *sc = ctask->sc; - - ctask->state = ISCSI_TASK_COMPLETED; - ctask->sc = NULL; - /* SCSI eh reuses commands to verify us */ - sc->SCp.ptr = NULL; -+ if (conn->ctask == ctask) -+ conn->ctask = NULL; - list_del_init(&ctask->running); - __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); - sc->scsi_done(sc); -@@ -241,6 +276,112 @@ static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask) - iscsi_complete_command(ctask); - } - -+/* -+ * session lock must be held -+ */ -+static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, -+ int err) -+{ -+ struct scsi_cmnd *sc; -+ -+ sc = ctask->sc; -+ if (!sc) -+ return; -+ -+ if (ctask->state == ISCSI_TASK_PENDING) -+ /* -+ * cmd never made it to the xmit thread, so we should not count -+ * the cmd in the sequencing -+ */ -+ conn->session->queued_cmdsn--; -+ else -+ conn->session->tt->cleanup_cmd_task(conn, ctask); -+ -+ sc->result = err; -+ scsi_set_resid(sc, scsi_bufflen(sc)); -+ if (conn->ctask == ctask) -+ conn->ctask = NULL; -+ /* release ref from queuecommand */ -+ __iscsi_put_ctask(ctask); -+} -+ -+/** -+ * iscsi_free_mgmt_task - return mgmt task back to pool -+ * @conn: iscsi connection -+ * @mtask: mtask -+ * -+ * Must be called with session lock. -+ */ -+void iscsi_free_mgmt_task(struct iscsi_conn *conn, -+ struct iscsi_mgmt_task *mtask) -+{ -+ list_del_init(&mtask->running); -+ if (conn->login_mtask == mtask) -+ return; -+ -+ if (conn->ping_mtask == mtask) -+ conn->ping_mtask = NULL; -+ __kfifo_put(conn->session->mgmtpool.queue, -+ (void*)&mtask, sizeof(void*)); -+} -+EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task); -+ -+static struct iscsi_mgmt_task * -+__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, -+ char *data, uint32_t data_size) -+{ -+ struct iscsi_session *session = conn->session; -+ struct iscsi_mgmt_task *mtask; -+ -+ if (session->state == ISCSI_STATE_TERMINATE) -+ return NULL; -+ -+ if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || -+ hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) -+ /* -+ * Login and Text are sent serially, in -+ * request-followed-by-response sequence. -+ * Same mtask can be used. Same ITT must be used. -+ * Note that login_mtask is preallocated at conn_create(). -+ */ -+ mtask = conn->login_mtask; -+ else { -+ BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); -+ BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); -+ -+ if (!__kfifo_get(session->mgmtpool.queue, -+ (void*)&mtask, sizeof(void*))) -+ return NULL; -+ } -+ -+ if (data_size) { -+ memcpy(mtask->data, data, data_size); -+ mtask->data_count = data_size; -+ } else -+ mtask->data_count = 0; -+ -+ memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); -+ INIT_LIST_HEAD(&mtask->running); -+ list_add_tail(&mtask->running, &conn->mgmtqueue); -+ return mtask; -+} -+ -+int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, -+ char *data, uint32_t data_size) -+{ -+ struct iscsi_conn *conn = cls_conn->dd_data; -+ struct iscsi_session *session = conn->session; -+ int err = 0; -+ -+ spin_lock_bh(&session->lock); -+ if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) -+ err = -EPERM; -+ spin_unlock_bh(&session->lock); -+ scsi_queue_work(session->host, &conn->xmitwork); -+ return err; -+} -+EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); -+ - /** - * iscsi_cmd_rsp - SCSI Command Response processing - * @conn: iscsi connection -@@ -275,8 +416,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - - if (datalen < 2) { - invalid_datalen: -- printk(KERN_ERR "iscsi: Got CHECK_CONDITION but " -- "invalid data buffer size of %d\n", datalen); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "Got CHECK_CONDITION but invalid data " -+ "buffer size of %d\n", datalen); - sc->result = DID_BAD_TARGET << 16; - goto out; - } -@@ -291,20 +433,19 @@ invalid_datalen: - min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); - } - -- if (sc->sc_data_direction == DMA_TO_DEVICE) -- goto out; -- -- if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) { -+ if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | -+ ISCSI_FLAG_CMD_OVERFLOW)) { - int res_count = be32_to_cpu(rhdr->residual_count); - -- if (res_count > 0 && res_count <= sc->request_bufflen) -- sc->resid = res_count; -+ if (res_count > 0 && -+ (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || -+ res_count <= scsi_bufflen(sc))) -+ scsi_set_resid(sc, res_count); - else - sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; -- } else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW) -+ } else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | -+ ISCSI_FLAG_CMD_BIDI_OVERFLOW)) - sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; -- else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW) -- sc->resid = be32_to_cpu(rhdr->residual_count); - - out: - debug_scsi("done [sc %lx res %d itt 0x%x]\n", -@@ -321,18 +462,51 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) - conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - conn->tmfrsp_pdus_cnt++; - -- if (conn->tmabort_state != TMABORT_INITIAL) -+ if (conn->tmf_state != TMF_QUEUED) - return; - - if (tmf->response == ISCSI_TMF_RSP_COMPLETE) -- conn->tmabort_state = TMABORT_SUCCESS; -+ conn->tmf_state = TMF_SUCCESS; - else if (tmf->response == ISCSI_TMF_RSP_NO_TASK) -- conn->tmabort_state = TMABORT_NOT_FOUND; -+ conn->tmf_state = TMF_NOT_FOUND; - else -- conn->tmabort_state = TMABORT_FAILED; -+ conn->tmf_state = TMF_FAILED; - wake_up(&conn->ehwait); - } - -+static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) -+{ -+ struct iscsi_nopout hdr; -+ struct iscsi_mgmt_task *mtask; -+ -+ if (!rhdr && conn->ping_mtask) -+ return; -+ -+ memset(&hdr, 0, sizeof(struct iscsi_nopout)); -+ hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; -+ hdr.flags = ISCSI_FLAG_CMD_FINAL; -+ -+ if (rhdr) { -+ memcpy(hdr.lun, rhdr->lun, 8); -+ hdr.ttt = rhdr->ttt; -+ hdr.itt = RESERVED_ITT; -+ } else -+ hdr.ttt = RESERVED_ITT; -+ -+ mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0); -+ if (!mtask) { -+ iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n"); -+ return; -+ } -+ -+ /* only track our nops */ -+ if (!rhdr) { -+ conn->ping_mtask = mtask; -+ conn->last_ping = jiffies; -+ } -+ scsi_queue_work(conn->session->host, &conn->xmitwork); -+} -+ - static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - char *data, int datalen) - { -@@ -349,9 +523,10 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { - memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); - itt = get_itt(rejected_pdu.itt); -- printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected " -- "due to DataDigest error.\n", itt, -- rejected_pdu.opcode); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "itt 0x%x had pdu (op 0x%x) rejected " -+ "due to DataDigest error.\n", itt, -+ rejected_pdu.opcode); - } - } - return 0; -@@ -368,8 +543,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - * queuecommand or send generic. session lock must be held and verify - * itt must have been called. - */ --int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, -- char *data, int datalen) -+static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, -+ char *data, int datalen) - { - struct iscsi_session *session = conn->session; - int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; -@@ -377,6 +552,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - struct iscsi_mgmt_task *mtask; - uint32_t itt; - -+ conn->last_recv = jiffies; - if (hdr->itt != RESERVED_ITT) - itt = get_itt(hdr->itt); - else -@@ -432,10 +608,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - */ - if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) - rc = ISCSI_ERR_CONN_FAILED; -- list_del(&mtask->running); -- if (conn->login_mtask != mtask) -- __kfifo_put(session->mgmtpool.queue, -- (void*)&mtask, sizeof(void*)); -+ iscsi_free_mgmt_task(conn, mtask); - break; - case ISCSI_OP_SCSI_TMFUNC_RSP: - if (datalen) { -@@ -444,20 +617,26 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - } - - iscsi_tmf_rsp(conn, hdr); -+ iscsi_free_mgmt_task(conn, mtask); - break; - case ISCSI_OP_NOOP_IN: -- if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) { -+ if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || -+ datalen) { - rc = ISCSI_ERR_PROTO; - break; - } - conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - -- if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) -- rc = ISCSI_ERR_CONN_FAILED; -- list_del(&mtask->running); -- if (conn->login_mtask != mtask) -- __kfifo_put(session->mgmtpool.queue, -- (void*)&mtask, sizeof(void*)); -+ if (conn->ping_mtask != mtask) { -+ /* -+ * If this is not in response to one of our -+ * nops then it must be from userspace. -+ */ -+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, -+ datalen)) -+ rc = ISCSI_ERR_CONN_FAILED; -+ } -+ iscsi_free_mgmt_task(conn, mtask); - break; - default: - rc = ISCSI_ERR_BAD_OPCODE; -@@ -476,8 +655,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) - break; - -- if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0)) -- rc = ISCSI_ERR_CONN_FAILED; -+ iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr); - break; - case ISCSI_OP_REJECT: - rc = iscsi_handle_reject(conn, hdr, data, datalen); -@@ -496,7 +674,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - - return rc; - } --EXPORT_SYMBOL_GPL(__iscsi_complete_pdu); - - int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - char *data, int datalen) -@@ -521,18 +698,13 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - if (hdr->itt != RESERVED_ITT) { - if (((__force u32)hdr->itt & ISCSI_AGE_MASK) != - (session->age << ISCSI_AGE_SHIFT)) { -- printk(KERN_ERR "iscsi: received itt %x expected " -- "session age (%x)\n", (__force u32)hdr->itt, -- session->age & ISCSI_AGE_MASK); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "received itt %x expected session " -+ "age (%x)\n", (__force u32)hdr->itt, -+ session->age & ISCSI_AGE_MASK); - return ISCSI_ERR_BAD_ITT; - } - -- if (((__force u32)hdr->itt & ISCSI_CID_MASK) != -- (conn->id << ISCSI_CID_SHIFT)) { -- printk(KERN_ERR "iscsi: received itt %x, expected " -- "CID (%x)\n", (__force u32)hdr->itt, conn->id); -- return ISCSI_ERR_BAD_ITT; -- } - itt = get_itt(hdr->itt); - } else - itt = ~0U; -@@ -541,16 +713,17 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr, - ctask = session->cmds[itt]; - - if (!ctask->sc) { -- printk(KERN_INFO "iscsi: dropping ctask with " -- "itt 0x%x\n", ctask->itt); -+ iscsi_conn_printk(KERN_INFO, conn, "dropping ctask " -+ "with itt 0x%x\n", ctask->itt); - /* force drop */ - return ISCSI_ERR_NO_SCSI_CMD; - } - - if (ctask->sc->SCp.phase != session->age) { -- printk(KERN_ERR "iscsi: ctask's session age %d, " -- "expected %d\n", ctask->sc->SCp.phase, -- session->age); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "iscsi: ctask's session age %d, " -+ "expected %d\n", ctask->sc->SCp.phase, -+ session->age); - return ISCSI_ERR_SESSION_FAILED; - } - } -@@ -595,30 +768,36 @@ static void iscsi_prep_mtask(struct iscsi_conn *conn, - */ - nop->cmdsn = cpu_to_be32(session->cmdsn); - if (hdr->itt != RESERVED_ITT) { -- hdr->itt = build_itt(mtask->itt, conn->id, session->age); -+ hdr->itt = build_itt(mtask->itt, session->age); -+ /* -+ * TODO: We always use immediate, so we never hit this. -+ * If we start to send tmfs or nops as non-immediate then -+ * we should start checking the cmdsn numbers for mgmt tasks. -+ */ - if (conn->c_stage == ISCSI_CONN_STARTED && -- !(hdr->opcode & ISCSI_OP_IMMEDIATE)) -+ !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { -+ session->queued_cmdsn++; - session->cmdsn++; -+ } - } - - if (session->tt->init_mgmt_task) - session->tt->init_mgmt_task(conn, mtask); - - debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n", -- hdr->opcode, hdr->itt, mtask->data_count); -+ hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt, -+ mtask->data_count); - } - - static int iscsi_xmit_mtask(struct iscsi_conn *conn) - { - struct iscsi_hdr *hdr = conn->mtask->hdr; -- int rc, was_logout = 0; -+ int rc; - -+ if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) -+ conn->session->state = ISCSI_STATE_LOGGING_OUT; - spin_unlock_bh(&conn->session->lock); -- if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) { -- conn->session->state = ISCSI_STATE_IN_RECOVERY; -- iscsi_block_session(session_to_cls(conn->session)); -- was_logout = 1; -- } -+ - rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask); - spin_lock_bh(&conn->session->lock); - if (rc) -@@ -626,11 +805,6 @@ static int iscsi_xmit_mtask(struct iscsi_conn *conn) - - /* done with this in-progress mtask */ - conn->mtask = NULL; -- -- if (was_logout) { -- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); -- return -ENODATA; -- } - return 0; - } - -@@ -641,9 +815,11 @@ static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn) - /* - * Check for iSCSI window and take care of CmdSN wrap-around - */ -- if (!iscsi_sna_lte(session->cmdsn, session->max_cmdsn)) { -- debug_scsi("iSCSI CmdSN closed. MaxCmdSN %u CmdSN %u\n", -- session->max_cmdsn, session->cmdsn); -+ if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) { -+ debug_scsi("iSCSI CmdSN closed. ExpCmdSn %u MaxCmdSN %u " -+ "CmdSN %u/%u\n", session->exp_cmdsn, -+ session->max_cmdsn, session->cmdsn, -+ session->queued_cmdsn); - return -ENOSPC; - } - return 0; -@@ -652,21 +828,13 @@ static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn) - static int iscsi_xmit_ctask(struct iscsi_conn *conn) - { - struct iscsi_cmd_task *ctask = conn->ctask; -- int rc = 0; -- -- /* -- * serialize with TMF AbortTask -- */ -- if (ctask->state == ISCSI_TASK_ABORTING) -- goto done; -+ int rc; - - __iscsi_get_ctask(ctask); - spin_unlock_bh(&conn->session->lock); - rc = conn->session->tt->xmit_cmd_task(conn, ctask); - spin_lock_bh(&conn->session->lock); - __iscsi_put_ctask(ctask); -- --done: - if (!rc) - /* done with this ctask */ - conn->ctask = NULL; -@@ -674,6 +842,22 @@ done: - } - - /** -+ * iscsi_requeue_ctask - requeue ctask to run from session workqueue -+ * @ctask: ctask to requeue -+ * -+ * LLDs that need to run a ctask from the session workqueue should call -+ * this. The session lock must be held. -+ */ -+void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask) -+{ -+ struct iscsi_conn *conn = ctask->conn; -+ -+ list_move_tail(&ctask->running, &conn->requeue); -+ scsi_queue_work(conn->session->host, &conn->xmitwork); -+} -+EXPORT_SYMBOL_GPL(iscsi_requeue_ctask); -+ -+/** - * iscsi_data_xmit - xmit any command into the scheduled connection - * @conn: iscsi connection - * -@@ -711,32 +895,38 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) - * overflow us with nop-ins - */ - check_mgmt: -- while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask, -- sizeof(void*))) { -+ while (!list_empty(&conn->mgmtqueue)) { -+ conn->mtask = list_entry(conn->mgmtqueue.next, -+ struct iscsi_mgmt_task, running); -+ if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { -+ iscsi_free_mgmt_task(conn, conn->mtask); -+ conn->mtask = NULL; -+ continue; -+ } -+ - iscsi_prep_mtask(conn, conn->mtask); -- list_add_tail(&conn->mtask->running, &conn->mgmt_run_list); -+ list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list); - rc = iscsi_xmit_mtask(conn); - if (rc) - goto again; - } - -- /* process command queue */ -+ /* process pending command queue */ - while (!list_empty(&conn->xmitqueue)) { -- rc = iscsi_check_cmdsn_window_closed(conn); -- if (rc) { -- spin_unlock_bh(&conn->session->lock); -- return rc; -- } -- /* -- * iscsi tcp may readd the task to the xmitqueue to send -- * write data -- */ -+ if (conn->tmf_state == TMF_QUEUED) -+ break; -+ - conn->ctask = list_entry(conn->xmitqueue.next, - struct iscsi_cmd_task, running); -- if (conn->ctask->state == ISCSI_TASK_PENDING) { -- iscsi_prep_scsi_cmd_pdu(conn->ctask); -- conn->session->tt->init_cmd_task(conn->ctask); -+ if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { -+ fail_command(conn, conn->ctask, DID_IMM_RETRY << 16); -+ continue; - } -+ if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) { -+ fail_command(conn, conn->ctask, DID_ABORT << 16); -+ continue; -+ } -+ - conn->ctask->state = ISCSI_TASK_RUNNING; - list_move_tail(conn->xmitqueue.next, &conn->run_list); - rc = iscsi_xmit_ctask(conn); -@@ -747,7 +937,28 @@ check_mgmt: - * we need to check the mgmt queue for nops that need to - * be sent to aviod starvation - */ -- if (__kfifo_len(conn->mgmtqueue)) -+ if (!list_empty(&conn->mgmtqueue)) -+ goto check_mgmt; -+ } -+ -+ while (!list_empty(&conn->requeue)) { -+ if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL) -+ break; -+ -+ /* -+ * we always do fastlogout - conn stop code will clean up. -+ */ -+ if (conn->session->state == ISCSI_STATE_LOGGING_OUT) -+ break; -+ -+ conn->ctask = list_entry(conn->requeue.next, -+ struct iscsi_cmd_task, running); -+ conn->ctask->state = ISCSI_TASK_RUNNING; -+ list_move_tail(conn->requeue.next, &conn->run_list); -+ rc = iscsi_xmit_ctask(conn); -+ if (rc) -+ goto again; -+ if (!list_empty(&conn->mgmtqueue)) - goto check_mgmt; - } - spin_unlock_bh(&conn->session->lock); -@@ -782,6 +993,8 @@ enum { - FAILURE_SESSION_TERMINATE, - FAILURE_SESSION_IN_RECOVERY, - FAILURE_SESSION_RECOVERY_TIMEOUT, -+ FAILURE_SESSION_LOGGING_OUT, -+ FAILURE_SESSION_NOT_READY, - }; - - int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) -@@ -797,10 +1010,17 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) - sc->SCp.ptr = NULL; - - host = sc->device->host; -- session = iscsi_hostdata(host->hostdata); -+ spin_unlock(host->host_lock); - -+ session = iscsi_hostdata(host->hostdata); - spin_lock(&session->lock); - -+ reason = iscsi_session_chkready(session_to_cls(session)); -+ if (reason) { -+ sc->result = reason; -+ goto fault; -+ } -+ - /* - * ISCSI_STATE_FAILED is a temp. state. The recovery - * code will decide what is best to do with command queued -@@ -814,32 +1034,37 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) - * be entering our queuecommand while a block is starting - * up because the block code is not locked) - */ -- if (session->state == ISCSI_STATE_IN_RECOVERY) { -+ switch (session->state) { -+ case ISCSI_STATE_IN_RECOVERY: - reason = FAILURE_SESSION_IN_RECOVERY; -- goto reject; -- } -- -- if (session->state == ISCSI_STATE_RECOVERY_FAILED) -+ sc->result = DID_IMM_RETRY << 16; -+ break; -+ case ISCSI_STATE_LOGGING_OUT: -+ reason = FAILURE_SESSION_LOGGING_OUT; -+ sc->result = DID_IMM_RETRY << 16; -+ break; -+ case ISCSI_STATE_RECOVERY_FAILED: - reason = FAILURE_SESSION_RECOVERY_TIMEOUT; -- else if (session->state == ISCSI_STATE_TERMINATE) -+ sc->result = DID_NO_CONNECT << 16; -+ break; -+ case ISCSI_STATE_TERMINATE: - reason = FAILURE_SESSION_TERMINATE; -- else -+ sc->result = DID_NO_CONNECT << 16; -+ break; -+ default: - reason = FAILURE_SESSION_FREED; -+ sc->result = DID_NO_CONNECT << 16; -+ } - goto fault; - } - - conn = session->leadconn; - if (!conn) { - reason = FAILURE_SESSION_FREED; -+ sc->result = DID_NO_CONNECT << 16; - goto fault; - } - -- /* -- * We check this here and in data xmit, because if we get to the point -- * that this check is hitting the window then we have enough IO in -- * flight and enough IO waiting to be transmitted it is better -- * to let the scsi/block layer queue up. -- */ - if (iscsi_check_cmdsn_window_closed(conn)) { - reason = FAILURE_WINDOW_CLOSED; - goto reject; -@@ -850,12 +1075,13 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) - reason = FAILURE_OOM; - goto reject; - } -+ session->queued_cmdsn++; -+ - sc->SCp.phase = session->age; - sc->SCp.ptr = (char *)ctask; - - atomic_set(&ctask->refcount, 1); - ctask->state = ISCSI_TASK_PENDING; -- ctask->mtask = NULL; - ctask->conn = conn; - ctask->sc = sc; - INIT_LIST_HEAD(&ctask->running); -@@ -864,20 +1090,21 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) - spin_unlock(&session->lock); - - scsi_queue_work(host, &conn->xmitwork); -+ spin_lock(host->host_lock); - return 0; - - reject: - spin_unlock(&session->lock); - debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); -+ spin_lock(host->host_lock); - return SCSI_MLQUEUE_HOST_BUSY; - - fault: - spin_unlock(&session->lock); -- printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n", -- sc->cmnd[0], reason); -- sc->result = (DID_NO_CONNECT << 16); -- sc->resid = sc->request_bufflen; -+ debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); -+ scsi_set_resid(sc, scsi_bufflen(sc)); - sc->scsi_done(sc); -+ spin_lock(host->host_lock); - return 0; - } - EXPORT_SYMBOL_GPL(iscsi_queuecommand); -@@ -891,72 +1118,15 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int depth) - } - EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); - --static struct iscsi_mgmt_task * --__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, -- char *data, uint32_t data_size) --{ -- struct iscsi_session *session = conn->session; -- struct iscsi_mgmt_task *mtask; -- -- if (session->state == ISCSI_STATE_TERMINATE) -- return NULL; -- -- if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || -- hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) -- /* -- * Login and Text are sent serially, in -- * request-followed-by-response sequence. -- * Same mtask can be used. Same ITT must be used. -- * Note that login_mtask is preallocated at conn_create(). -- */ -- mtask = conn->login_mtask; -- else { -- BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); -- BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); -- -- if (!__kfifo_get(session->mgmtpool.queue, -- (void*)&mtask, sizeof(void*))) -- return NULL; -- } -- -- if (data_size) { -- memcpy(mtask->data, data, data_size); -- mtask->data_count = data_size; -- } else -- mtask->data_count = 0; -- -- INIT_LIST_HEAD(&mtask->running); -- memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); -- __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*)); -- return mtask; --} -- --int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, -- char *data, uint32_t data_size) --{ -- struct iscsi_conn *conn = cls_conn->dd_data; -- struct iscsi_session *session = conn->session; -- int err = 0; -- -- spin_lock_bh(&session->lock); -- if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) -- err = -EPERM; -- spin_unlock_bh(&session->lock); -- scsi_queue_work(session->host, &conn->xmitwork); -- return err; --} --EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); -- - void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) - { - struct iscsi_session *session = class_to_transport_session(cls_session); -- struct iscsi_conn *conn = session->leadconn; - - spin_lock_bh(&session->lock); - if (session->state != ISCSI_STATE_LOGGED_IN) { - session->state = ISCSI_STATE_RECOVERY_FAILED; -- if (conn) -- wake_up(&conn->ehwait); -+ if (session->leadconn) -+ wake_up(&session->leadconn->ehwait); - } - spin_unlock_bh(&session->lock); - } -@@ -967,30 +1137,25 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc) - struct Scsi_Host *host = sc->device->host; - struct iscsi_session *session = iscsi_hostdata(host->hostdata); - struct iscsi_conn *conn = session->leadconn; -- int fail_session = 0; - -+ mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_TERMINATE) { - failed: - debug_scsi("failing host reset: session terminated " - "[CID %d age %d]\n", conn->id, session->age); - spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); - return FAILED; - } - -- if (sc->SCp.phase == session->age) { -- debug_scsi("failing connection CID %d due to SCSI host reset\n", -- conn->id); -- fail_session = 1; -- } - spin_unlock_bh(&session->lock); -- -+ mutex_unlock(&session->eh_mutex); - /* - * we drop the lock here but the leadconn cannot be destoyed while - * we are in the scsi eh - */ -- if (fail_session) -- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - - debug_scsi("iscsi_eh_host_reset wait for relogin\n"); - wait_event_interruptible(conn->ehwait, -@@ -1000,190 +1165,284 @@ failed: - if (signal_pending(current)) - flush_signals(current); - -+ mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_LOGGED_IN) -- printk(KERN_INFO "iscsi: host reset succeeded\n"); -+ iscsi_session_printk(KERN_INFO, session, -+ "host reset succeeded\n"); - else - goto failed; - spin_unlock_bh(&session->lock); -- -+ mutex_unlock(&session->eh_mutex); - return SUCCESS; - } - EXPORT_SYMBOL_GPL(iscsi_eh_host_reset); - --static void iscsi_tmabort_timedout(unsigned long data) -+static void iscsi_tmf_timedout(unsigned long data) - { -- struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)data; -- struct iscsi_conn *conn = ctask->conn; -+ struct iscsi_conn *conn = (struct iscsi_conn *)data; - struct iscsi_session *session = conn->session; - - spin_lock(&session->lock); -- if (conn->tmabort_state == TMABORT_INITIAL) { -- conn->tmabort_state = TMABORT_TIMEDOUT; -- debug_scsi("tmabort timedout [sc %p itt 0x%x]\n", -- ctask->sc, ctask->itt); -+ if (conn->tmf_state == TMF_QUEUED) { -+ conn->tmf_state = TMF_TIMEDOUT; -+ debug_scsi("tmf timedout\n"); - /* unblock eh_abort() */ - wake_up(&conn->ehwait); - } - spin_unlock(&session->lock); - } - --static int iscsi_exec_abort_task(struct scsi_cmnd *sc, -- struct iscsi_cmd_task *ctask) -+static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, -+ struct iscsi_tm *hdr, int age, -+ int timeout) - { -- struct iscsi_conn *conn = ctask->conn; - struct iscsi_session *session = conn->session; -- struct iscsi_tm *hdr = &conn->tmhdr; -- -- /* -- * ctask timed out but session is OK requests must be serialized. -- */ -- memset(hdr, 0, sizeof(struct iscsi_tm)); -- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; -- hdr->flags = ISCSI_TM_FUNC_ABORT_TASK; -- hdr->flags |= ISCSI_FLAG_CMD_FINAL; -- memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); -- hdr->rtt = ctask->hdr->itt; -- hdr->refcmdsn = ctask->hdr->cmdsn; -+ struct iscsi_mgmt_task *mtask; - -- ctask->mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, -- NULL, 0); -- if (!ctask->mtask) { -+ mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, -+ NULL, 0); -+ if (!mtask) { -+ spin_unlock_bh(&session->lock); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -- debug_scsi("abort sent failure [itt 0x%x]\n", ctask->itt); -+ spin_lock_bh(&session->lock); -+ debug_scsi("tmf exec failure\n"); - return -EPERM; - } -- ctask->state = ISCSI_TASK_ABORTING; -- -- debug_scsi("abort sent [itt 0x%x]\n", ctask->itt); -+ conn->tmfcmd_pdus_cnt++; -+ conn->tmf_timer.expires = timeout * HZ + jiffies; -+ conn->tmf_timer.function = iscsi_tmf_timedout; -+ conn->tmf_timer.data = (unsigned long)conn; -+ add_timer(&conn->tmf_timer); -+ debug_scsi("tmf set timeout\n"); - -- if (conn->tmabort_state == TMABORT_INITIAL) { -- conn->tmfcmd_pdus_cnt++; -- conn->tmabort_timer.expires = 20*HZ + jiffies; -- conn->tmabort_timer.function = iscsi_tmabort_timedout; -- conn->tmabort_timer.data = (unsigned long)ctask; -- add_timer(&conn->tmabort_timer); -- debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt); -- } - spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); - scsi_queue_work(session->host, &conn->xmitwork); - - /* - * block eh thread until: - * -- * 1) abort response -- * 2) abort timeout -+ * 1) tmf response -+ * 2) tmf timeout - * 3) session is terminated or restarted or userspace has - * given up on recovery - */ -- wait_event_interruptible(conn->ehwait, -- sc->SCp.phase != session->age || -+ wait_event_interruptible(conn->ehwait, age != session->age || - session->state != ISCSI_STATE_LOGGED_IN || -- conn->tmabort_state != TMABORT_INITIAL); -+ conn->tmf_state != TMF_QUEUED); - if (signal_pending(current)) - flush_signals(current); -- del_timer_sync(&conn->tmabort_timer); -+ del_timer_sync(&conn->tmf_timer); -+ -+ mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); -+ /* if the session drops it will clean up the mtask */ -+ if (age != session->age || -+ session->state != ISCSI_STATE_LOGGED_IN) -+ return -ENOTCONN; - return 0; - } - - /* -- * session lock must be held -+ * Fail commands. session lock held and recv side suspended and xmit -+ * thread flushed - */ --static struct iscsi_mgmt_task * --iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt) -+static void fail_all_commands(struct iscsi_conn *conn, unsigned lun, -+ int error) - { -- int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*); -- struct iscsi_mgmt_task *task; -+ struct iscsi_cmd_task *ctask, *tmp; - -- debug_scsi("searching %d tasks\n", nr_tasks); -+ if (conn->ctask && (conn->ctask->sc->device->lun == lun || lun == -1)) -+ conn->ctask = NULL; - -- for (i = 0; i < nr_tasks; i++) { -- __kfifo_get(fifo, (void*)&task, sizeof(void*)); -- debug_scsi("check task %u\n", task->itt); -+ /* flush pending */ -+ list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) { -+ if (lun == ctask->sc->device->lun || lun == -1) { -+ debug_scsi("failing pending sc %p itt 0x%x\n", -+ ctask->sc, ctask->itt); -+ fail_command(conn, ctask, error << 16); -+ } -+ } - -- if (task->itt == itt) { -- debug_scsi("matched task\n"); -- return task; -+ list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) { -+ if (lun == ctask->sc->device->lun || lun == -1) { -+ debug_scsi("failing requeued sc %p itt 0x%x\n", -+ ctask->sc, ctask->itt); -+ fail_command(conn, ctask, error << 16); - } -+ } - -- __kfifo_put(fifo, (void*)&task, sizeof(void*)); -+ /* fail all other running */ -+ list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) { -+ if (lun == ctask->sc->device->lun || lun == -1) { -+ debug_scsi("failing in progress sc %p itt 0x%x\n", -+ ctask->sc, ctask->itt); -+ fail_command(conn, ctask, DID_BUS_BUSY << 16); -+ } - } -- return NULL; - } - --static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask) -+static void iscsi_suspend_tx(struct iscsi_conn *conn) - { -- struct iscsi_conn *conn = ctask->conn; -- struct iscsi_session *session = conn->session; -+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); -+ scsi_flush_work(conn->session->host); -+} - -- if (!ctask->mtask) -- return -EINVAL; -+static void iscsi_start_tx(struct iscsi_conn *conn) -+{ -+ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); -+ scsi_queue_work(conn->session->host, &conn->xmitwork); -+} - -- if (!iscsi_remove_mgmt_task(conn->mgmtqueue, ctask->mtask->itt)) -- list_del(&ctask->mtask->running); -- __kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask, -- sizeof(void*)); -- ctask->mtask = NULL; -- return 0; -+static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) -+{ -+ struct iscsi_cls_session *cls_session; -+ struct iscsi_session *session; -+ struct iscsi_conn *conn; -+ enum scsi_eh_timer_return rc = EH_NOT_HANDLED; -+ -+ cls_session = starget_to_session(scsi_target(scmd->device)); -+ session = class_to_transport_session(cls_session); -+ -+ debug_scsi("scsi cmd %p timedout\n", scmd); -+ -+ spin_lock(&session->lock); -+ if (session->state != ISCSI_STATE_LOGGED_IN) { -+ /* -+ * We are probably in the middle of iscsi recovery so let -+ * that complete and handle the error. -+ */ -+ rc = EH_RESET_TIMER; -+ goto done; -+ } -+ -+ conn = session->leadconn; -+ if (!conn) { -+ /* In the middle of shuting down */ -+ rc = EH_RESET_TIMER; -+ goto done; -+ } -+ -+ if (!conn->recv_timeout && !conn->ping_timeout) -+ goto done; -+ /* -+ * if the ping timedout then we are in the middle of cleaning up -+ * and can let the iscsi eh handle it -+ */ -+ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) + -+ (conn->ping_timeout * HZ), jiffies)) -+ rc = EH_RESET_TIMER; -+ /* -+ * if we are about to check the transport then give the command -+ * more time -+ */ -+ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ), -+ jiffies)) -+ rc = EH_RESET_TIMER; -+ /* if in the middle of checking the transport then give us more time */ -+ if (conn->ping_mtask) -+ rc = EH_RESET_TIMER; -+done: -+ spin_unlock(&session->lock); -+ debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh"); -+ return rc; - } - --/* -- * session lock must be held -- */ --static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, -- int err) -+static void iscsi_check_transport_timeouts(unsigned long data) - { -- struct scsi_cmnd *sc; -+ struct iscsi_conn *conn = (struct iscsi_conn *)data; -+ struct iscsi_session *session = conn->session; -+ unsigned long timeout, next_timeout = 0, last_recv; - -- sc = ctask->sc; -- if (!sc) -+ spin_lock(&session->lock); -+ if (session->state != ISCSI_STATE_LOGGED_IN) -+ goto done; -+ -+ timeout = conn->recv_timeout; -+ if (!timeout) -+ goto done; -+ -+ timeout *= HZ; -+ last_recv = conn->last_recv; -+ if (time_before_eq(last_recv + timeout + (conn->ping_timeout * HZ), -+ jiffies)) { -+ iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs " -+ "expired, last rx %lu, last ping %lu, " -+ "now %lu\n", conn->ping_timeout, last_recv, -+ conn->last_ping, jiffies); -+ spin_unlock(&session->lock); -+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - return; -+ } - -- if (ctask->state != ISCSI_TASK_PENDING) -- conn->session->tt->cleanup_cmd_task(conn, ctask); -- iscsi_ctask_mtask_cleanup(ctask); -+ if (time_before_eq(last_recv + timeout, jiffies)) { -+ if (time_before_eq(conn->last_ping, last_recv)) { -+ /* send a ping to try to provoke some traffic */ -+ debug_scsi("Sending nopout as ping on conn %p\n", conn); -+ iscsi_send_nopout(conn, NULL); -+ } -+ next_timeout = last_recv + timeout + (conn->ping_timeout * HZ); -+ } else -+ next_timeout = last_recv + timeout; - -- sc->result = err; -- sc->resid = sc->request_bufflen; -- if (conn->ctask == ctask) -- conn->ctask = NULL; -- /* release ref from queuecommand */ -- __iscsi_put_ctask(ctask); -+ debug_scsi("Setting next tmo %lu\n", next_timeout); -+ mod_timer(&conn->transport_timer, next_timeout); -+done: -+ spin_unlock(&session->lock); -+} -+ -+static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask, -+ struct iscsi_tm *hdr) -+{ -+ memset(hdr, 0, sizeof(*hdr)); -+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; -+ hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK; -+ hdr->flags |= ISCSI_FLAG_CMD_FINAL; -+ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); -+ hdr->rtt = ctask->hdr->itt; -+ hdr->refcmdsn = ctask->hdr->cmdsn; - } - - int iscsi_eh_abort(struct scsi_cmnd *sc) - { -- struct iscsi_cmd_task *ctask; -+ struct Scsi_Host *host = sc->device->host; -+ struct iscsi_session *session = iscsi_hostdata(host->hostdata); - struct iscsi_conn *conn; -- struct iscsi_session *session; -- int rc; -+ struct iscsi_cmd_task *ctask; -+ struct iscsi_tm *hdr; -+ int rc, age; - -+ mutex_lock(&session->eh_mutex); -+ spin_lock_bh(&session->lock); - /* - * if session was ISCSI_STATE_IN_RECOVERY then we may not have - * got the command. - */ - if (!sc->SCp.ptr) { - debug_scsi("sc never reached iscsi layer or it completed.\n"); -+ spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); - return SUCCESS; - } - -- ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; -- conn = ctask->conn; -- session = conn->session; -- -- conn->eh_abort_cnt++; -- debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); -- -- spin_lock_bh(&session->lock); -- - /* - * If we are not logged in or we have started a new session - * then let the host reset code handle this - */ -- if (session->state != ISCSI_STATE_LOGGED_IN || -- sc->SCp.phase != session->age) -- goto failed; -+ if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN || -+ sc->SCp.phase != session->age) { -+ spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); -+ return FAILED; -+ } -+ -+ conn = session->leadconn; -+ conn->eh_abort_cnt++; -+ age = session->age; -+ -+ ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; -+ debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); - - /* ctask completed before time out */ - if (!ctask->sc) { -@@ -1191,123 +1450,212 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) - goto success; - } - -- /* what should we do here ? */ -- if (conn->ctask == ctask) { -- printk(KERN_INFO "iscsi: sc %p itt 0x%x partially sent. " -- "Failing abort\n", sc, ctask->itt); -- goto failed; -- } -- - if (ctask->state == ISCSI_TASK_PENDING) { - fail_command(conn, ctask, DID_ABORT << 16); - goto success; - } - -- conn->tmabort_state = TMABORT_INITIAL; -- rc = iscsi_exec_abort_task(sc, ctask); -- if (rc || sc->SCp.phase != session->age || -- session->state != ISCSI_STATE_LOGGED_IN) -+ /* only have one tmf outstanding at a time */ -+ if (conn->tmf_state != TMF_INITIAL) - goto failed; -- iscsi_ctask_mtask_cleanup(ctask); -+ conn->tmf_state = TMF_QUEUED; - -- switch (conn->tmabort_state) { -- case TMABORT_SUCCESS: -+ hdr = &conn->tmhdr; -+ iscsi_prep_abort_task_pdu(ctask, hdr); -+ -+ if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) { -+ rc = FAILED; -+ goto failed; -+ } -+ -+ switch (conn->tmf_state) { -+ case TMF_SUCCESS: - spin_unlock_bh(&session->lock); -+ iscsi_suspend_tx(conn); - /* - * clean up task if aborted. grab the recv lock as a writer - */ - write_lock_bh(conn->recv_lock); - spin_lock(&session->lock); - fail_command(conn, ctask, DID_ABORT << 16); -+ conn->tmf_state = TMF_INITIAL; - spin_unlock(&session->lock); - write_unlock_bh(conn->recv_lock); -- /* -- * make sure xmit thread is not still touching the -- * ctask/scsi_cmnd -- */ -- scsi_flush_work(session->host); -+ iscsi_start_tx(conn); - goto success_unlocked; -- case TMABORT_NOT_FOUND: -- if (!ctask->sc) { -+ case TMF_TIMEDOUT: -+ spin_unlock_bh(&session->lock); -+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -+ goto failed_unlocked; -+ case TMF_NOT_FOUND: -+ if (!sc->SCp.ptr) { -+ conn->tmf_state = TMF_INITIAL; - /* ctask completed before tmf abort response */ - debug_scsi("sc completed while abort in progress\n"); - goto success; - } - /* fall through */ - default: -- /* timedout or failed */ -- spin_unlock_bh(&session->lock); -- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -- goto failed_unlocked; -+ conn->tmf_state = TMF_INITIAL; -+ goto failed; - } - - success: - spin_unlock_bh(&session->lock); - success_unlocked: - debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); -+ mutex_unlock(&session->eh_mutex); - return SUCCESS; - - failed: - spin_unlock_bh(&session->lock); - failed_unlocked: -- debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); -+ debug_scsi("abort failed [sc %p itt 0x%x]\n", sc, -+ ctask ? ctask->itt : 0); -+ mutex_unlock(&session->eh_mutex); - return FAILED; - } - EXPORT_SYMBOL_GPL(iscsi_eh_abort); - -+static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) -+{ -+ memset(hdr, 0, sizeof(*hdr)); -+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; -+ hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK; -+ hdr->flags |= ISCSI_FLAG_CMD_FINAL; -+ int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); -+ hdr->rtt = RESERVED_ITT; -+} -+ -+int iscsi_eh_device_reset(struct scsi_cmnd *sc) -+{ -+ struct Scsi_Host *host = sc->device->host; -+ struct iscsi_session *session = iscsi_hostdata(host->hostdata); -+ struct iscsi_conn *conn; -+ struct iscsi_tm *hdr; -+ int rc = FAILED; -+ -+ debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun); -+ -+ mutex_lock(&session->eh_mutex); -+ spin_lock_bh(&session->lock); -+ /* -+ * Just check if we are not logged in. We cannot check for -+ * the phase because the reset could come from a ioctl. -+ */ -+ if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) -+ goto unlock; -+ conn = session->leadconn; -+ -+ /* only have one tmf outstanding at a time */ -+ if (conn->tmf_state != TMF_INITIAL) -+ goto unlock; -+ conn->tmf_state = TMF_QUEUED; -+ -+ hdr = &conn->tmhdr; -+ iscsi_prep_lun_reset_pdu(sc, hdr); -+ -+ if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, -+ session->lu_reset_timeout)) { -+ rc = FAILED; -+ goto unlock; -+ } -+ -+ switch (conn->tmf_state) { -+ case TMF_SUCCESS: -+ break; -+ case TMF_TIMEDOUT: -+ spin_unlock_bh(&session->lock); -+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); -+ goto done; -+ default: -+ conn->tmf_state = TMF_INITIAL; -+ goto unlock; -+ } -+ -+ rc = SUCCESS; -+ spin_unlock_bh(&session->lock); -+ -+ iscsi_suspend_tx(conn); -+ /* need to grab the recv lock then session lock */ -+ write_lock_bh(conn->recv_lock); -+ spin_lock(&session->lock); -+ fail_all_commands(conn, sc->device->lun, DID_ERROR); -+ conn->tmf_state = TMF_INITIAL; -+ spin_unlock(&session->lock); -+ write_unlock_bh(conn->recv_lock); -+ -+ iscsi_start_tx(conn); -+ goto done; -+ -+unlock: -+ spin_unlock_bh(&session->lock); -+done: -+ debug_scsi("iscsi_eh_device_reset %s\n", -+ rc == SUCCESS ? "SUCCESS" : "FAILED"); -+ mutex_unlock(&session->eh_mutex); -+ return rc; -+} -+EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); -+ -+/* -+ * Pre-allocate a pool of @max items of @item_size. By default, the pool -+ * should be accessed via kfifo_{get,put} on q->queue. -+ * Optionally, the caller can obtain the array of object pointers -+ * by passing in a non-NULL @items pointer -+ */ - int --iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size) -+iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size) - { -- int i; -+ int i, num_arrays = 1; - -- *items = kmalloc(max * sizeof(void*), GFP_KERNEL); -- if (*items == NULL) -- return -ENOMEM; -+ memset(q, 0, sizeof(*q)); - - q->max = max; -- q->pool = kmalloc(max * sizeof(void*), GFP_KERNEL); -- if (q->pool == NULL) { -- kfree(*items); -- return -ENOMEM; -- } -+ -+ /* If the user passed an items pointer, he wants a copy of -+ * the array. */ -+ if (items) -+ num_arrays++; -+ q->pool = kzalloc(num_arrays * max * sizeof(void*), GFP_KERNEL); -+ if (q->pool == NULL) -+ goto enomem; - - q->queue = kfifo_init((void*)q->pool, max * sizeof(void*), - GFP_KERNEL, NULL); -- if (q->queue == ERR_PTR(-ENOMEM)) { -- kfree(q->pool); -- kfree(*items); -- return -ENOMEM; -- } -+ if (q->queue == ERR_PTR(-ENOMEM)) -+ goto enomem; - - for (i = 0; i < max; i++) { -- q->pool[i] = kmalloc(item_size, GFP_KERNEL); -+ q->pool[i] = kzalloc(item_size, GFP_KERNEL); - if (q->pool[i] == NULL) { -- int j; -- -- for (j = 0; j < i; j++) -- kfree(q->pool[j]); -- -- kfifo_free(q->queue); -- kfree(q->pool); -- kfree(*items); -- return -ENOMEM; -+ q->max = i; -+ goto enomem; - } -- memset(q->pool[i], 0, item_size); -- (*items)[i] = q->pool[i]; - __kfifo_put(q->queue, (void*)&q->pool[i], sizeof(void*)); - } -+ -+ if (items) { -+ *items = q->pool + max; -+ memcpy(*items, q->pool, max * sizeof(void *)); -+ } -+ - return 0; -+ -+enomem: -+ iscsi_pool_free(q); -+ return -ENOMEM; - } - EXPORT_SYMBOL_GPL(iscsi_pool_init); - --void iscsi_pool_free(struct iscsi_queue *q, void **items) -+void iscsi_pool_free(struct iscsi_pool *q) - { - int i; - - for (i = 0; i < q->max; i++) -- kfree(items[i]); -- kfree(q->pool); -- kfree(items); -+ kfree(q->pool[i]); -+ if (q->pool) -+ kfree(q->pool); - } - EXPORT_SYMBOL_GPL(iscsi_pool_free); - -@@ -1357,11 +1705,11 @@ iscsi_session_setup(struct iscsi_transport *iscsit, - printk(KERN_ERR "iscsi: invalid queue depth of %d. " - "Queue depth must be between 1 and %d.\n", - qdepth, ISCSI_MAX_CMD_PER_LUN); -- qdepth = ISCSI_DEF_CMD_PER_LUN; -+ qdepth = ISCSI_DEF_CMD_PER_LUN; - } - -- if (cmds_max < 2 || (cmds_max & (cmds_max - 1)) || -- cmds_max >= ISCSI_MGMT_ITT_OFFSET) { -+ if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET || -+ cmds_max < 2) { - if (cmds_max != 0) - printk(KERN_ERR "iscsi: invalid can_queue of %d. " - "can_queue must be a power of 2 and between " -@@ -1384,19 +1732,24 @@ iscsi_session_setup(struct iscsi_transport *iscsit, - shost->max_cmd_len = iscsit->max_cmd_len; - shost->transportt = scsit; - shost->transportt->create_work_queue = 1; -+ shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out; - *hostno = shost->host_no; - - session = iscsi_hostdata(shost->hostdata); - memset(session, 0, sizeof(struct iscsi_session)); - session->host = shost; - session->state = ISCSI_STATE_FREE; -+ session->fast_abort = 1; -+ session->lu_reset_timeout = 15; -+ session->abort_timeout = 10; - session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX; - session->cmds_max = cmds_max; -- session->cmdsn = initial_cmdsn; -+ session->queued_cmdsn = session->cmdsn = initial_cmdsn; - session->exp_cmdsn = initial_cmdsn + 1; - session->max_cmdsn = initial_cmdsn + 1; - session->max_r2t = 1; - session->tt = iscsit; -+ mutex_init(&session->eh_mutex); - - /* initialize SCSI PDU commands pool */ - if (iscsi_pool_init(&session->cmdpool, session->cmds_max, -@@ -1451,9 +1804,9 @@ module_put: - cls_session_fail: - scsi_remove_host(shost); - add_host_fail: -- iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); -+ iscsi_pool_free(&session->mgmtpool); - mgmtpool_alloc_fail: -- iscsi_pool_free(&session->cmdpool, (void**)session->cmds); -+ iscsi_pool_free(&session->cmdpool); - cmdpool_alloc_fail: - scsi_host_put(shost); - return NULL; -@@ -1473,10 +1826,11 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) - struct iscsi_session *session = iscsi_hostdata(shost->hostdata); - struct module *owner = cls_session->transport->owner; - -+ iscsi_remove_session(cls_session); - scsi_remove_host(shost); - -- iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); -- iscsi_pool_free(&session->cmdpool, (void**)session->cmds); -+ iscsi_pool_free(&session->mgmtpool); -+ iscsi_pool_free(&session->cmdpool); - - kfree(session->password); - kfree(session->password_in); -@@ -1486,8 +1840,9 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) - kfree(session->netdev); - kfree(session->hwaddress); - kfree(session->initiatorname); -+ kfree(session->ifacename); - -- iscsi_destroy_session(cls_session); -+ iscsi_free_session(cls_session); - scsi_host_put(shost); - module_put(owner); - } -@@ -1517,17 +1872,17 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) - conn->c_stage = ISCSI_CONN_INITIAL_STAGE; - conn->id = conn_idx; - conn->exp_statsn = 0; -- conn->tmabort_state = TMABORT_INITIAL; -+ conn->tmf_state = TMF_INITIAL; -+ -+ init_timer(&conn->transport_timer); -+ conn->transport_timer.data = (unsigned long)conn; -+ conn->transport_timer.function = iscsi_check_transport_timeouts; -+ - INIT_LIST_HEAD(&conn->run_list); - INIT_LIST_HEAD(&conn->mgmt_run_list); -+ INIT_LIST_HEAD(&conn->mgmtqueue); - INIT_LIST_HEAD(&conn->xmitqueue); -- -- /* initialize general immediate & non-immediate PDU commands queue */ -- conn->mgmtqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), -- GFP_KERNEL, NULL); -- if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) -- goto mgmtqueue_alloc_fail; -- -+ INIT_LIST_HEAD(&conn->requeue); - INIT_WORK(&conn->xmitwork, iscsi_xmitworker); - - /* allocate login_mtask used for the login/text sequences */ -@@ -1545,7 +1900,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) - goto login_mtask_data_alloc_fail; - conn->login_mtask->data = conn->data = data; - -- init_timer(&conn->tmabort_timer); -+ init_timer(&conn->tmf_timer); - init_waitqueue_head(&conn->ehwait); - - return cls_conn; -@@ -1554,8 +1909,6 @@ login_mtask_data_alloc_fail: - __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, - sizeof(void*)); - login_mtask_alloc_fail: -- kfifo_free(conn->mgmtqueue); --mgmtqueue_alloc_fail: - iscsi_destroy_conn(cls_conn); - return NULL; - } -@@ -1574,8 +1927,9 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) - struct iscsi_session *session = conn->session; - unsigned long flags; - -+ del_timer_sync(&conn->transport_timer); -+ - spin_lock_bh(&session->lock); -- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); - conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; - if (session->leadconn == conn) { - /* -@@ -1598,9 +1952,10 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) - } - spin_unlock_irqrestore(session->host->host_lock, flags); - msleep_interruptible(500); -- printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d " -- "host_failed %d\n", session->host->host_busy, -- session->host->host_failed); -+ iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): " -+ "host_busy %d host_failed %d\n", -+ session->host->host_busy, -+ session->host->host_failed); - /* - * force eh_abort() to unblock - */ -@@ -1608,22 +1963,17 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) - } - - /* flush queued up work because we free the connection below */ -- scsi_flush_work(session->host); -+ iscsi_suspend_tx(conn); - - spin_lock_bh(&session->lock); - kfree(conn->data); - kfree(conn->persistent_address); - __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, - sizeof(void*)); -- if (session->leadconn == conn) { -+ if (session->leadconn == conn) - session->leadconn = NULL; -- /* no connections exits.. reset sequencing */ -- session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1; -- } - spin_unlock_bh(&session->lock); - -- kfifo_free(conn->mgmtqueue); -- - iscsi_destroy_conn(cls_conn); - } - EXPORT_SYMBOL_GPL(iscsi_conn_teardown); -@@ -1634,21 +1984,41 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) - struct iscsi_session *session = conn->session; - - if (!session) { -- printk(KERN_ERR "iscsi: can't start unbound connection\n"); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "can't start unbound connection\n"); - return -EPERM; - } - - if ((session->imm_data_en || !session->initial_r2t_en) && - session->first_burst > session->max_burst) { -- printk("iscsi: invalid burst lengths: " -- "first_burst %d max_burst %d\n", -- session->first_burst, session->max_burst); -+ iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: " -+ "first_burst %d max_burst %d\n", -+ session->first_burst, session->max_burst); - return -EINVAL; - } - -+ if (conn->ping_timeout && !conn->recv_timeout) { -+ iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of " -+ "zero. Using 5 seconds\n."); -+ conn->recv_timeout = 5; -+ } -+ -+ if (conn->recv_timeout && !conn->ping_timeout) { -+ iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of " -+ "zero. Using 5 seconds.\n"); -+ conn->ping_timeout = 5; -+ } -+ - spin_lock_bh(&session->lock); - conn->c_stage = ISCSI_CONN_STARTED; - session->state = ISCSI_STATE_LOGGED_IN; -+ session->queued_cmdsn = session->cmdsn; -+ -+ conn->last_recv = jiffies; -+ conn->last_ping = jiffies; -+ if (conn->recv_timeout && conn->ping_timeout) -+ mod_timer(&conn->transport_timer, -+ jiffies + (conn->recv_timeout * HZ)); - - switch(conn->stop_stage) { - case STOP_CONN_RECOVER: -@@ -1657,13 +2027,11 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) - * commands after successful recovery - */ - conn->stop_stage = 0; -- conn->tmabort_state = TMABORT_INITIAL; -+ conn->tmf_state = TMF_INITIAL; - session->age++; -- spin_unlock_bh(&session->lock); -- -- iscsi_unblock_session(session_to_cls(session)); -- wake_up(&conn->ehwait); -- return 0; -+ if (session->age == 16) -+ session->age = 0; -+ break; - case STOP_CONN_TERM: - conn->stop_stage = 0; - break; -@@ -1672,6 +2040,8 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) - } - spin_unlock_bh(&session->lock); - -+ iscsi_unblock_session(session_to_cls(session)); -+ wake_up(&conn->ehwait); - return 0; - } - EXPORT_SYMBOL_GPL(iscsi_conn_start); -@@ -1682,58 +2052,43 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn) - struct iscsi_mgmt_task *mtask, *tmp; - - /* handle pending */ -- while (__kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) { -- if (mtask == conn->login_mtask) -- continue; -+ list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) { - debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt); -- __kfifo_put(session->mgmtpool.queue, (void*)&mtask, -- sizeof(void*)); -+ iscsi_free_mgmt_task(conn, mtask); - } - - /* handle running */ - list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) { - debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt); -- list_del(&mtask->running); -- -- if (mtask == conn->login_mtask) -- continue; -- __kfifo_put(session->mgmtpool.queue, (void*)&mtask, -- sizeof(void*)); -+ iscsi_free_mgmt_task(conn, mtask); - } - - conn->mtask = NULL; - } - --/* Fail commands. Mutex and session lock held and recv side suspended */ --static void fail_all_commands(struct iscsi_conn *conn) --{ -- struct iscsi_cmd_task *ctask, *tmp; -- -- /* flush pending */ -- list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) { -- debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc, -- ctask->itt); -- fail_command(conn, ctask, DID_BUS_BUSY << 16); -- } -- -- /* fail all other running */ -- list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) { -- debug_scsi("failing in progress sc %p itt 0x%x\n", -- ctask->sc, ctask->itt); -- fail_command(conn, ctask, DID_BUS_BUSY << 16); -- } -- -- conn->ctask = NULL; --} -- - static void iscsi_start_session_recovery(struct iscsi_session *session, - struct iscsi_conn *conn, int flag) - { - int old_stop_stage; - -+ del_timer_sync(&conn->transport_timer); -+ -+ mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (conn->stop_stage == STOP_CONN_TERM) { - spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); -+ return; -+ } -+ -+ /* -+ * The LLD either freed/unset the lock on us, or userspace called -+ * stop but did not create a proper connection (connection was never -+ * bound or it was unbound then stop was called). -+ */ -+ if (!conn->recv_lock) { -+ spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); - return; - } - -@@ -1750,9 +2105,9 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, - old_stop_stage = conn->stop_stage; - conn->stop_stage = flag; - conn->c_stage = ISCSI_CONN_STOPPED; -- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); - spin_unlock_bh(&session->lock); -- scsi_flush_work(session->host); -+ -+ iscsi_suspend_tx(conn); - - write_lock_bh(conn->recv_lock); - set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); -@@ -1778,9 +2133,11 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, - * flush queues. - */ - spin_lock_bh(&session->lock); -- fail_all_commands(conn); -+ fail_all_commands(conn, -1, -+ STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR); - flush_control_queues(session, conn); - spin_unlock_bh(&session->lock); -+ mutex_unlock(&session->eh_mutex); - } - - void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) -@@ -1794,7 +2151,8 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) - iscsi_start_session_recovery(session, conn, flag); - break; - default: -- printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); -+ iscsi_conn_printk(KERN_ERR, conn, -+ "invalid stop flag %d\n", flag); - } - } - EXPORT_SYMBOL_GPL(iscsi_conn_stop); -@@ -1828,6 +2186,21 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, - uint32_t value; - - switch(param) { -+ case ISCSI_PARAM_FAST_ABORT: -+ sscanf(buf, "%d", &session->fast_abort); -+ break; -+ case ISCSI_PARAM_ABORT_TMO: -+ sscanf(buf, "%d", &session->abort_timeout); -+ break; -+ case ISCSI_PARAM_LU_RESET_TMO: -+ sscanf(buf, "%d", &session->lu_reset_timeout); -+ break; -+ case ISCSI_PARAM_PING_TMO: -+ sscanf(buf, "%d", &conn->ping_timeout); -+ break; -+ case ISCSI_PARAM_RECV_TMO: -+ sscanf(buf, "%d", &conn->recv_timeout); -+ break; - case ISCSI_PARAM_MAX_RECV_DLENGTH: - sscanf(buf, "%d", &conn->max_recv_dlength); - break; -@@ -1926,6 +2299,10 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, - if (!conn->persistent_address) - return -ENOMEM; - break; -+ case ISCSI_PARAM_IFACE_NAME: -+ if (!session->ifacename) -+ session->ifacename = kstrdup(buf, GFP_KERNEL); -+ break; - default: - return -ENOSYS; - } -@@ -1942,6 +2319,15 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, - int len; - - switch(param) { -+ case ISCSI_PARAM_FAST_ABORT: -+ len = sprintf(buf, "%d\n", session->fast_abort); -+ break; -+ case ISCSI_PARAM_ABORT_TMO: -+ len = sprintf(buf, "%d\n", session->abort_timeout); -+ break; -+ case ISCSI_PARAM_LU_RESET_TMO: -+ len = sprintf(buf, "%d\n", session->lu_reset_timeout); -+ break; - case ISCSI_PARAM_INITIAL_R2T_EN: - len = sprintf(buf, "%d\n", session->initial_r2t_en); - break; -@@ -1984,6 +2370,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, - case ISCSI_PARAM_PASSWORD_IN: - len = sprintf(buf, "%s\n", session->password_in); - break; -+ case ISCSI_PARAM_IFACE_NAME: -+ len = sprintf(buf, "%s\n", session->ifacename); -+ break; - default: - return -ENOSYS; - } -@@ -1999,6 +2388,12 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, - int len; - - switch(param) { -+ case ISCSI_PARAM_PING_TMO: -+ len = sprintf(buf, "%u\n", conn->ping_timeout); -+ break; -+ case ISCSI_PARAM_RECV_TMO: -+ len = sprintf(buf, "%u\n", conn->recv_timeout); -+ break; - case ISCSI_PARAM_MAX_RECV_DLENGTH: - len = sprintf(buf, "%u\n", conn->max_recv_dlength); - break; -@@ -2059,7 +2454,6 @@ int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, - else - len = sprintf(buf, "%s\n", session->initiatorname); - break; -- - default: - return -ENOSYS; - } -diff --git a/kernel/libiscsi.h b/kernel/libiscsi.h -index 3ee0f4f..a409e3a 100644 ---- a/kernel/libiscsi.h -+++ b/kernel/libiscsi.h -@@ -57,23 +57,31 @@ struct iscsi_nopin; - #define ISCSI_MAX_CMD_PER_LUN 128 - - /* Task Mgmt states */ --#define TMABORT_INITIAL 0x0 --#define TMABORT_SUCCESS 0x1 --#define TMABORT_FAILED 0x2 --#define TMABORT_TIMEDOUT 0x3 --#define TMABORT_NOT_FOUND 0x4 -+enum { -+ TMF_INITIAL, -+ TMF_QUEUED, -+ TMF_SUCCESS, -+ TMF_FAILED, -+ TMF_TIMEDOUT, -+ TMF_NOT_FOUND, -+}; - - /* Connection suspend "bit" */ - #define ISCSI_SUSPEND_BIT 1 - - #define ISCSI_ITT_MASK (0xfff) --#define ISCSI_CID_SHIFT 12 --#define ISCSI_CID_MASK (0xffff << ISCSI_CID_SHIFT) - #define ISCSI_AGE_SHIFT 28 - #define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT) - - #define ISCSI_ADDRESS_BUF_LEN 64 - -+enum { -+ /* this is the maximum possible storage for AHSs */ -+ ISCSI_MAX_AHS_SIZE = sizeof(struct iscsi_ecdb_ahdr) + -+ sizeof(struct iscsi_rlength_ahdr), -+ ISCSI_DIGEST_SIZE = sizeof(__u32), -+}; -+ - struct iscsi_mgmt_task { - /* - * Becuae LLDs allocate their hdr differently, this is a pointer to -@@ -91,15 +99,17 @@ enum { - ISCSI_TASK_COMPLETED, - ISCSI_TASK_PENDING, - ISCSI_TASK_RUNNING, -- ISCSI_TASK_ABORTING, - }; - - struct iscsi_cmd_task { - /* -- * Becuae LLDs allocate their hdr differently, this is a pointer to -- * that storage. It must be setup at session creation time. -+ * Because LLDs allocate their hdr differently, this is a pointer -+ * and length to that storage. It must be setup at session -+ * creation time. - */ - struct iscsi_cmd *hdr; -+ unsigned short hdr_max; -+ unsigned short hdr_len; /* accumulated size of hdr used */ - int itt; /* this ITT */ - - uint32_t unsol_datasn; -@@ -110,7 +120,6 @@ struct iscsi_cmd_task { - unsigned data_count; /* remaining Data-Out */ - struct scsi_cmnd *sc; /* associated SCSI cmd*/ - struct iscsi_conn *conn; /* used connection */ -- struct iscsi_mgmt_task *mtask; /* tmf mtask in progr */ - - /* state set/tested under session->lock */ - int state; -@@ -119,6 +128,19 @@ struct iscsi_cmd_task { - void *dd_data; /* driver/transport data */ - }; - -+static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask) -+{ -+ return (void*)ctask->hdr + ctask->hdr_len; -+} -+ -+/* Connection's states */ -+enum { -+ ISCSI_CONN_INITIAL_STAGE, -+ ISCSI_CONN_STARTED, -+ ISCSI_CONN_STOPPED, -+ ISCSI_CONN_CLEANUP_WAIT, -+}; -+ - struct iscsi_conn { - struct iscsi_cls_conn *cls_conn; /* ptr to class connection */ - void *dd_data; /* iscsi_transport data */ -@@ -132,6 +154,12 @@ struct iscsi_conn { - * conn_stop() flag: stop to recover, stop to terminate - */ - int stop_stage; -+ struct timer_list transport_timer; -+ unsigned long last_recv; -+ unsigned long last_ping; -+ int ping_timeout; -+ int recv_timeout; -+ struct iscsi_mgmt_task *ping_mtask; - - /* iSCSI connection-wide sequencing */ - uint32_t exp_statsn; -@@ -152,10 +180,11 @@ struct iscsi_conn { - struct iscsi_cmd_task *ctask; /* xmit ctask in progress */ - - /* xmit */ -- struct kfifo *mgmtqueue; /* mgmt (control) xmit queue */ -+ struct list_head mgmtqueue; /* mgmt (control) xmit queue */ - struct list_head mgmt_run_list; /* list of control tasks */ - struct list_head xmitqueue; /* data-path cmd queue */ - struct list_head run_list; /* list of cmds in progress */ -+ struct list_head requeue; /* tasks needing another run */ - struct work_struct xmitwork; /* per-conn. xmit workqueue */ - unsigned long suspend_tx; /* suspend Tx */ - unsigned long suspend_rx; /* suspend Rx */ -@@ -163,8 +192,8 @@ struct iscsi_conn { - /* abort */ - wait_queue_head_t ehwait; /* used in eh_abort() */ - struct iscsi_tm tmhdr; -- struct timer_list tmabort_timer; -- int tmabort_state; /* see TMABORT_INITIAL, etc.*/ -+ struct timer_list tmf_timer; -+ int tmf_state; /* see TMF_INITIAL, etc.*/ - - /* negotiated params */ - unsigned max_recv_dlength; /* initiator_max_recv_dsl*/ -@@ -198,19 +227,42 @@ struct iscsi_conn { - uint32_t eh_abort_cnt; - }; - --struct iscsi_queue { -+struct iscsi_pool { - struct kfifo *queue; /* FIFO Queue */ - void **pool; /* Pool of elements */ - int max; /* Max number of elements */ - }; - -+/* Session's states */ -+enum { -+ ISCSI_STATE_FREE = 1, -+ ISCSI_STATE_LOGGED_IN, -+ ISCSI_STATE_FAILED, -+ ISCSI_STATE_TERMINATE, -+ ISCSI_STATE_IN_RECOVERY, -+ ISCSI_STATE_RECOVERY_FAILED, -+ ISCSI_STATE_LOGGING_OUT, -+}; -+ - struct iscsi_session { -+ /* -+ * Syncs up the scsi eh thread with the iscsi eh thread when sending -+ * task management functions. This must be taken before the session -+ * and recv lock. -+ */ -+ struct mutex eh_mutex; -+ - /* iSCSI session-wide sequencing */ - uint32_t cmdsn; - uint32_t exp_cmdsn; - uint32_t max_cmdsn; - -+ /* This tracks the reqs queued into the initiator */ -+ uint32_t queued_cmdsn; -+ - /* configuration */ -+ int abort_timeout; -+ int lu_reset_timeout; - int initial_r2t_en; - unsigned max_r2t; - int imm_data_en; -@@ -221,6 +273,7 @@ struct iscsi_session { - int pdu_inorder_en; - int dataseq_inorder_en; - int erl; -+ int fast_abort; - int tpgt; - char *username; - char *username_in; -@@ -231,6 +284,7 @@ struct iscsi_session { - /* hw address or netdev iscsi connection is bound to */ - char *hwaddress; - char *netdev; -+ char *ifacename; - /* control data */ - struct iscsi_transport *tt; - struct Scsi_Host *host; -@@ -246,10 +300,10 @@ struct iscsi_session { - - int cmds_max; /* size of cmds array */ - struct iscsi_cmd_task **cmds; /* Original Cmds arr */ -- struct iscsi_queue cmdpool; /* PDU's pool */ -+ struct iscsi_pool cmdpool; /* PDU's pool */ - int mgmtpool_max; /* size of mgmt array */ - struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */ -- struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */ -+ struct iscsi_pool mgmtpool; /* Mgmt PDU's pool */ - }; - - /* -@@ -258,6 +312,7 @@ struct iscsi_session { - extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth); - extern int iscsi_eh_abort(struct scsi_cmnd *sc); - extern int iscsi_eh_host_reset(struct scsi_cmnd *sc); -+extern int iscsi_eh_device_reset(struct scsi_cmnd *sc); - extern int iscsi_queuecommand(struct scsi_cmnd *sc, - void (*done)(struct scsi_cmnd *)); - -@@ -288,6 +343,10 @@ extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session, - #define session_to_cls(_sess) \ - hostdata_session(_sess->host->hostdata) - -+#define iscsi_session_printk(prefix, _sess, fmt, a...) \ -+ iscsi_cls_session_printk(prefix, \ -+ (struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a) -+ - /* - * connection management - */ -@@ -302,6 +361,9 @@ extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err); - extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, - enum iscsi_param param, char *buf); - -+#define iscsi_conn_printk(prefix, _c, fmt, a...) \ -+ iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a) -+ - /* - * pdu and task processing - */ -@@ -312,15 +374,34 @@ extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *, - char *, uint32_t); - extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, - char *, int); --extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, -- char *, int); - extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *, - uint32_t *); -+extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask); -+extern void iscsi_free_mgmt_task(struct iscsi_conn *conn, -+ struct iscsi_mgmt_task *mtask); - - /* - * generic helpers - */ --extern void iscsi_pool_free(struct iscsi_queue *, void **); --extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int); -+extern void iscsi_pool_free(struct iscsi_pool *); -+extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int); -+ -+/* -+ * inline functions to deal with padding. -+ */ -+static inline unsigned int -+iscsi_padded(unsigned int len) -+{ -+ return (len + ISCSI_PAD_LEN - 1) & ~(ISCSI_PAD_LEN - 1); -+} -+ -+static inline unsigned int -+iscsi_padding(unsigned int len) -+{ -+ len &= (ISCSI_PAD_LEN - 1); -+ if (len) -+ len = ISCSI_PAD_LEN - len; -+ return len; -+} - - #endif -diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c -index 29730a1..98bb210 100644 ---- a/kernel/scsi_transport_iscsi.c -+++ b/kernel/scsi_transport_iscsi.c -@@ -30,10 +30,10 @@ - #include "scsi_transport_iscsi.h" - #include "iscsi_if.h" - --#define ISCSI_SESSION_ATTRS 15 --#define ISCSI_CONN_ATTRS 11 -+#define ISCSI_SESSION_ATTRS 20 -+#define ISCSI_CONN_ATTRS 13 - #define ISCSI_HOST_ATTRS 4 --#define ISCSI_TRANSPORT_VERSION "2.0-865" -+#define ISCSI_TRANSPORT_VERSION "2.0-869" - - struct iscsi_internal { - int daemon_pid; -@@ -50,6 +50,7 @@ struct iscsi_internal { - }; - - static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ -+static struct workqueue_struct *iscsi_eh_timer_workq; - - /* - * list of registered transports and lock that must -@@ -115,6 +116,8 @@ static struct attribute_group iscsi_transport_group = { - .attrs = iscsi_transport_attrs, - }; - -+ -+ - static int iscsi_setup_host(struct transport_container *tc, struct device *dev, - struct class_device *cdev) - { -@@ -124,13 +127,31 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev, - memset(ihost, 0, sizeof(*ihost)); - INIT_LIST_HEAD(&ihost->sessions); - mutex_init(&ihost->mutex); -+ atomic_set(&ihost->nr_scans, 0); -+ -+ snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d", -+ shost->host_no); -+ ihost->scan_workq = create_singlethread_workqueue( -+ ihost->scan_workq_name); -+ if (!ihost->scan_workq) -+ return -ENOMEM; -+ return 0; -+} -+ -+static int iscsi_remove_host(struct transport_container *tc, struct device *dev, -+ struct class_device *cdev) -+{ -+ struct Scsi_Host *shost = dev_to_shost(dev); -+ struct iscsi_host *ihost = shost->shost_data; -+ -+ destroy_workqueue(ihost->scan_workq); - return 0; - } - - static DECLARE_TRANSPORT_CLASS(iscsi_host_class, - "iscsi_host", - iscsi_setup_host, -- NULL, -+ iscsi_remove_host, - NULL); - - static DECLARE_TRANSPORT_CLASS(iscsi_session_class, -@@ -201,6 +222,54 @@ static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid) - * The following functions can be used by LLDs that allocate - * their own scsi_hosts or by software iscsi LLDs - */ -+static struct { -+ int value; -+ char *name; -+} iscsi_session_state_names[] = { -+ { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" }, -+ { ISCSI_SESSION_FAILED, "FAILED" }, -+ { ISCSI_SESSION_FREE, "FREE" }, -+}; -+ -+static const char *iscsi_session_state_name(int state) -+{ -+ int i; -+ char *name = NULL; -+ -+ for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) { -+ if (iscsi_session_state_names[i].value == state) { -+ name = iscsi_session_state_names[i].name; -+ break; -+ } -+ } -+ return name; -+} -+ -+int iscsi_session_chkready(struct iscsi_cls_session *session) -+{ -+ unsigned long flags; -+ int err; -+ -+ spin_lock_irqsave(&session->lock, flags); -+ switch (session->state) { -+ case ISCSI_SESSION_LOGGED_IN: -+ err = 0; -+ break; -+ case ISCSI_SESSION_FAILED: -+ err = DID_IMM_RETRY << 16; -+ break; -+ case ISCSI_SESSION_FREE: -+ err = DID_NO_CONNECT << 16; -+ break; -+ default: -+ err = DID_NO_CONNECT << 16; -+ break; -+ } -+ spin_unlock_irqrestore(&session->lock, flags); -+ return err; -+} -+EXPORT_SYMBOL_GPL(iscsi_session_chkready); -+ - static void iscsi_session_release(struct device *dev) - { - struct iscsi_cls_session *session = iscsi_dev_to_session(dev); -@@ -216,6 +285,25 @@ static int iscsi_is_session_dev(const struct device *dev) - return dev->release == iscsi_session_release; - } - -+/** -+ * iscsi_scan_finished - helper to report when running scans are done -+ * @shost: scsi host -+ * @time: scan run time -+ * -+ * This function can be used by drives like qla4xxx to report to the scsi -+ * layer when the scans it kicked off at module load time are done. -+ */ -+int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time) -+{ -+ struct iscsi_host *ihost = shost->shost_data; -+ /* -+ * qla4xxx will have kicked off some session unblocks before calling -+ * scsi_scan_host, so just wait for them to complete. -+ */ -+ return !atomic_read(&ihost->nr_scans); -+} -+EXPORT_SYMBOL_GPL(iscsi_scan_finished); -+ - static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, - uint id, uint lun) - { -@@ -234,14 +322,50 @@ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, - return 0; - } - -+static void iscsi_scan_session(struct work_struct *work) -+{ -+ struct iscsi_cls_session *session = -+ container_of(work, struct iscsi_cls_session, scan_work); -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&session->lock, flags); -+ if (session->state != ISCSI_SESSION_LOGGED_IN) { -+ spin_unlock_irqrestore(&session->lock, flags); -+ goto done; -+ } -+ spin_unlock_irqrestore(&session->lock, flags); -+ -+ scsi_scan_target(&session->dev, 0, session->target_id, -+ SCAN_WILD_CARD, 1); -+done: -+ atomic_dec(&ihost->nr_scans); -+} -+ - static void session_recovery_timedout(struct work_struct *work) - { - struct iscsi_cls_session *session = - container_of(work, struct iscsi_cls_session, - recovery_work.work); -+ unsigned long flags; -+ -+ iscsi_cls_session_printk(KERN_INFO, session, -+ "session recovery timed out after %d secs\n", -+ session->recovery_tmo); - -- dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " -- "out after %d secs\n", session->recovery_tmo); -+ spin_lock_irqsave(&session->lock, flags); -+ switch (session->state) { -+ case ISCSI_SESSION_FAILED: -+ session->state = ISCSI_SESSION_FREE; -+ break; -+ case ISCSI_SESSION_LOGGED_IN: -+ case ISCSI_SESSION_FREE: -+ /* we raced with the unblock's flush */ -+ spin_unlock_irqrestore(&session->lock, flags); -+ return; -+ } -+ spin_unlock_irqrestore(&session->lock, flags); - - if (session->transport->session_recovery_timedout) - session->transport->session_recovery_timedout(session); -@@ -249,22 +373,103 @@ static void session_recovery_timedout(struct work_struct *work) - scsi_target_unblock(&session->dev); - } - --void iscsi_unblock_session(struct iscsi_cls_session *session) -+static void __iscsi_unblock_session(struct work_struct *work) - { -- if (!cancel_delayed_work(&session->recovery_work)) -- flush_scheduled_work(); -+ struct iscsi_cls_session *session = -+ container_of(work, struct iscsi_cls_session, -+ unblock_work); -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ unsigned long flags; -+ -+ /* -+ * The recovery and unblock work get run from the same workqueue, -+ * so try to cancel it if it was going to run after this unblock. -+ */ -+ cancel_delayed_work(&session->recovery_work); -+ spin_lock_irqsave(&session->lock, flags); -+ session->state = ISCSI_SESSION_LOGGED_IN; -+ spin_unlock_irqrestore(&session->lock, flags); -+ /* start IO */ - scsi_target_unblock(&session->dev); -+ /* -+ * Only do kernel scanning if the driver is properly hooked into -+ * the async scanning code (drivers like iscsi_tcp do login and -+ * scanning from userspace). -+ */ -+ if (shost->hostt->scan_finished) { -+ if (queue_work(ihost->scan_workq, &session->scan_work)) -+ atomic_inc(&ihost->nr_scans); -+ } -+} -+ -+/** -+ * iscsi_unblock_session - set a session as logged in and start IO. -+ * @session: iscsi session -+ * -+ * Mark a session as ready to accept IO. -+ */ -+void iscsi_unblock_session(struct iscsi_cls_session *session) -+{ -+ queue_work(iscsi_eh_timer_workq, &session->unblock_work); -+ /* -+ * make sure all the events have completed before tell the driver -+ * it is safe -+ */ -+ flush_workqueue(iscsi_eh_timer_workq); - } - EXPORT_SYMBOL_GPL(iscsi_unblock_session); - --void iscsi_block_session(struct iscsi_cls_session *session) -+static void __iscsi_block_session(struct work_struct *work) - { -+ struct iscsi_cls_session *session = -+ container_of(work, struct iscsi_cls_session, -+ block_work); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&session->lock, flags); -+ session->state = ISCSI_SESSION_FAILED; -+ spin_unlock_irqrestore(&session->lock, flags); - scsi_target_block(&session->dev); -- schedule_delayed_work(&session->recovery_work, -- session->recovery_tmo * HZ); -+ queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, -+ session->recovery_tmo * HZ); -+} -+ -+void iscsi_block_session(struct iscsi_cls_session *session) -+{ -+ queue_work(iscsi_eh_timer_workq, &session->block_work); - } - EXPORT_SYMBOL_GPL(iscsi_block_session); - -+static void __iscsi_unbind_session(struct work_struct *work) -+{ -+ struct iscsi_cls_session *session = -+ container_of(work, struct iscsi_cls_session, -+ unbind_work); -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ -+ /* Prevent new scans and make sure scanning is not in progress */ -+ mutex_lock(&ihost->mutex); -+ if (list_empty(&session->host_list)) { -+ mutex_unlock(&ihost->mutex); -+ return; -+ } -+ list_del_init(&session->host_list); -+ mutex_unlock(&ihost->mutex); -+ -+ scsi_remove_target(&session->dev); -+ iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); -+} -+ -+static int iscsi_unbind_session(struct iscsi_cls_session *session) -+{ -+ struct Scsi_Host *shost = iscsi_session_to_shost(session); -+ struct iscsi_host *ihost = shost->shost_data; -+ -+ return queue_work(ihost->scan_workq, &session->unbind_work); -+} -+ - struct iscsi_cls_session * - iscsi_alloc_session(struct Scsi_Host *shost, - struct iscsi_transport *transport) -@@ -278,9 +483,15 @@ iscsi_alloc_session(struct Scsi_Host *shost, - - session->transport = transport; - session->recovery_tmo = 120; -+ session->state = ISCSI_SESSION_FREE; - INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); - INIT_LIST_HEAD(&session->host_list); - INIT_LIST_HEAD(&session->sess_list); -+ INIT_WORK(&session->unblock_work, __iscsi_unblock_session); -+ INIT_WORK(&session->block_work, __iscsi_block_session); -+ INIT_WORK(&session->unbind_work, __iscsi_unbind_session); -+ INIT_WORK(&session->scan_work, iscsi_scan_session); -+ spin_lock_init(&session->lock); - - /* this is released in the dev's release function */ - scsi_host_get(shost); -@@ -297,6 +508,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) - { - struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost; -+ unsigned long flags; - int err; - - ihost = shost->shost_data; -@@ -307,15 +519,21 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) - session->sid); - err = device_add(&session->dev); - if (err) { -- dev_printk(KERN_ERR, &session->dev, "iscsi: could not " -- "register session's dev\n"); -+ iscsi_cls_session_printk(KERN_ERR, session, -+ "could not register session's dev\n"); - goto release_host; - } - transport_register_device(&session->dev); - -+ spin_lock_irqsave(&sesslock, flags); -+ list_add(&session->sess_list, &sesslist); -+ spin_unlock_irqrestore(&sesslock, flags); -+ - mutex_lock(&ihost->mutex); - list_add(&session->host_list, &ihost->sessions); - mutex_unlock(&ihost->mutex); -+ -+ iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION); - return 0; - - release_host: -@@ -328,9 +546,10 @@ EXPORT_SYMBOL_GPL(iscsi_add_session); - * iscsi_create_session - create iscsi class session - * @shost: scsi host - * @transport: iscsi transport -+ * @target_id: which target - * - * This can be called from a LLD or iscsi_transport. -- **/ -+ */ - struct iscsi_cls_session * - iscsi_create_session(struct Scsi_Host *shost, - struct iscsi_transport *transport, -@@ -350,19 +569,65 @@ iscsi_create_session(struct Scsi_Host *shost, - } - EXPORT_SYMBOL_GPL(iscsi_create_session); - -+static void iscsi_conn_release(struct device *dev) -+{ -+ struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); -+ struct device *parent = conn->dev.parent; -+ -+ kfree(conn); -+ put_device(parent); -+} -+ -+static int iscsi_is_conn_dev(const struct device *dev) -+{ -+ return dev->release == iscsi_conn_release; -+} -+ -+static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data) -+{ -+ if (!iscsi_is_conn_dev(dev)) -+ return 0; -+ return iscsi_destroy_conn(iscsi_dev_to_conn(dev)); -+} -+ - void iscsi_remove_session(struct iscsi_cls_session *session) - { - struct Scsi_Host *shost = iscsi_session_to_shost(session); - struct iscsi_host *ihost = shost->shost_data; -+ unsigned long flags; -+ int err; -+ -+ spin_lock_irqsave(&sesslock, flags); -+ list_del(&session->sess_list); -+ spin_unlock_irqrestore(&sesslock, flags); - -+ /* make sure there are no blocks/unblocks queued */ -+ flush_workqueue(iscsi_eh_timer_workq); -+ /* make sure the timedout callout is not running */ - if (!cancel_delayed_work(&session->recovery_work)) -- flush_scheduled_work(); -+ flush_workqueue(iscsi_eh_timer_workq); -+ /* -+ * If we are blocked let commands flow again. The lld or iscsi -+ * layer should set up the queuecommand to fail commands. -+ * We assume that LLD will not be calling block/unblock while -+ * removing the session. -+ */ -+ spin_lock_irqsave(&session->lock, flags); -+ session->state = ISCSI_SESSION_FREE; -+ spin_unlock_irqrestore(&session->lock, flags); - -- mutex_lock(&ihost->mutex); -- list_del(&session->host_list); -- mutex_unlock(&ihost->mutex); -+ scsi_target_unblock(&session->dev); -+ /* flush running scans then delete devices */ -+ flush_workqueue(ihost->scan_workq); -+ __iscsi_unbind_session(&session->unbind_work); - -- scsi_remove_target(&session->dev); -+ /* hw iscsi may not have removed all connections from session */ -+ err = device_for_each_child(&session->dev, NULL, -+ iscsi_iter_destroy_conn_fn); -+ if (err) -+ iscsi_cls_session_printk(KERN_ERR, session, -+ "Could not delete all connections " -+ "for session. Error %d.\n", err); - - transport_unregister_device(&session->dev); - device_del(&session->dev); -@@ -371,9 +636,9 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session); - - void iscsi_free_session(struct iscsi_cls_session *session) - { -+ iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION); - put_device(&session->dev); - } -- - EXPORT_SYMBOL_GPL(iscsi_free_session); - - /** -@@ -382,7 +647,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_session); - * - * Can be called by a LLD or iscsi_transport. There must not be - * any running connections. -- **/ -+ */ - int iscsi_destroy_session(struct iscsi_cls_session *session) - { - iscsi_remove_session(session); -@@ -391,20 +656,6 @@ int iscsi_destroy_session(struct iscsi_cls_session *session) - } - EXPORT_SYMBOL_GPL(iscsi_destroy_session); - --static void iscsi_conn_release(struct device *dev) --{ -- struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); -- struct device *parent = conn->dev.parent; -- -- kfree(conn); -- put_device(parent); --} -- --static int iscsi_is_conn_dev(const struct device *dev) --{ -- return dev->release == iscsi_conn_release; --} -- - /** - * iscsi_create_conn - create iscsi class connection - * @session: iscsi cls session -@@ -418,12 +669,13 @@ static int iscsi_is_conn_dev(const struct device *dev) - * for software iscsi we could be trying to preallocate a connection struct - * in which case there could be two connection structs and cid would be - * non-zero. -- **/ -+ */ - struct iscsi_cls_conn * - iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) - { - struct iscsi_transport *transport = session->transport; - struct iscsi_cls_conn *conn; -+ unsigned long flags; - int err; - - conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL); -@@ -447,11 +699,16 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) - conn->dev.release = iscsi_conn_release; - err = device_register(&conn->dev); - if (err) { -- dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register " -- "connection's dev\n"); -+ iscsi_cls_session_printk(KERN_ERR, session, "could not " -+ "register connection's dev\n"); - goto release_parent_ref; - } - transport_register_device(&conn->dev); -+ -+ spin_lock_irqsave(&connlock, flags); -+ list_add(&conn->conn_list, &connlist); -+ conn->active = 1; -+ spin_unlock_irqrestore(&connlock, flags); - return conn; - - release_parent_ref: -@@ -465,17 +722,23 @@ EXPORT_SYMBOL_GPL(iscsi_create_conn); - - /** - * iscsi_destroy_conn - destroy iscsi class connection -- * @session: iscsi cls session -+ * @conn: iscsi cls session - * - * This can be called from a LLD or iscsi_transport. -- **/ -+ */ - int iscsi_destroy_conn(struct iscsi_cls_conn *conn) - { -+ unsigned long flags; -+ -+ spin_lock_irqsave(&connlock, flags); -+ conn->active = 0; -+ list_del(&conn->conn_list); -+ spin_unlock_irqrestore(&connlock, flags); -+ - transport_unregister_device(&conn->dev); - device_unregister(&conn->dev); - return 0; - } -- - EXPORT_SYMBOL_GPL(iscsi_destroy_conn); - - /* -@@ -544,8 +807,8 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, - skb = alloc_skb(len, GFP_ATOMIC); - if (!skb) { - iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED); -- dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver " -- "control PDU: OOM\n"); -+ iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver " -+ "control PDU: OOM\n"); - return -ENOMEM; - } - -@@ -578,8 +841,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) - - skb = alloc_skb(len, GFP_ATOMIC); - if (!skb) { -- dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored " -- "conn error (%d)\n", error); -+ iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored " -+ "conn error (%d)\n", error); - return; - } - -@@ -593,8 +856,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) - - iscsi_broadcast_skb(skb, GFP_ATOMIC); - -- dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n", -- error); -+ iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n", -+ error); - } - EXPORT_SYMBOL_GPL(iscsi_conn_error); - -@@ -649,8 +912,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) - - skbstat = alloc_skb(len, GFP_ATOMIC); - if (!skbstat) { -- dev_printk(KERN_ERR, &conn->dev, "iscsi: can not " -- "deliver stats: OOM\n"); -+ iscsi_cls_conn_printk(KERN_ERR, conn, "can not " -+ "deliver stats: OOM\n"); - return -ENOMEM; - } - -@@ -685,132 +948,77 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) - } - - /** -- * iscsi_if_destroy_session_done - send session destr. completion event -- * @conn: last connection for session -- * -- * This is called by HW iscsi LLDs to notify userpsace that its HW has -- * removed a session. -- **/ --int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn) -+ * iscsi_session_event - send session destr. completion event -+ * @session: iscsi class session -+ * @event: type of event -+ */ -+int iscsi_session_event(struct iscsi_cls_session *session, -+ enum iscsi_uevent_e event) - { - struct iscsi_internal *priv; -- struct iscsi_cls_session *session; - struct Scsi_Host *shost; - struct iscsi_uevent *ev; - struct sk_buff *skb; - struct nlmsghdr *nlh; -- unsigned long flags; - int rc, len = NLMSG_SPACE(sizeof(*ev)); - -- priv = iscsi_if_transport_lookup(conn->transport); -+ priv = iscsi_if_transport_lookup(session->transport); - if (!priv) - return -EINVAL; -- -- session = iscsi_dev_to_session(conn->dev.parent); - shost = iscsi_session_to_shost(session); - - skb = alloc_skb(len, GFP_KERNEL); - if (!skb) { -- dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " -- "session creation event\n"); -+ iscsi_cls_session_printk(KERN_ERR, session, -+ "Cannot notify userspace of session " -+ "event %u\n", event); - return -ENOMEM; - } - - nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); - ev = NLMSG_DATA(nlh); -- ev->transport_handle = iscsi_handle(conn->transport); -- ev->type = ISCSI_KEVENT_DESTROY_SESSION; -- ev->r.d_session.host_no = shost->host_no; -- ev->r.d_session.sid = session->sid; -- -- /* -- * this will occur if the daemon is not up, so we just warn -- * the user and when the daemon is restarted it will handle it -- */ -- rc = iscsi_broadcast_skb(skb, GFP_KERNEL); -- if (rc < 0) -- dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " -- "session destruction event. Check iscsi daemon\n"); -- -- spin_lock_irqsave(&sesslock, flags); -- list_del(&session->sess_list); -- spin_unlock_irqrestore(&sesslock, flags); -- -- spin_lock_irqsave(&connlock, flags); -- conn->active = 0; -- list_del(&conn->conn_list); -- spin_unlock_irqrestore(&connlock, flags); -- -- return rc; --} --EXPORT_SYMBOL_GPL(iscsi_if_destroy_session_done); -- --/** -- * iscsi_if_create_session_done - send session creation completion event -- * @conn: leading connection for session -- * -- * This is called by HW iscsi LLDs to notify userpsace that its HW has -- * created a session or a existing session is back in the logged in state. -- **/ --int iscsi_if_create_session_done(struct iscsi_cls_conn *conn) --{ -- struct iscsi_internal *priv; -- struct iscsi_cls_session *session; -- struct Scsi_Host *shost; -- struct iscsi_uevent *ev; -- struct sk_buff *skb; -- struct nlmsghdr *nlh; -- unsigned long flags; -- int rc, len = NLMSG_SPACE(sizeof(*ev)); -+ ev->transport_handle = iscsi_handle(session->transport); - -- priv = iscsi_if_transport_lookup(conn->transport); -- if (!priv) -+ ev->type = event; -+ switch (event) { -+ case ISCSI_KEVENT_DESTROY_SESSION: -+ ev->r.d_session.host_no = shost->host_no; -+ ev->r.d_session.sid = session->sid; -+ break; -+ case ISCSI_KEVENT_CREATE_SESSION: -+ ev->r.c_session_ret.host_no = shost->host_no; -+ ev->r.c_session_ret.sid = session->sid; -+ break; -+ case ISCSI_KEVENT_UNBIND_SESSION: -+ ev->r.unbind_session.host_no = shost->host_no; -+ ev->r.unbind_session.sid = session->sid; -+ break; -+ default: -+ iscsi_cls_session_printk(KERN_ERR, session, "Invalid event " -+ "%u.\n", event); -+ kfree_skb(skb); - return -EINVAL; -- -- session = iscsi_dev_to_session(conn->dev.parent); -- shost = iscsi_session_to_shost(session); -- -- skb = alloc_skb(len, GFP_KERNEL); -- if (!skb) { -- dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " -- "session creation event\n"); -- return -ENOMEM; - } - -- nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); -- ev = NLMSG_DATA(nlh); -- ev->transport_handle = iscsi_handle(conn->transport); -- ev->type = ISCSI_UEVENT_CREATE_SESSION; -- ev->r.c_session_ret.host_no = shost->host_no; -- ev->r.c_session_ret.sid = session->sid; -- - /* - * this will occur if the daemon is not up, so we just warn - * the user and when the daemon is restarted it will handle it - */ - rc = iscsi_broadcast_skb(skb, GFP_KERNEL); - if (rc < 0) -- dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " -- "session creation event. Check iscsi daemon\n"); -- -- spin_lock_irqsave(&sesslock, flags); -- list_add(&session->sess_list, &sesslist); -- spin_unlock_irqrestore(&sesslock, flags); -- -- spin_lock_irqsave(&connlock, flags); -- list_add(&conn->conn_list, &connlist); -- conn->active = 1; -- spin_unlock_irqrestore(&connlock, flags); -+ iscsi_cls_session_printk(KERN_ERR, session, -+ "Cannot notify userspace of session " -+ "event %u. Check iscsi daemon\n", -+ event); - return rc; - } --EXPORT_SYMBOL_GPL(iscsi_if_create_session_done); -+EXPORT_SYMBOL_GPL(iscsi_session_event); - - static int - iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) - { - struct iscsi_transport *transport = priv->iscsi_transport; - struct iscsi_cls_session *session; -- unsigned long flags; - uint32_t hostno; - - session = transport->create_session(transport, &priv->t, -@@ -821,10 +1029,6 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) - if (!session) - return -ENOMEM; - -- spin_lock_irqsave(&sesslock, flags); -- list_add(&session->sess_list, &sesslist); -- spin_unlock_irqrestore(&sesslock, flags); -- - ev->r.c_session_ret.host_no = hostno; - ev->r.c_session_ret.sid = session->sid; - return 0; -@@ -835,47 +1039,34 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) - { - struct iscsi_cls_conn *conn; - struct iscsi_cls_session *session; -- unsigned long flags; - - session = iscsi_session_lookup(ev->u.c_conn.sid); - if (!session) { -- printk(KERN_ERR "iscsi: invalid session %d\n", -+ printk(KERN_ERR "iscsi: invalid session %d.\n", - ev->u.c_conn.sid); - return -EINVAL; - } - - conn = transport->create_conn(session, ev->u.c_conn.cid); - if (!conn) { -- printk(KERN_ERR "iscsi: couldn't create a new " -- "connection for session %d\n", -- session->sid); -+ iscsi_cls_session_printk(KERN_ERR, session, -+ "couldn't create a new connection."); - return -ENOMEM; - } - - ev->r.c_conn_ret.sid = session->sid; - ev->r.c_conn_ret.cid = conn->cid; -- -- spin_lock_irqsave(&connlock, flags); -- list_add(&conn->conn_list, &connlist); -- conn->active = 1; -- spin_unlock_irqrestore(&connlock, flags); -- - return 0; - } - - static int - iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) - { -- unsigned long flags; - struct iscsi_cls_conn *conn; - - conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); - if (!conn) - return -EINVAL; -- spin_lock_irqsave(&connlock, flags); -- conn->active = 0; -- list_del(&conn->conn_list); -- spin_unlock_irqrestore(&connlock, flags); - - if (transport->destroy_conn) - transport->destroy_conn(conn); -@@ -1002,7 +1193,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) - struct iscsi_internal *priv; - struct iscsi_cls_session *session; - struct iscsi_cls_conn *conn; -- unsigned long flags; - - priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); - if (!priv) -@@ -1020,13 +1210,16 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) - break; - case ISCSI_UEVENT_DESTROY_SESSION: - session = iscsi_session_lookup(ev->u.d_session.sid); -- if (session) { -- spin_lock_irqsave(&sesslock, flags); -- list_del(&session->sess_list); -- spin_unlock_irqrestore(&sesslock, flags); -- -+ if (session) - transport->destroy_session(session); -- } else -+ else -+ err = -EINVAL; -+ break; -+ case ISCSI_UEVENT_UNBIND_SESSION: -+ session = iscsi_session_lookup(ev->u.d_session.sid); -+ if (session) -+ iscsi_unbind_session(session); -+ else - err = -EINVAL; - break; - case ISCSI_UEVENT_CREATE_CONN: -@@ -1097,61 +1290,49 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) - } - - /* -- * Get message from skb (based on rtnetlink_rcv_skb). Each message is -- * processed by iscsi_if_recv_msg. Malformed skbs with wrong lengths or -- * invalid creds are discarded silently. -+ * Get message from skb. Each message is processed by iscsi_if_recv_msg. -+ * Malformed skbs with wrong lengths or invalid creds are not processed. - */ - static void --iscsi_if_rx(struct sock *sk, int len) -+iscsi_if_rx(struct sk_buff *skb) - { -- struct sk_buff *skb; -- - mutex_lock(&rx_queue_mutex); -- while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { -- if (NETLINK_CREDS(skb)->uid) { -- skb_pull(skb, skb->len); -- goto free_skb; -+ while (skb->len >= NLMSG_SPACE(0)) { -+ int err; -+ uint32_t rlen; -+ struct nlmsghdr *nlh; -+ struct iscsi_uevent *ev; -+ -+ nlh = nlmsg_hdr(skb); -+ if (nlh->nlmsg_len < sizeof(*nlh) || -+ skb->len < nlh->nlmsg_len) { -+ break; - } - -- while (skb->len >= NLMSG_SPACE(0)) { -- int err; -- uint32_t rlen; -- struct nlmsghdr *nlh; -- struct iscsi_uevent *ev; -+ ev = NLMSG_DATA(nlh); -+ rlen = NLMSG_ALIGN(nlh->nlmsg_len); -+ if (rlen > skb->len) -+ rlen = skb->len; - -- nlh = (struct nlmsghdr *)skb->data; -- if (nlh->nlmsg_len < sizeof(*nlh) || -- skb->len < nlh->nlmsg_len) { -- break; -- } -- -- ev = NLMSG_DATA(nlh); -- rlen = NLMSG_ALIGN(nlh->nlmsg_len); -- if (rlen > skb->len) -- rlen = skb->len; -- -- err = iscsi_if_recv_msg(skb, nlh); -- if (err) { -- ev->type = ISCSI_KEVENT_IF_ERROR; -- ev->iferror = err; -- } -- do { -- /* -- * special case for GET_STATS: -- * on success - sending reply and stats from -- * inside of if_recv_msg(), -- * on error - fall through. -- */ -- if (ev->type == ISCSI_UEVENT_GET_STATS && !err) -- break; -- err = iscsi_if_send_reply( -- NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, -- nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); -- } while (err < 0 && err != -ECONNREFUSED); -- skb_pull(skb, rlen); -+ err = iscsi_if_recv_msg(skb, nlh); -+ if (err) { -+ ev->type = ISCSI_KEVENT_IF_ERROR; -+ ev->iferror = err; - } --free_skb: -- kfree_skb(skb); -+ do { -+ /* -+ * special case for GET_STATS: -+ * on success - sending reply and stats from -+ * inside of if_recv_msg(), -+ * on error - fall through. -+ */ -+ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) -+ break; -+ err = iscsi_if_send_reply( -+ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, -+ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); -+ } while (err < 0 && err != -ECONNREFUSED); -+ skb_pull(skb, rlen); - } - mutex_unlock(&rx_queue_mutex); - } -@@ -1191,6 +1372,8 @@ iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT); - iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN); - iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS); - iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS); -+iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO); -+iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO); - - #define iscsi_cdev_to_session(_cdev) \ - iscsi_dev_to_session(_cdev->dev) -@@ -1229,6 +1412,19 @@ iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1); - iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1); - iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1); - iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1); -+iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); -+iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); -+iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); -+iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); -+ -+static ssize_t -+show_priv_session_state(struct class_device *cdev, char *buf) -+{ -+ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); -+ return sprintf(buf, "%s\n", iscsi_session_state_name(session->state)); -+} -+static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, -+ NULL); - - #define iscsi_priv_session_attr_show(field, format) \ - static ssize_t \ -@@ -1425,6 +1621,8 @@ iscsi_register_transport(struct iscsi_transport *tt) - SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN); - SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS); - SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT); -+ SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO); -+ SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO); - - BUG_ON(count > ISCSI_CONN_ATTRS); - priv->conn_attrs[count] = NULL; -@@ -1450,7 +1648,12 @@ iscsi_register_transport(struct iscsi_transport *tt) - SETUP_SESSION_RD_ATTR(password_in, ISCSI_USERNAME_IN); - SETUP_SESSION_RD_ATTR(username, ISCSI_PASSWORD); - SETUP_SESSION_RD_ATTR(username_in, ISCSI_PASSWORD_IN); -+ SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT); -+ SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO); -+ SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO); -+ SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME); - SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); -+ SETUP_PRIV_SESSION_RD_ATTR(state); - - BUG_ON(count > ISCSI_SESSION_ATTRS); - priv->session_attrs[count] = NULL; -@@ -1470,6 +1673,32 @@ free_priv: - } - EXPORT_SYMBOL_GPL(iscsi_register_transport); - -+void iscsi_trans_error(struct iscsi_transport *tt) -+{ -+ struct nlmsghdr *nlh; -+ struct sk_buff *skb; -+ struct iscsi_uevent *ev; -+ int len = NLMSG_SPACE(sizeof(*ev)); -+ struct iscsi_internal *priv; -+ -+ priv = iscsi_if_transport_lookup(tt); -+ if (!priv) -+ return; -+ -+ skb = alloc_skb(len, GFP_KERNEL); -+ if (!skb) { -+ printk(KERN_ERR "iscsi: gracefully ignored transport error\n"); -+ return; -+ } -+ -+ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); -+ ev = NLMSG_DATA(nlh); -+ ev->transport_handle = iscsi_handle(tt); -+ ev->type = ISCSI_KEVENT_TRANS_ERROR; -+ -+ iscsi_broadcast_skb(skb, GFP_KERNEL); -+} -+ - int iscsi_unregister_transport(struct iscsi_transport *tt) - { - struct iscsi_internal *priv; -@@ -1479,6 +1708,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) - - mutex_lock(&rx_queue_mutex); - -+ iscsi_trans_error(tt); -+ - priv = iscsi_if_transport_lookup(tt); - BUG_ON (!priv); - -@@ -1523,15 +1754,21 @@ static __init int iscsi_transport_init(void) - if (err) - goto unregister_conn_class; - -- nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, -+ nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL, - THIS_MODULE); - if (!nls) { - err = -ENOBUFS; - goto unregister_session_class; - } - -+ iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh"); -+ if (!iscsi_eh_timer_workq) -+ goto release_nls; -+ - return 0; - -+release_nls: -+ netlink_kernel_release(nls); - unregister_session_class: - transport_class_unregister(&iscsi_session_class); - unregister_conn_class: -@@ -1545,7 +1782,8 @@ unregister_transport_class: - - static void __exit iscsi_transport_exit(void) - { -- sock_release(nls->sk_socket); -+ destroy_workqueue(iscsi_eh_timer_workq); -+ netlink_kernel_release(nls); - transport_class_unregister(&iscsi_connection_class); - transport_class_unregister(&iscsi_session_class); - transport_class_unregister(&iscsi_host_class); -diff --git a/kernel/scsi_transport_iscsi.h b/kernel/scsi_transport_iscsi.h -index 05e2554..3492abe 100644 ---- a/kernel/scsi_transport_iscsi.h -+++ b/kernel/scsi_transport_iscsi.h -@@ -24,6 +24,8 @@ - #define SCSI_TRANSPORT_ISCSI_H - - #include -+#include -+#include - #include "iscsi_if.h" - - struct scsi_transport_template; -@@ -116,7 +118,7 @@ struct iscsi_transport { - char *data, uint32_t data_size); - void (*get_stats) (struct iscsi_cls_conn *conn, - struct iscsi_stats *stats); -- void (*init_cmd_task) (struct iscsi_cmd_task *ctask); -+ int (*init_cmd_task) (struct iscsi_cmd_task *ctask); - void (*init_mgmt_task) (struct iscsi_conn *conn, - struct iscsi_mgmt_task *mtask); - int (*xmit_cmd_task) (struct iscsi_conn *conn, -@@ -147,13 +149,6 @@ extern void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error); - extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, - char *data, uint32_t data_size); - -- --/* Connection's states */ --#define ISCSI_CONN_INITIAL_STAGE 0 --#define ISCSI_CONN_STARTED 1 --#define ISCSI_CONN_STOPPED 2 --#define ISCSI_CONN_CLEANUP_WAIT 3 -- - struct iscsi_cls_conn { - struct list_head conn_list; /* item in connlist */ - void *dd_data; /* LLD private data */ -@@ -167,18 +162,25 @@ struct iscsi_cls_conn { - #define iscsi_dev_to_conn(_dev) \ - container_of(_dev, struct iscsi_cls_conn, dev) - --/* Session's states */ --#define ISCSI_STATE_FREE 1 --#define ISCSI_STATE_LOGGED_IN 2 --#define ISCSI_STATE_FAILED 3 --#define ISCSI_STATE_TERMINATE 4 --#define ISCSI_STATE_IN_RECOVERY 5 --#define ISCSI_STATE_RECOVERY_FAILED 6 -+#define iscsi_conn_to_session(_conn) \ -+ iscsi_dev_to_session(_conn->dev.parent) -+ -+/* iscsi class session state */ -+enum { -+ ISCSI_SESSION_LOGGED_IN, -+ ISCSI_SESSION_FAILED, -+ ISCSI_SESSION_FREE, -+}; - - struct iscsi_cls_session { - struct list_head sess_list; /* item in session_list */ - struct list_head host_list; - struct iscsi_transport *transport; -+ spinlock_t lock; -+ struct work_struct block_work; -+ struct work_struct unblock_work; -+ struct work_struct scan_work; -+ struct work_struct unbind_work; - - /* recovery fields */ - int recovery_tmo; -@@ -186,6 +188,7 @@ struct iscsi_cls_session { - - int target_id; - -+ int state; - int sid; /* session id */ - void *dd_data; /* LLD private data */ - struct device dev; /* sysfs transport/container device */ -@@ -202,18 +205,28 @@ struct iscsi_cls_session { - - struct iscsi_host { - struct list_head sessions; -+ atomic_t nr_scans; - struct mutex mutex; -+ struct workqueue_struct *scan_workq; -+ char scan_workq_name[KOBJ_NAME_LEN]; - }; - - /* - * session and connection functions that can be used by HW iSCSI LLDs - */ -+#define iscsi_cls_session_printk(prefix, _cls_session, fmt, a...) \ -+ dev_printk(prefix, &(_cls_session)->dev, fmt, ##a) -+ -+#define iscsi_cls_conn_printk(prefix, _cls_conn, fmt, a...) \ -+ dev_printk(prefix, &(_cls_conn)->dev, fmt, ##a) -+ -+extern int iscsi_session_chkready(struct iscsi_cls_session *session); - extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost, - struct iscsi_transport *transport); - extern int iscsi_add_session(struct iscsi_cls_session *session, - unsigned int target_id); --extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn); --extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn); -+extern int iscsi_session_event(struct iscsi_cls_session *session, -+ enum iscsi_uevent_e event); - extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, - struct iscsi_transport *t, - unsigned int target_id); -@@ -225,6 +238,6 @@ extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess, - extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn); - extern void iscsi_unblock_session(struct iscsi_cls_session *session); - extern void iscsi_block_session(struct iscsi_cls_session *session); -- -+extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time); - - #endif -diff --git a/test/regression.sh b/test/regression.sh -index 5b535f7..25d4a28 100755 ---- a/test/regression.sh -+++ b/test/regression.sh -@@ -133,7 +133,7 @@ function bonnie_run() { - - function fatal() { - echo "regression.sh: $1" -- echo "Usage: regression.sh [test#[:#]] [bsize]" -+ echo "Usage: regression.sh [-f | ] [test#[:#]] [bsize]" - exit 1 - } - -@@ -147,18 +147,19 @@ test ! -e ${datfile} && fatal "can not find regression.dat" - test ! -e ${disktest} && fatal "can not find disktest" - test ! -e ${iscsiadm} && fatal "can not find iscsiadm" - test ! -e ${bonnie} && fatal "can not find bonnie++" --test x$1 = x && fatal "target name parameter error" --test x$2 = x && fatal "ipnumber parameter error" --test x$3 = x && fatal "SCSI device parameter error" - - if test x$1 = "x-f" -o x$1 = "x--format"; then -- mkfs_run -- exit --fi -+ test x$2 = x && fatal "SCSI device parameter error" -+ device=$2 -+else -+ test x$1 = x && fatal "target name parameter error" -+ test x$2 = x && fatal "ipnumber parameter error" -+ test x$3 = x && fatal "SCSI device parameter error" - --target="$1" --ipnr="$2" --device=$3 -+ target="$1" -+ ipnr="$2" -+ device=$3 -+fi - - device_dir="$(dirname ${device})" - device_partition='' -@@ -172,6 +173,11 @@ case "${device_dir}" in - /dev/iscsi/*) device_partition="${device}-part1" ;; - esac - -+if test x$1 = "x-f" -o x$1 = "x--format"; then -+ mkfs_run -+ exit -+fi -+ - if [ -z "${device_partition}" ]; then - echo 'Unable to find device name for first partition.' >&2 - exit 1 -diff --git a/usr/Makefile b/usr/Makefile -index db33ed1..b3e0e72 100644 ---- a/usr/Makefile -+++ b/usr/Makefile -@@ -34,23 +34,30 @@ CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../include -D$(OSNAME) $(IPC_CFLAGS) - PROGRAMS = iscsid iscsiadm iscsistart - - # sources shared between iscsid, iscsiadm and iscsistart --ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iscsi_sysfs.o idbm.o -+ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iface.o idbm.o iscsi_sysfs.o - # sources shared between iscsid and iscsiadm - COMMON_SRCS = $(ISCSI_LIB_SRCS) - # core initiator files --INITIATOR_SRCS = initiator.o queue.o actor.o mgmt_ipc.o isns.o transport.o -+INITIATOR_SRCS = initiator.o scsi.o actor.o mgmt_ipc.o isns.o transport.o -+# fw boot files -+FW_BOOT_SRCS = $(wildcard ../utils/fwparam_ibft/*.o) - - all: $(PROGRAMS) - - iscsid: $(COMMON_SRCS) $(IPC_OBJ) $(INITIATOR_SRCS) iscsid.o - $(CC) $(CFLAGS) $^ -o $@ - --iscsiadm: $(COMMON_SRCS) strings.o discovery.o iscsiadm.o -+iscsiadm: $(COMMON_SRCS) $(FW_BOOT_SRCS) strings.o discovery.o iscsiadm.o - $(CC) $(CFLAGS) $^ -o $@ - --iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) iscsistart.o \ -- statics.o -+iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \ -+ iscsistart.o statics.o - $(CC) $(CFLAGS) -static $^ -o $@ - - clean: -- rm -f *.o $(PROGRAMS) -+ rm -f *.o $(PROGRAMS) .depend -+ -+depend: -+ gcc $(CFLAGS) -M `ls *.c` > .depend -+ -+-include .depend -diff --git a/usr/actor.c b/usr/actor.c -index 90b12e2..b487632 100644 ---- a/usr/actor.c -+++ b/usr/actor.c -@@ -16,26 +16,25 @@ - * - * See the file COPYING included with this distribution for more details. - */ -- --#include - #include - #include "actor.h" - #include "log.h" -+#include "list.h" - --static struct qelem pend_list; --static struct qelem poll_list; --static struct qelem actor_list; --static volatile uint32_t previous_time; -+static LIST_HEAD(pend_list); -+static LIST_HEAD(poll_list); -+static LIST_HEAD(actor_list); -+static volatile uint64_t previous_time; - static volatile uint32_t scheduler_loops; - static volatile int poll_in_progress; - static volatile uint64_t actor_jiffies = 0; - - #define actor_diff(_time1, _time2) ({ \ -- uint32_t __ret; \ -+ uint64_t __ret; \ - if ((_time2) >= (_time1)) \ - __ret = (_time2) - (_time1); \ - else \ -- __ret = (0xffffffff - (_time1)) + (_time2); \ -+ __ret = ((~0ULL) - (_time1)) + (_time2); \ - __ret; \ - }) - -@@ -61,19 +60,12 @@ actor_init(void) - poll_in_progress = 0; - previous_time = 0; - scheduler_loops = 0; -- pend_list.q_forw = &pend_list; -- pend_list.q_back = &pend_list; -- actor_list.q_forw = &actor_list; -- actor_list.q_back = &actor_list; -- poll_list.q_forw = &poll_list; -- poll_list.q_back = &poll_list; - } - - void - actor_new(actor_t *thread, void (*callback)(void *), void *data) - { -- thread->item.q_forw = &thread->item; -- thread->item.q_back = &thread->item; -+ INIT_LIST_HEAD(&thread->list); - thread->state = ACTOR_NOTSCHEDULED; - thread->callback = callback; - thread->data = data; -@@ -89,7 +81,7 @@ actor_delete(actor_t *thread) - case ACTOR_WAITING: - case ACTOR_POLL_WAITING: - log_debug(1, "deleting a scheduled/waiting thread!"); -- remque(&thread->item); -+ list_del_init(&thread->list); - break; - default: - break; -@@ -101,57 +93,67 @@ static void - actor_schedule_private(actor_t *thread, uint32_t ttschedule) - { - uint64_t delay_time, current_time; -- struct qelem *next_item; - actor_t *next_thread; - - delay_time = ACTOR_MS_TO_TICKS(ttschedule); - current_time = ACTOR_TICKS; - -- log_debug(7, "thread %08lx schedule: delay %" PRIu64 " state %d", -- (long)thread, delay_time, thread->state); -+ log_debug(7, "thread %p schedule: delay %" PRIu64 " state %d", -+ thread, delay_time, thread->state); - - /* convert ttscheduled msecs in 10s of msecs by dividing for now. - * later we will change param to 10s of msecs */ - switch(thread->state) { - case ACTOR_WAITING: - log_error("rescheduling a waiting thread!"); -+ list_del(&thread->list); - case ACTOR_NOTSCHEDULED: -+ INIT_LIST_HEAD(&thread->list); - /* if ttschedule is 0, put in scheduled queue and change - * state to scheduled, else add current time to ttschedule and - * insert in the queue at the correct point */ - if (delay_time == 0) { - if (poll_in_progress) { - thread->state = ACTOR_POLL_WAITING; -- insque(&thread->item, poll_list.q_back); -+ list_add_tail(&thread->list, &poll_list); - } else { - thread->state = ACTOR_SCHEDULED; -- insque(&thread->item, actor_list.q_back); -+ list_add_tail(&thread->list, &actor_list); - } -- } -- else { -+ } else { - thread->state = ACTOR_WAITING; - thread->ttschedule = delay_time; - thread->scheduled_at = current_time; - - /* insert new entry in sort order */ -- next_item = pend_list.q_forw; -- while (next_item != &pend_list) { -- next_thread = (actor_t *)next_item; -+ list_for_each_entry(next_thread, &pend_list, list) { -+ log_debug(7, "thread %p %" PRIu64 " %"PRIu64, -+ next_thread, -+ next_thread->scheduled_at + -+ next_thread->ttschedule, -+ current_time + delay_time); - - if (time_after(next_thread->scheduled_at + - next_thread->ttschedule, -- current_time + delay_time)) -- break; -- next_item = next_item->q_forw; -+ current_time + delay_time)) { -+ list_add(&thread->list, -+ &next_thread->list); -+ goto done; -+ } - } - -- insque(&thread->item, next_item->q_back); -+ list_add_tail(&thread->list, &pend_list); - } -+done: - break; - case ACTOR_POLL_WAITING: - case ACTOR_SCHEDULED: - // don't do anything - break; -+ case ACTOR_INVALID: -+ log_error("BUG: Trying to schedule a thread that has not been " -+ "setup. Ignoring sched."); -+ break; - } - - } -@@ -174,7 +176,7 @@ int - actor_timer_mod(actor_t *thread, uint32_t timeout, void *data) - { - if (thread->state == ACTOR_WAITING) { -- remque(&thread->item); -+ list_del_init(&thread->list); - thread->data = data; - actor_schedule_private(thread, timeout); - return 1; -@@ -185,9 +187,9 @@ actor_timer_mod(actor_t *thread, uint32_t timeout, void *data) - void - actor_check(uint64_t current_time) - { -- while (pend_list.q_forw != &pend_list) { -- actor_t *thread = (actor_t *)pend_list.q_forw; -+ struct actor *thread, *tmp; - -+ list_for_each_entry_safe(thread, tmp, &pend_list, list) { - if (actor_diff_time(thread, current_time)) { - log_debug(7, "thread %08lx wait some more", - (long)thread); -@@ -196,22 +198,22 @@ actor_check(uint64_t current_time) - } - - /* it is time to schedule this entry */ -- remque(&thread->item); -+ list_del_init(&thread->list); - - log_debug(2, "thread %08lx was scheduled at %" PRIu64 ":" - "%" PRIu64 ", curtime %" PRIu64 " q_forw %p " - "&pend_list %p", - (long)thread, thread->scheduled_at, thread->ttschedule, -- current_time, pend_list.q_forw, &pend_list); -+ current_time, pend_list.next, &pend_list); - - if (poll_in_progress) { - thread->state = ACTOR_POLL_WAITING; -- insque(&thread->item, poll_list.q_back); -+ list_add_tail(&thread->list, &poll_list); - log_debug(7, "thread %08lx now in poll_list", - (long)thread); - } else { - thread->state = ACTOR_SCHEDULED; -- insque(&thread->item, actor_list.q_back); -+ list_add_tail(&thread->list, &actor_list); - log_debug(7, "thread %08lx now in actor_list", - (long)thread); - } -@@ -222,6 +224,7 @@ void - actor_poll(void) - { - uint64_t current_time; -+ struct actor *thread; - - /* check that there are no any concurrency */ - if (poll_in_progress) { -@@ -245,11 +248,14 @@ actor_poll(void) - - /* the following code to check in the main data path */ - poll_in_progress = 1; -- while (actor_list.q_forw != &actor_list) { -- actor_t *thread = (actor_t *)actor_list.q_forw; -+ while (!list_empty(&actor_list)) { -+ thread = list_entry(actor_list.next, struct actor, list); -+ list_del_init(&thread->list); -+ - if (thread->state != ACTOR_SCHEDULED) -- log_debug(1, "actor_list: thread state corrupted!"); -- remque(&thread->item); -+ log_error("actor_list: thread state corrupted! " -+ "Thread with state %d in actor list.", -+ thread->state); - thread->state = ACTOR_NOTSCHEDULED; - log_debug(7, "exec thread %08lx callback", (long)thread); - thread->callback(thread->data); -@@ -257,13 +263,16 @@ actor_poll(void) - } - poll_in_progress = 0; - -- while (poll_list.q_forw != &poll_list) { -- actor_t *thread = (actor_t *)poll_list.q_forw; -+ while (!list_empty(&poll_list)) { -+ thread = list_entry(poll_list.next, struct actor, list); -+ list_del_init(&thread->list); -+ - if (thread->state != ACTOR_POLL_WAITING) -- log_debug(1, "poll_list: thread state corrupted!"); -- remque(&thread->item); -+ log_error("poll_list: thread state corrupted!" -+ "Thread with state %d in poll list.", -+ thread->state); - thread->state = ACTOR_SCHEDULED; -- insque(&thread->item, actor_list.q_back); -+ list_add_tail(&thread->list, &actor_list); - log_debug(7, "thread %08lx removed from poll_list", - (long)thread); - } -diff --git a/usr/actor.h b/usr/actor.h -index 88c69d6..7a71d42 100644 ---- a/usr/actor.h -+++ b/usr/actor.h -@@ -20,11 +20,13 @@ - #define ACTOR_H - - #include "types.h" -+#include "list.h" - - #define ACTOR_RESOLUTION 250 /* in millis */ - #define ACTOR_MAX_LOOPS 1 - - typedef enum actor_state_e { -+ ACTOR_INVALID, - ACTOR_WAITING, - ACTOR_SCHEDULED, - ACTOR_NOTSCHEDULED, -@@ -32,12 +34,12 @@ typedef enum actor_state_e { - } actor_state_e; - - typedef struct actor { -- struct qelem item; /* must be first element in the struct */ -- actor_state_e state; -- void *data; -- void (*callback)(void * ); -- uint64_t scheduled_at; -- uint64_t ttschedule; -+ struct list_head list; -+ actor_state_e state; -+ void *data; -+ void (*callback)(void * ); -+ uint64_t scheduled_at; -+ uint64_t ttschedule; - } actor_t; - - extern void actor_new(actor_t *thread, void (*callback)(void *), void * data); -diff --git a/usr/config.h b/usr/config.h -index 493be42..e767d6d 100644 ---- a/usr/config.h -+++ b/usr/config.h -@@ -66,8 +66,6 @@ struct iscsi_connection_timeout_config { - int logout_timeout; - int auth_timeout; - int active_timeout; -- int idle_timeout; -- int ping_timeout; - int noop_out_interval; - int noop_out_timeout; - }; -@@ -86,7 +84,8 @@ struct iscsi_session_timeout_config { - */ - struct iscsi_error_timeout_config { - int abort_timeout; -- int reset_timeout; -+ int host_reset_timeout; -+ int lu_reset_timeout; - }; - - /* all TCP options go in this structure. -@@ -124,6 +123,7 @@ struct iscsi_session_operational_config { - int MaxConnections; - int MaxOutstandingR2T; - int ERL; -+ int FastAbort; - }; - - #define CONFIG_DIGEST_NEVER 0 -@@ -158,6 +158,7 @@ typedef enum discovery_type { - DISCOVERY_TYPE_SLP, - DISCOVERY_TYPE_ISNS, - DISCOVERY_TYPE_STATIC, -+ DISCOVERY_TYPE_FWBOOT, - } discovery_type_e; - - typedef struct conn_rec { -@@ -196,6 +197,12 @@ typedef struct iface_rec { - */ - char hwaddress[ISCSI_MAX_IFACE_LEN]; - char transport_name[ISCSI_TRANSPORT_NAME_MAXLEN]; -+ /* -+ * This is only used for boot now, but the iser guys -+ * can use this for their virtualization idea. -+ */ -+ char alias[TARGET_NAME_MAXLEN + 1]; -+ char iname[TARGET_NAME_MAXLEN + 1]; - } iface_rec_t; - - typedef struct node_rec { -diff --git a/usr/discovery.c b/usr/discovery.c -index f1decef..dab6249 100644 ---- a/usr/discovery.c -+++ b/usr/discovery.c -@@ -50,7 +50,10 @@ - - static int rediscover = 0; - --int discovery_offload_sendtargets(idbm_t *db, int host_no, int do_login, -+static char initiator_name[TARGET_NAME_MAXLEN]; -+static char initiator_alias[TARGET_NAME_MAXLEN]; -+ -+int discovery_offload_sendtargets(int host_no, int do_login, - discovery_rec_t *drec) - { - struct sockaddr_storage ss; -@@ -180,12 +183,12 @@ iterate_targets(iscsi_session_t *session, uint32_t ttt) - return 1; - } - --static int add_portal(idbm_t *db, discovery_rec_t *drec, -- struct list_head *ifaces, node_rec_t *rec, -- char *address, char *port, char *tag) -+static int add_portal(struct list_head *rec_list, discovery_rec_t *drec, -+ char *targetname, char *address, char *port, char *tag) - { - struct sockaddr_storage ss; - char host[NI_MAXHOST]; -+ struct node_rec *rec; - - /* resolve the address, in case it was a DNS name */ - if (resolve_address(address, port, &ss)) { -@@ -197,6 +200,16 @@ static int add_portal(idbm_t *db, discovery_rec_t *drec, - getnameinfo((struct sockaddr *) &ss, sizeof(ss), - host, sizeof(host), NULL, 0, NI_NUMERICHOST); - -+ rec = calloc(1, sizeof(*rec)); -+ if (!rec) -+ return 0; -+ -+ idbm_node_setup_from_conf(rec); -+ rec->disc_type = drec->type; -+ rec->disc_port = drec->port; -+ strcpy(rec->disc_address, drec->address); -+ -+ strncpy(rec->name, targetname, TARGET_NAME_MAXLEN); - if (tag && *tag) - rec->tpgt = atoi(tag); - else -@@ -207,21 +220,17 @@ static int add_portal(idbm_t *db, discovery_rec_t *drec, - rec->conn[0].port = ISCSI_LISTEN_PORT; - strncpy(rec->conn[0].address, address, NI_MAXHOST); - -- if (idbm_add_nodes(db, rec, drec, ifaces)) -- log_error("Could not add record for %s %s,%d,%d\n", -- rec->name, address, rec->conn[0].port, rec->tpgt); -+ list_add_tail(&rec->list, rec_list); - return 1; - } - - static int --add_target_record(idbm_t *db, char *name, char *end, -- discovery_rec_t *drec, struct list_head *ifaces, -- char *default_port) -+add_target_record(char *name, char *end, discovery_rec_t *drec, -+ struct list_head *rec_list, char *default_port) - { - char *text = NULL; - char *nul = name; - size_t length; -- node_rec_t rec; - - /* address = IPv4 - * address = [IPv6] -@@ -248,10 +257,6 @@ add_target_record(idbm_t *db, char *name, char *end, - log_error("TargetName %s too long, ignoring", name); - return 0; - } -- -- idbm_node_setup_from_conf(db, &rec); -- strncpy(rec.name, name, TARGET_NAME_MAXLEN); -- - text = name + length; - - /* skip NULs after the name */ -@@ -264,7 +269,7 @@ add_target_record(idbm_t *db, char *name, char *end, - log_error("no default address known for target %s", - name); - return 0; -- } else if (!add_portal(db, drec, ifaces, &rec, drec->address, -+ } else if (!add_portal(rec_list, drec, name, drec->address, - default_port, NULL)) { - log_error("failed to add default portal, ignoring " - "target %s", name); -@@ -302,7 +307,7 @@ add_target_record(idbm_t *db, char *name, char *end, - *temp = '\0'; - } - -- if (!add_portal(db, drec, ifaces, &rec, address, port, -+ if (!add_portal(rec_list, drec, name, address, port, - tag)) { - log_error("failed to add default portal, " - "ignoring target %s", name); -@@ -318,9 +323,9 @@ add_target_record(idbm_t *db, char *name, char *end, - } - - static int --process_sendtargets_response(idbm_t *db, struct string_buffer *sendtargets, -+process_sendtargets_response(struct string_buffer *sendtargets, - int final, discovery_rec_t *drec, -- struct list_head *ifaces, -+ struct list_head *rec_list, - char *default_port) - { - char *start = buffer_data(sendtargets); -@@ -371,8 +376,8 @@ process_sendtargets_response(idbm_t *db, struct string_buffer *sendtargets, - * the end of. don't bother passing the - * "TargetName=" prefix. - */ -- if (!add_target_record(db, record + 11, text, -- drec, ifaces, -+ if (!add_target_record(record + 11, text, -+ drec, rec_list, - default_port)) { - log_error( - "failed to add target record"); -@@ -400,8 +405,8 @@ process_sendtargets_response(idbm_t *db, struct string_buffer *sendtargets, - "processing final sendtargets record %p, " - "line %s", - record, record); -- if (add_target_record (db, record + 11, text, -- drec, ifaces, default_port)) { -+ if (add_target_record (record + 11, text, -+ drec, rec_list, default_port)) { - num_targets++; - record = NULL; - truncate_buffer(sendtargets, 0); -@@ -502,36 +507,38 @@ msecs_until(struct timeval *timer) - return msecs; - } - --static int --soonest_msecs(struct timeval *t1, struct timeval *t2, struct timeval *t3) -+static int request_initiator_name(void) - { -- int m1 = msecs_until(t1); -- int m2 = msecs_until(t2); -- int m3 = msecs_until(t3); -+ int rc; -+ iscsiadm_req_t req; -+ iscsiadm_rsp_t rsp; - -- /* infinity is -1, handle it specically */ -- if ((m1 == -1) && (m2 == -1)) -- return m3; -+ memset(initiator_name, 0, TARGET_NAME_MAXLEN); -+ initiator_name[0] = '\0'; -+ memset(initiator_alias, 0, TARGET_NAME_MAXLEN); -+ initiator_alias[0] = '\0'; - -- if ((m1 == -1) && (m3 == -1)) -- return m2; -+ memset(&req, 0, sizeof(req)); -+ req.command = MGMT_IPC_CONFIG_INAME; - -- if ((m2 == -1) && (m3 == -1)) -- return m1; -+ rc = do_iscsid(&req, &rsp); -+ if (rc) -+ return EIO; - -- if (m1 == -1) -- return (m2 < m3) ? m2 : m3; -+ if (rsp.u.config.var[0] != '\0') -+ strcpy(initiator_name, rsp.u.config.var); - -- if (m2 == -1) -- return (m1 < m3) ? m1 : m3; -+ memset(&req, 0, sizeof(req)); -+ req.command = MGMT_IPC_CONFIG_IALIAS; - -- if (m3 == -1) -- return (m1 < m2) ? m1 : m2; -+ rc = do_iscsid(&req, &rsp); -+ if (rc) -+ /* alias is optional so return ok */ -+ return 0; - -- if (m1 < m2) -- return (m1 < m3) ? m1 : m3; -- else -- return (m2 < m3) ? m2 : m3; -+ if (rsp.u.config.var[0] != '\0') -+ strcpy(initiator_alias, rsp.u.config.var); -+ return 0; - } - - static iscsi_session_t * -@@ -548,7 +555,6 @@ init_new_session(struct iscsi_sendtargets_config *config) - session->conn[0].login_timeout = config->conn_timeo.login_timeout; - session->conn[0].auth_timeout = config->conn_timeo.auth_timeout; - session->conn[0].active_timeout = config->conn_timeo.active_timeout; -- session->conn[0].idle_timeout = config->conn_timeo.idle_timeout; - session->conn[0].hdrdgst_en = ISCSI_DIGEST_NONE; - session->conn[0].datadgst_en = ISCSI_DIGEST_NONE; - -@@ -577,6 +583,12 @@ init_new_session(struct iscsi_sendtargets_config *config) - session->isid[5] = 0; - - /* initialize the session */ -+ if (request_initiator_name() ||initiator_name[0] == '\0') { -+ log_error("Cannot perform discovery. Initiatorname required."); -+ free(session); -+ return NULL; -+ } -+ - session->initiator_name = initiator_name; - session->initiator_alias = initiator_alias; - session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN; -@@ -672,15 +684,14 @@ setup_authentication(iscsi_session_t *session, - } - - static int --process_recvd_pdu(idbm_t *db, struct iscsi_hdr *pdu, -+process_recvd_pdu(struct iscsi_hdr *pdu, - discovery_rec_t *drec, -- struct list_head *ifaces, -+ struct list_head *rec_list, - iscsi_session_t *session, - struct string_buffer *sendtargets, - char *default_port, - int *active, - int *valid_text, -- struct timeval *async_timer, - char *data) - { - int rc=0; -@@ -693,6 +704,7 @@ process_recvd_pdu(idbm_t *db, struct iscsi_hdr *pdu, - int final = - (text_response->flags & ISCSI_FLAG_CMD_FINAL) || - (text_response-> ttt == ISCSI_RESERVED_TAG); -+ size_t curr_data_length; - - log_debug(4, "discovery session to %s:%d received text" - " response, %d data bytes, ttt 0x%x, " -@@ -706,24 +718,17 @@ process_recvd_pdu(idbm_t *db, struct iscsi_hdr *pdu, - /* mark how much more data in the sendtargets - * buffer is now valid - */ -- enlarge_data (sendtargets, dlength); -+ curr_data_length = data_length(sendtargets); -+ enlarge_data(sendtargets, dlength); -+ memcpy(buffer_data(sendtargets) + curr_data_length, -+ data, dlength); - -- /* -- * we got a response so clear out the current -- * db values -- * -- * TODO: should we make whether rm the current -- * values configurable (maybe a --clear option) -- */ -- if (!*valid_text) -- idbm_new_discovery(db, drec); - *valid_text = 1; -- - /* process as much as we can right now */ -- process_sendtargets_response(db, sendtargets, -+ process_sendtargets_response(sendtargets, - final, - drec, -- ifaces, -+ rec_list, - default_port); - - if (final) { -@@ -814,22 +819,21 @@ done: - iscsi_io_disconnect(&session->conn[0]); - } - --int discovery_sendtargets(idbm_t *db, discovery_rec_t *drec, -- struct list_head *ifaces) -+int discovery_sendtargets(discovery_rec_t *drec, -+ struct list_head *rec_list) - { - iscsi_session_t *session; - struct pollfd pfd; - struct iscsi_hdr pdu_buffer; - struct iscsi_hdr *pdu = &pdu_buffer; - char *data = NULL; -- char *end_of_data; - int active = 0, valid_text = 0; -- struct timeval connection_timer, async_timer; -+ struct timeval connection_timer; - int timeout; - int rc; - struct string_buffer sendtargets; - uint8_t status_class = 0, status_detail = 0; -- unsigned int login_failures = 0; -+ unsigned int login_failures = 0, data_len; - int login_delay = 0; - struct sockaddr_storage ss; - char host[NI_MAXHOST], serv[NI_MAXSERV], default_port[NI_MAXSERV]; -@@ -840,13 +844,13 @@ int discovery_sendtargets(idbm_t *db, discovery_rec_t *drec, - drec->address, drec->port); - memset(&pdu_buffer, 0, sizeof (pdu_buffer)); - clear_timer(&connection_timer); -- clear_timer(&async_timer); - - /* allocate a new session, and initialize default values */ - session = init_new_session(config); - if (session == NULL) { -- log_error("discovery process to %s:%d failed to " -- "allocate a session", drec->address, drec->port); -+ log_error("Discovery process to %s:%d failed to " -+ "create a discovery session.", -+ drec->address, drec->port); - return 1; - } - -@@ -856,13 +860,15 @@ int discovery_sendtargets(idbm_t *db, discovery_rec_t *drec, - session->isid[1], session->isid[2], session->isid[3], - session->isid[4], session->isid[5]); - -- /* allocate data buffers for SendTargets data and discovery pipe info */ -- init_string_buffer(&sendtargets, -- session->conn[0].max_recv_dlength); -- if (!sendtargets.buffer) { -+ /* allocate data buffers for SendTargets data */ -+ data = malloc(session->conn[0].max_recv_dlength); -+ if (!data) { - rc = 1; - goto free_session; - } -+ data_len = session->conn[0].max_recv_dlength; -+ -+ init_string_buffer(&sendtargets, 0); - - sprintf(default_port, "%d", drec->port); - /* resolve the DiscoveryAddress to an IP address */ -@@ -872,11 +878,9 @@ int discovery_sendtargets(idbm_t *db, discovery_rec_t *drec, - goto free_sendtargets; - } - -- log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d, " -- "active %d, idle %d, ping %d", -+ log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d.", - session->conn[0].login_timeout, session->reopen_cnt, -- session->conn[0].auth_timeout, session->conn[0].active_timeout, -- session->conn[0].idle_timeout, session->conn[0].ping_timeout); -+ session->conn[0].auth_timeout); - - /* setup authentication variables for the session*/ - rc = setup_authentication(session, drec, config); -@@ -958,8 +962,9 @@ redirect_reconnect: - - status_class = 0; - status_detail = 0; -- rc = iscsi_login(session, 0, buffer_data(&sendtargets), -- unused_length(&sendtargets), -+ -+ memset(data, 0, data_len); -+ rc = iscsi_login(session, 0, data, data_len, - &status_class, &status_detail); - - switch (rc) { -@@ -968,7 +973,6 @@ redirect_reconnect: - break; - - case LOGIN_IO_ERROR: -- case LOGIN_WRONG_PORTAL_GROUP: - case LOGIN_REDIRECTION_FAILED: - /* try again */ - log_warning("retrying discovery login to %s", host); -@@ -1047,13 +1051,9 @@ redirect_reconnect: - goto reconnect; - } - -- rediscover: - /* reinitialize */ - truncate_buffer(&sendtargets, 0); - -- /* we're going to do a discovery regardless */ -- clear_timer(&async_timer); -- - /* ask for targets */ - if (!request_targets(session)) { - goto reconnect; -@@ -1068,40 +1068,8 @@ redirect_reconnect: - pfd.fd = session->conn[0].socket_fd; - pfd.events = POLLIN | POLLPRI; - -- /* check timers before blocking */ -- if (timer_expired(&connection_timer)) { -- log_warning("discovery session to %s:%d session " -- "logout, connection timer expired", -- drec->address, drec->port); -- iscsi_logout_and_disconnect(session); -- rc = 1; -- goto free_sendtargets; -- } -- -- if (active) { -- /* ignore the async timer, we're in the middle -- * of a discovery -- */ -- timeout = msecs_until(&connection_timer); -- } else { -- /* to avoid doing LUN probing repeatedly, try to merge -- * multiple Async PDUs into one rediscovery by -- * deferring discovery until a timeout expires. -- */ -- if (timer_expired(&async_timer)) { -- log_debug(4, -- "discovery session to %s:%d async " -- "timer expired, rediscovering", -- drec->address, drec->port); -- clear_timer(&async_timer); -- goto rediscover; -- } else -- timeout = -- soonest_msecs(NULL, -- &connection_timer, -- &async_timer); -- } -- -+repoll: -+ timeout = msecs_until(&connection_timer); - /* block until we receive a PDU, a TCP FIN, a TCP RST, - * or a timeout - */ -@@ -1118,50 +1086,24 @@ redirect_reconnect: - "discovery process to %s:%d returned from poll, rc %d", - drec->address, drec->port, rc); - -+ if (timer_expired(&connection_timer)) { -+ log_warning("discovery session to %s:%d session " -+ "logout, connection timer expired", -+ drec->address, drec->port); -+ iscsi_logout_and_disconnect(session); -+ rc = 1; -+ goto free_sendtargets; -+ } -+ - if (rc > 0) { - if (pfd.revents & (POLLIN | POLLPRI)) { -- /* put any PDU data into the -- * sendtargets buffer for now -- */ -- data = buffer_data(&sendtargets) + -- data_length(&sendtargets); -- end_of_data = -- data + unused_length(&sendtargets); - timeout = msecs_until(&connection_timer); - -- if (iscsi_io_recv_pdu(&session->conn[0], -- pdu, ISCSI_DIGEST_NONE, data, -- end_of_data - data, ISCSI_DIGEST_NONE, -- timeout)) { -- /* -- * process iSCSI PDU received -- */ -- rc = process_recvd_pdu( -- db, -- pdu, -- drec, -- ifaces, -- session, -- &sendtargets, -- default_port, -- &active, -- &valid_text, -- &async_timer, -- data); -- if (rc == DISCOVERY_NEED_RECONNECT) -- goto reconnect; -- -- /* reset timers after receiving a PDU */ -- if (active) -- set_timer(&connection_timer, -- session->conn[0].active_timeout); -- else -- /* -- * 3 minutes to try -- * to go long-lived -- */ -- set_timer(&connection_timer, 3 * 60); -- } else { -+ memset(data, 0, data_len); -+ if (!iscsi_io_recv_pdu(&session->conn[0], -+ pdu, ISCSI_DIGEST_NONE, data, -+ data_len, ISCSI_DIGEST_NONE, -+ timeout)) { - log_debug(1, "discovery session to " - "%s:%d failed to recv a PDU " - "response, terminating", -@@ -1171,7 +1113,25 @@ redirect_reconnect: - rc = 1; - goto free_sendtargets; - } -+ -+ /* -+ * process iSCSI PDU received -+ */ -+ rc = process_recvd_pdu(pdu, drec, rec_list, -+ session, &sendtargets, -+ default_port, -+ &active, &valid_text, data); -+ if (rc == DISCOVERY_NEED_RECONNECT) -+ goto reconnect; -+ -+ /* reset timers after receiving a PDU */ -+ if (active) { -+ set_timer(&connection_timer, -+ session->conn[0].active_timeout); -+ goto repoll; -+ } - } -+ - if (pfd.revents & POLLHUP) { - log_warning("discovery session to %s:%d " - "terminating after hangup", -@@ -1213,6 +1173,7 @@ redirect_reconnect: - - free_sendtargets: - free_string_buffer(&sendtargets); -+ free(data); - free_session: - free(session); - return rc; -diff --git a/usr/idbm.c b/usr/idbm.c -index dcdae56..389d904 100644 ---- a/usr/idbm.c -+++ b/usr/idbm.c -@@ -24,13 +24,11 @@ - #include - #include - #include --#include - #include --#include - #include -+#include - #include - #include --#include - - #include "idbm.h" - #include "log.h" -@@ -38,12 +36,15 @@ - #include "iscsi_settings.h" - #include "transport.h" - #include "iscsi_sysfs.h" -+#include "iface.h" - - #define IDBM_HIDE 0 /* Hide parameter when print. */ - #define IDBM_SHOW 1 /* Show parameter when print. */ - #define IDBM_MASKED 2 /* Show "stars" instead of real value when print */ - --#define __recinfo_str(_key, _info, _rec, _name, _show, _n) do { \ -+static struct idbm *db; -+ -+#define __recinfo_str(_key, _info, _rec, _name, _show, _n, _mod) do { \ - _info[_n].type = TYPE_STR; \ - strncpy(_info[_n].name, _key, NAME_MAXVAL); \ - if (strlen((char*)_rec->_name)) \ -@@ -52,20 +53,22 @@ - _info[_n].data = &_rec->_name; \ - _info[_n].data_len = sizeof(_rec->_name); \ - _info[_n].visible = _show; \ -+ _info[_n].can_modify = _mod; \ - _n++; \ - } while(0) - --#define __recinfo_int(_key, _info, _rec, _name, _show, _n) do { \ -+#define __recinfo_int(_key, _info, _rec, _name, _show, _n, _mod) do { \ - _info[_n].type = TYPE_INT; \ - strncpy(_info[_n].name, _key, NAME_MAXVAL); \ - snprintf(_info[_n].value, VALUE_MAXVAL, "%d", _rec->_name); \ - _info[_n].data = &_rec->_name; \ - _info[_n].data_len = sizeof(_rec->_name); \ - _info[_n].visible = _show; \ -+ _info[_n].can_modify = _mod; \ - _n++; \ - } while(0) - --#define __recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n) do { \ -+#define __recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n, _mod) do { \ - _info[_n].type = TYPE_INT_O; \ - strncpy(_info[_n].name, _key, NAME_MAXVAL); \ - if (_rec->_name == 0) strncpy(_info[_n].value, _op0, VALUE_MAXVAL); \ -@@ -76,37 +79,79 @@ - _info[_n].opts[0] = _op0; \ - _info[_n].opts[1] = _op1; \ - _info[_n].numopts = 2; \ -+ _info[_n].can_modify = _mod; \ - _n++; \ - } while(0) - --#define __recinfo_int_o3(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_n)do{ \ -- __recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n); _n--; \ -- if (_rec->_name == 2) strncpy(_info[_n].value, _op2, VALUE_MAXVAL); \ -+#define __recinfo_int_o3(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_n, \ -+ _mod) do { \ -+ __recinfo_int_o2(_key,_info,_rec,_name,_show,_op0,_op1,_n, _mod); \ -+ _n--; \ -+ if (_rec->_name == 2) strncpy(_info[_n].value, _op2, VALUE_MAXVAL);\ - _info[_n].opts[2] = _op2; \ - _info[_n].numopts = 3; \ - _n++; \ - } while(0) - --#define __recinfo_int_o4(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3,_n)do{\ -- __recinfo_int_o3(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_n); _n--; \ -+#define __recinfo_int_o4(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3,_n, \ -+ _mod) do { \ -+ __recinfo_int_o3(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_n, _mod); \ -+ _n--; \ - if (_rec->_name == 3) strncpy(_info[_n].value, _op3, VALUE_MAXVAL); \ - _info[_n].opts[3] = _op3; \ - _info[_n].numopts = 4; \ - _n++; \ - } while(0) - --#define __recinfo_int_o5(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3,_op4,_n)do{\ -- __recinfo_int_o4(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3,_n); _n--; \ -+#define __recinfo_int_o5(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3, \ -+ _op4,_n, _mod) do { \ -+ __recinfo_int_o4(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3, \ -+ _n,_mod); \ -+ _n--; \ - if (_rec->_name == 4) strncpy(_info[_n].value, _op4, VALUE_MAXVAL); \ - _info[_n].opts[4] = _op4; \ - _info[_n].numopts = 5; \ - _n++; \ - } while(0) - -+#define __recinfo_int_o6(_key,_info,_rec,_name,_show,_op0,_op1,_op2, \ -+ _op3,_op4,_op5,_n,_mod) do { \ -+ __recinfo_int_o5(_key,_info,_rec,_name,_show,_op0,_op1,_op2,_op3, \ -+ _op4,_n,_mod); \ -+ _n--; \ -+ if (_rec->_name == 5) strncpy(_info[_n].value, _op5, VALUE_MAXVAL); \ -+ _info[_n].opts[5] = _op5; \ -+ _info[_n].numopts = 6; \ -+ _n++; \ -+} while(0) -+ -+/* -+ * from linux kernel -+ */ -+static char *strstrip(char *s) -+{ -+ size_t size; -+ char *end; -+ -+ size = strlen(s); -+ if (!size) -+ return s; -+ -+ end = s + size - 1; -+ while (end >= s && isspace(*end)) -+ end--; -+ *(end + 1) = '\0'; -+ -+ while (*s && isspace(*s)) -+ s++; -+ -+ return s; -+} -+ - static char *get_global_string_param(char *pathname, const char *key) - { - FILE *f = NULL; -- int c, len; -+ int len; - char *line, buffer[1024]; - char *name = NULL; - -@@ -119,30 +164,21 @@ static char *get_global_string_param(char *pathname, const char *key) - if ((f = fopen(pathname, "r"))) { - while ((line = fgets(buffer, sizeof (buffer), f))) { - -- while (line && isspace(c = *line)) -- line++; -+ line = strstrip(line); - - if (strncmp(line, key, len) == 0) { - char *end = line + len; - -- /* the name is everything up to the first -- * bit of whitespace -+ /* -+ * make sure ther is something after the -+ * key. - */ -- while (*end && (!isspace(c = *end))) -- end++; -- -- if (isspace(c = *end)) -- *end = '\0'; -- -- if (end > line + len) -+ if (strlen(end)) - name = strdup(line + len); - } - } - fclose(f); -- if (!name) -- log_error("an %s is required, but was not found in %s", -- key, pathname); -- else -+ if (name) - log_debug(5, "%s=%s", key, name); - } else - log_error("can't open %s configuration file %s", key, pathname); -@@ -152,7 +188,13 @@ static char *get_global_string_param(char *pathname, const char *key) - - char *get_iscsi_initiatorname(char *pathname) - { -- return get_global_string_param(pathname, "InitiatorName="); -+ char *name; -+ -+ name = get_global_string_param(pathname, "InitiatorName="); -+ if (!name) -+ log_error("An InitiatorName= is required, but was not " -+ "found in %s", pathname); -+ return name; - } - - char *get_iscsi_initiatoralias(char *pathname) -@@ -166,50 +208,47 @@ idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri) - int num = 0; - - __recinfo_int_o2("discovery.startup", ri, r, startup, IDBM_SHOW, -- "manual", "automatic", num); -- __recinfo_int_o5("discovery.type", ri, r, type, IDBM_SHOW, -+ "manual", "automatic", num, 1); -+ __recinfo_int_o6("discovery.type", ri, r, type, IDBM_SHOW, - "sendtargets", "offload_send_targets", "slp", "isns", -- "static", num); -+ "static", "fw", num, 0); - if (r->type == DISCOVERY_TYPE_SENDTARGETS) { - __recinfo_str("discovery.sendtargets.address", ri, r, -- address, IDBM_SHOW, num); -+ address, IDBM_SHOW, num, 0); - __recinfo_int("discovery.sendtargets.port", ri, r, -- port, IDBM_SHOW, num); -+ port, IDBM_SHOW, num, 0); - __recinfo_int_o2("discovery.sendtargets.auth.authmethod", ri, r, - u.sendtargets.auth.authmethod, -- IDBM_SHOW, "None", "CHAP", num); -+ IDBM_SHOW, "None", "CHAP", num, 1); - __recinfo_str("discovery.sendtargets.auth.username", ri, r, -- u.sendtargets.auth.username, IDBM_SHOW, num); -+ u.sendtargets.auth.username, IDBM_SHOW, num, 1); - __recinfo_str("discovery.sendtargets.auth.password", ri, r, -- u.sendtargets.auth.password, IDBM_MASKED, num); -+ u.sendtargets.auth.password, IDBM_MASKED, num, 1); - __recinfo_int("discovery.sendtargets.auth.password_length", - ri, r, u.sendtargets.auth.password_length, -- IDBM_HIDE, num); -+ IDBM_HIDE, num, 1); - __recinfo_str("discovery.sendtargets.auth.username_in", ri, r, -- u.sendtargets.auth.username_in, IDBM_SHOW, num); -+ u.sendtargets.auth.username_in, IDBM_SHOW, num, 1); - __recinfo_str("discovery.sendtargets.auth.password_in", ri, r, -- u.sendtargets.auth.password_in, IDBM_MASKED, num); -+ u.sendtargets.auth.password_in, IDBM_MASKED, num, 1); - __recinfo_int("discovery.sendtargets.auth.password_in_length", - ri, r, u.sendtargets.auth.password_in_length, -- IDBM_HIDE, num); -+ IDBM_HIDE, num, 1); - __recinfo_int("discovery.sendtargets.timeo.login_timeout",ri, r, - u.sendtargets.conn_timeo.login_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - __recinfo_int("discovery.sendtargets.reopen_max",ri, r, - u.sendtargets.reopen_max, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - __recinfo_int("discovery.sendtargets.timeo.auth_timeout", ri, r, - u.sendtargets.conn_timeo.auth_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - __recinfo_int("discovery.sendtargets.timeo.active_timeout",ri,r, -- u.sendtargets.conn_timeo.active_timeout, -- IDBM_SHOW, num); -- __recinfo_int("discovery.sendtargets.timeo.idle_timeout", ri, r, -- u.sendtargets.conn_timeo.idle_timeout, -- IDBM_SHOW, num); -+ u.sendtargets.conn_timeo.active_timeout, -+ IDBM_SHOW, num, 1); - __recinfo_int("discovery.sendtargets.iscsi.MaxRecvDataSegmentLength", - ri, r, u.sendtargets.iscsi.MaxRecvDataSegmentLength, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - } - } - -@@ -218,159 +257,170 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) - { - int num = 0, i; - -- __recinfo_str("node.name", ri, r, name, IDBM_SHOW, num); -- __recinfo_int("node.tpgt", ri, r, tpgt, IDBM_SHOW, num); -+ __recinfo_str("node.name", ri, r, name, IDBM_SHOW, num, 0); -+ __recinfo_int("node.tpgt", ri, r, tpgt, IDBM_SHOW, num, 0); - __recinfo_int_o3("node.startup", ri, r, startup, -- IDBM_SHOW, "manual", "automatic", "onboot", num); -+ IDBM_SHOW, "manual", "automatic", "onboot", num, 1); -+ /* -+ * Note: because we do not add the iface.iscsi_ifacename to -+ * sysfs iscsiadm does some weird matching. We can change the iface -+ * values if a session is not running, but node record ifaces values -+ * have to be changed and so do the iface record ones. -+ * -+ * Users should nornmally not want to change the iface ones -+ * in the node record directly and instead do it through -+ * the iface mode which will do the right thing (althought that -+ * needs some locking). -+ */ - __recinfo_str("iface.hwaddress", ri, r, iface.hwaddress, IDBM_SHOW, -- num); -+ num, 1); - // __recinfo_str("iface.ipaddress", ri, r, iface.ipaddress, - // IDBM_SHOW, num); - __recinfo_str("iface.iscsi_ifacename", ri, r, iface.name, IDBM_SHOW, -- num); -+ num, 1); - __recinfo_str("iface.net_ifacename", ri, r, iface.netdev, IDBM_SHOW, -- num); -+ num, 1); - /* - * svn 780 compat: older versions used node.transport_name and - * rec->transport_name - */ - __recinfo_str("iface.transport_name", ri, r, iface.transport_name, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); -+ __recinfo_str("iface.initiatorname", ri, r, iface.iname, -+ IDBM_SHOW, num, 1); - __recinfo_str("node.discovery_address", ri, r, disc_address, IDBM_SHOW, -- num); -- __recinfo_int("node.discovery_port", ri, r, disc_port, IDBM_SHOW, num); -- __recinfo_int_o5("node.discovery_type", ri, r, disc_type, -+ num, 0); -+ __recinfo_int("node.discovery_port", ri, r, disc_port, IDBM_SHOW, -+ num, 0); -+ __recinfo_int_o6("node.discovery_type", ri, r, disc_type, - IDBM_SHOW, "send_targets", "offload_send_targets", -- "slp", "isns", "static", num); -+ "slp", "isns", "static", "fw", num, 0); - __recinfo_int("node.session.initial_cmdsn", ri, r, -- session.initial_cmdsn, IDBM_SHOW, num); -+ session.initial_cmdsn, IDBM_SHOW, num, 1); - __recinfo_int("node.session.initial_login_retry_max", ri, r, -- session.initial_login_retry_max, IDBM_SHOW, num); -+ session.initial_login_retry_max, IDBM_SHOW, num, 1); - __recinfo_int("node.session.cmds_max", ri, r, -- session.cmds_max, IDBM_SHOW, num); -+ session.cmds_max, IDBM_SHOW, num, 1); - __recinfo_int("node.session.queue_depth", ri, r, -- session.queue_depth, IDBM_SHOW, num); -+ session.queue_depth, IDBM_SHOW, num, 1); - __recinfo_int_o2("node.session.auth.authmethod", ri, r, -- session.auth.authmethod, IDBM_SHOW, "None", "CHAP", num); -+ session.auth.authmethod, IDBM_SHOW, "None", "CHAP", num, 1); - __recinfo_str("node.session.auth.username", ri, r, -- session.auth.username, IDBM_SHOW, num); -+ session.auth.username, IDBM_SHOW, num, 1); - __recinfo_str("node.session.auth.password", ri, r, -- session.auth.password, IDBM_MASKED, num); -+ session.auth.password, IDBM_MASKED, num, 1); - __recinfo_int("node.session.auth.password_length", ri, r, -- session.auth.password_length, IDBM_HIDE, num); -+ session.auth.password_length, IDBM_HIDE, num, 1); - __recinfo_str("node.session.auth.username_in", ri, r, -- session.auth.username_in, IDBM_SHOW, num); -+ session.auth.username_in, IDBM_SHOW, num, 1); - __recinfo_str("node.session.auth.password_in", ri, r, -- session.auth.password_in, IDBM_MASKED, num); -+ session.auth.password_in, IDBM_MASKED, num, 1); - __recinfo_int("node.session.auth.password_in_length", ri, r, -- session.auth.password_in_length, IDBM_HIDE, num); -+ session.auth.password_in_length, IDBM_HIDE, num, 1); - __recinfo_int("node.session.timeo.replacement_timeout", ri, r, - session.timeo.replacement_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - __recinfo_int("node.session.err_timeo.abort_timeout", ri, r, - session.err_timeo.abort_timeout, -- IDBM_SHOW, num); -- __recinfo_int("node.session.err_timeo.reset_timeout", ri, r, -- session.err_timeo.reset_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); -+ __recinfo_int("node.session.err_timeo.lu_reset_timeout", ri, r, -+ session.err_timeo.lu_reset_timeout, -+ IDBM_SHOW, num, 1); -+ __recinfo_int("node.session.err_timeo.host_reset_timeout", ri, r, -+ session.err_timeo.host_reset_timeout, -+ IDBM_SHOW, num, 1); -+ __recinfo_int_o2("node.session.iscsi.FastAbort", ri, r, -+ session.iscsi.FastAbort, IDBM_SHOW, "No", "Yes", -+ num, 1); - __recinfo_int_o2("node.session.iscsi.InitialR2T", ri, r, - session.iscsi.InitialR2T, IDBM_SHOW, -- "No", "Yes", num); -+ "No", "Yes", num, 1); - __recinfo_int_o2("node.session.iscsi.ImmediateData", - ri, r, session.iscsi.ImmediateData, IDBM_SHOW, -- "No", "Yes", num); -+ "No", "Yes", num, 1); - __recinfo_int("node.session.iscsi.FirstBurstLength", ri, r, -- session.iscsi.FirstBurstLength, IDBM_SHOW, num); -+ session.iscsi.FirstBurstLength, IDBM_SHOW, num, 1); - __recinfo_int("node.session.iscsi.MaxBurstLength", ri, r, -- session.iscsi.MaxBurstLength, IDBM_SHOW, num); -+ session.iscsi.MaxBurstLength, IDBM_SHOW, num, 1); - __recinfo_int("node.session.iscsi.DefaultTime2Retain", ri, r, -- session.iscsi.DefaultTime2Retain, IDBM_SHOW, num); -+ session.iscsi.DefaultTime2Retain, IDBM_SHOW, num, 1); - __recinfo_int("node.session.iscsi.DefaultTime2Wait", ri, r, -- session.iscsi.DefaultTime2Wait, IDBM_SHOW, num); -+ session.iscsi.DefaultTime2Wait, IDBM_SHOW, num, 1); - __recinfo_int("node.session.iscsi.MaxConnections", ri, r, -- session.iscsi.MaxConnections, IDBM_SHOW, num); -+ session.iscsi.MaxConnections, IDBM_SHOW, num, 1); - __recinfo_int("node.session.iscsi.MaxOutstandingR2T", ri, r, -- session.iscsi.MaxOutstandingR2T, IDBM_SHOW, num); -+ session.iscsi.MaxOutstandingR2T, IDBM_SHOW, num, 1); - __recinfo_int("node.session.iscsi.ERL", ri, r, -- session.iscsi.ERL, IDBM_SHOW, num); -+ session.iscsi.ERL, IDBM_SHOW, num, 1); - - for (i = 0; i < ISCSI_CONN_MAX; i++) { - char key[NAME_MAXVAL]; - - sprintf(key, "node.conn[%d].address", i); -- __recinfo_str(key, ri, r, conn[i].address, IDBM_SHOW, num); -+ __recinfo_str(key, ri, r, conn[i].address, IDBM_SHOW, num, 0); - sprintf(key, "node.conn[%d].port", i); -- __recinfo_int(key, ri, r, conn[i].port, IDBM_SHOW, num); -+ __recinfo_int(key, ri, r, conn[i].port, IDBM_SHOW, num, 0); - sprintf(key, "node.conn[%d].startup", i); - __recinfo_int_o3(key, ri, r, conn[i].startup, IDBM_SHOW, -- "manual", "automatic", "onboot", num); -+ "manual", "automatic", "onboot", num, 1); - sprintf(key, "node.conn[%d].tcp.window_size", i); - __recinfo_int(key, ri, r, conn[i].tcp.window_size, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - sprintf(key, "node.conn[%d].tcp.type_of_service", i); - __recinfo_int(key, ri, r, conn[i].tcp.type_of_service, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - sprintf(key, "node.conn[%d].timeo.logout_timeout", i); - __recinfo_int(key, ri, r, conn[i].timeo.logout_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - sprintf(key, "node.conn[%d].timeo.login_timeout", i); - __recinfo_int(key, ri, r, conn[i].timeo.login_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - sprintf(key, "node.conn[%d].timeo.auth_timeout", i); - __recinfo_int(key, ri, r, conn[i].timeo.auth_timeout, -- IDBM_SHOW, num); -- sprintf(key, "node.conn[%d].timeo.active_timeout", i); -- __recinfo_int(key, ri, r, conn[i].timeo.active_timeout, -- IDBM_SHOW, num); -- sprintf(key, "node.conn[%d].timeo.idle_timeout", i); -- __recinfo_int(key, ri, r, conn[i].timeo.idle_timeout, -- IDBM_SHOW, num); -- sprintf(key, "node.conn[%d].timeo.ping_timeout", i); -- __recinfo_int(key, ri, r, conn[i].timeo.ping_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - - sprintf(key, "node.conn[%d].timeo.noop_out_interval", i); - __recinfo_int(key, ri, r, conn[i].timeo.noop_out_interval, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - sprintf(key, "node.conn[%d].timeo.noop_out_timeout", i); - __recinfo_int(key, ri, r, conn[i].timeo.noop_out_timeout, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); - - sprintf(key, "node.conn[%d].iscsi.MaxRecvDataSegmentLength", i); - __recinfo_int(key, ri, r, -- conn[i].iscsi.MaxRecvDataSegmentLength, IDBM_SHOW, num); -+ conn[i].iscsi.MaxRecvDataSegmentLength, IDBM_SHOW, -+ num, 1); - sprintf(key, "node.conn[%d].iscsi.HeaderDigest", i); - __recinfo_int_o4(key, ri, r, conn[i].iscsi.HeaderDigest, - IDBM_SHOW, "None", "CRC32C", "CRC32C,None", -- "None,CRC32C", num); -+ "None,CRC32C", num, 1); - sprintf(key, "node.conn[%d].iscsi.DataDigest", i); - __recinfo_int_o4(key, ri, r, conn[i].iscsi.DataDigest, IDBM_SHOW, - "None", "CRC32C", "CRC32C,None", -- "None,CRC32C", num); -+ "None,CRC32C", num, 1); - sprintf(key, "node.conn[%d].iscsi.IFMarker", i); - __recinfo_int_o2(key, ri, r, conn[i].iscsi.IFMarker, IDBM_SHOW, -- "No", "Yes", num); -+ "No", "Yes", num, 1); - sprintf(key, "node.conn[%d].iscsi.OFMarker", i); - __recinfo_int_o2(key, ri, r, conn[i].iscsi.OFMarker, IDBM_SHOW, -- "No", "Yes", num); -+ "No", "Yes", num, 1); - } - } - --static void --idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri) -+void idbm_recinfo_iface(iface_rec_t *r, recinfo_t *ri) - { - int num = 0; - -- __recinfo_str("iface.iscsi_ifacename", ri, r, name, IDBM_SHOW, num); -- __recinfo_str("iface.net_ifacename", ri, r, netdev, IDBM_SHOW, num); --// __recinfo_str("iface.ipaddress", ri, r, ipaddress, IDBM_SHOW, num); -- __recinfo_str("iface.hwaddress", ri, r, hwaddress, IDBM_SHOW, num); -+ __recinfo_str("iface.iscsi_ifacename", ri, r, name, IDBM_SHOW, num, 0); -+ __recinfo_str("iface.net_ifacename", ri, r, netdev, IDBM_SHOW, num, 1); -+// __recinfo_str("iface.ipaddress", ri, r, ipaddress, IDBM_SHOW, num, 1); -+ __recinfo_str("iface.hwaddress", ri, r, hwaddress, IDBM_SHOW, num, 1); - __recinfo_str("iface.transport_name", ri, r, transport_name, -- IDBM_SHOW, num); -+ IDBM_SHOW, num, 1); -+ __recinfo_str("iface.initiatorname", ri, r, iname, IDBM_SHOW, num, 1); - } - --static recinfo_t* --idbm_recinfo_alloc(int max_keys) -+recinfo_t *idbm_recinfo_alloc(int max_keys) - { - recinfo_t *info; - -@@ -381,14 +431,7 @@ idbm_recinfo_alloc(int max_keys) - return info; - } - --enum { -- PRINT_TYPE_DISCOVERY, -- PRINT_TYPE_NODE, -- PRINT_TYPE_IFACE, --}; -- --static void --idbm_print(int type, void *rec, int show, FILE *f) -+void idbm_print(int type, void *rec, int show, FILE *f) - { - int i; - recinfo_t *info; -@@ -398,13 +441,13 @@ idbm_print(int type, void *rec, int show, FILE *f) - return; - - switch (type) { -- case PRINT_TYPE_DISCOVERY: -+ case IDBM_PRINT_TYPE_DISCOVERY: - idbm_recinfo_discovery((discovery_rec_t*)rec, info); - break; -- case PRINT_TYPE_NODE: -+ case IDBM_PRINT_TYPE_NODE: - idbm_recinfo_node((node_rec_t*)rec, info); - break; -- case PRINT_TYPE_IFACE: -+ case IDBM_PRINT_TYPE_IFACE: - idbm_recinfo_iface((struct iface_rec *)rec, info); - break; - } -@@ -444,7 +487,6 @@ idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type) - rec->u.sendtargets.conn_timeo.login_timeout=15; - rec->u.sendtargets.conn_timeo.auth_timeout = 45; - rec->u.sendtargets.conn_timeo.active_timeout=30; -- rec->u.sendtargets.conn_timeo.idle_timeout = 60; - rec->u.sendtargets.iscsi.MaxRecvDataSegmentLength = - DEF_INI_DISC_MAX_RECV_SEG_LEN; - } else if (type == DISCOVERY_TYPE_SLP) { -@@ -460,9 +502,8 @@ idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type) - } - } - --static int --idbm_node_update_param(recinfo_t *info, char *name, char *value, -- int line_number) -+int idbm_rec_update_param(recinfo_t *info, char *name, char *value, -+ int line_number) - { - int i; - int passwd_done = 0; -@@ -531,14 +572,38 @@ updated: - return 0; - } - --static void --idbm_recinfo_config(recinfo_t *info, FILE *f) -+/* -+ * TODO: we can also check for valid values here. -+ */ -+int idbm_verify_param(recinfo_t *info, char *name) -+{ -+ int i; -+ -+ for (i = 0; i < MAX_KEYS; i++) { -+ if (strcmp(name, info[i].name)) -+ continue; -+ -+ log_debug(7, "verify %s %d\n", name, info[i].can_modify); -+ if (info[i].can_modify) -+ return 0; -+ else { -+ log_error("Cannot modify %s. It is used to look up " -+ "the record and cannot be changed.", name); -+ return EINVAL; -+ } -+ } -+ -+ log_error("Cannot modify %s. Invalid param name.", name); -+ return EINVAL; -+} -+ -+void idbm_recinfo_config(recinfo_t *info, FILE *f) - { - char name[NAME_MAXVAL]; - char value[VALUE_MAXVAL]; - char *line, *nl, buffer[2048]; - int line_number = 0; -- int c, i; -+ int c = 0, i; - - fseek(f, 0, SEEK_SET); - -@@ -549,27 +614,16 @@ idbm_recinfo_config(recinfo_t *info, FILE *f) - if (!line) - continue; - -- /* skip leading whitespace */ -- while (isspace(c = *line)) -- line++; -- -- /* strip trailing whitespace, including the newline. -- * anything that needs the whitespace must be quoted. -- */ - nl = line + strlen(line) - 1; -- if (*nl == '\n') { -- do { -- *nl = '\0'; -- nl--; -- } while (isspace(c = *nl)); -- } else { -- log_warning("config file line %d too long", -+ if (*nl != '\n') { -+ log_warning("Config file line %d too long.", - line_number); - continue; - } - -+ line = strstrip(line); - /* process any non-empty, non-comment lines */ -- if (!*line || *line == '#') -+ if (!*line || *line == '\0' || *line == '\n' || *line == '#') - continue; - - /* parse name */ -@@ -606,16 +660,16 @@ idbm_recinfo_config(recinfo_t *info, FILE *f) - } - *(value+i) = 0; - -- (void)idbm_node_update_param(info, name, value, line_number); -+ (void)idbm_rec_update_param(info, name, value, line_number); - } while (line); - } - - /* - * TODO: remove db's copy of nrec and infos - */ --static void --idbm_sync_config(idbm_t *db) -+static void idbm_sync_config(void) - { -+ char *config_file; - FILE *f; - - /* in case of no configuration file found we just -@@ -631,15 +685,25 @@ idbm_sync_config(idbm_t *db) - idbm_recinfo_discovery(&db->drec_isns, db->dinfo_isns); - idbm_recinfo_node(&db->nrec, db->ninfo); - -- f = fopen(db->configfile, "r"); -+ if (!db->get_config_file) { -+ log_debug(1, "Could not get config file. No config file fn\n"); -+ return; -+ } -+ -+ config_file = db->get_config_file(); -+ if (!config_file) { -+ log_debug(1, "Could not get config file for sync config\n"); -+ return; -+ } -+ -+ f = fopen(config_file, "r"); - if (!f) { - log_debug(1, "cannot open configuration file %s. " - "Default location is %s.\n", -- db->configfile, CONFIG_FILE); -+ config_file, CONFIG_FILE); - return; - } -- -- log_debug(5, "updating defaults from '%s'", db->configfile); -+ log_debug(5, "updating defaults from '%s'", config_file); - - idbm_recinfo_config(db->dinfo_st, f); - idbm_recinfo_config(db->dinfo_slp, f); -@@ -668,29 +732,37 @@ idbm_sync_config(idbm_t *db) - strlen((char*)db->nrec.session.auth.password_in); - } - --void idbm_node_setup_from_conf(idbm_t *db, node_rec_t *rec) -+void idbm_node_setup_from_conf(node_rec_t *rec) - { - memset(rec, 0, sizeof(*rec)); - idbm_node_setup_defaults(rec); -- idbm_sync_config(db); -+ idbm_sync_config(); - memcpy(rec, &db->nrec, sizeof(*rec)); - } - --int idbm_print_discovery_info(idbm_t *db, discovery_rec_t *rec, int show) -+int idbm_print_discovery_info(discovery_rec_t *rec, int show) - { -- idbm_print(PRINT_TYPE_DISCOVERY, rec, show, stdout); -+ idbm_print(IDBM_PRINT_TYPE_DISCOVERY, rec, show, stdout); - return 1; - } - --int idbm_print_node_info(idbm_t *db, void *data, node_rec_t *rec) -+int idbm_print_node_info(void *data, node_rec_t *rec) -+{ -+ int show = *((int *)data); -+ -+ idbm_print(IDBM_PRINT_TYPE_NODE, rec, show, stdout); -+ return 0; -+} -+ -+int idbm_print_iface_info(void *data, struct iface_rec *iface) - { - int show = *((int *)data); - -- idbm_print(PRINT_TYPE_NODE, rec, show, stdout); -+ idbm_print(IDBM_PRINT_TYPE_IFACE, iface, show, stdout); - return 0; - } - --int idbm_print_node_flat(idbm_t *db, void *data, node_rec_t *rec) -+int idbm_print_node_flat(void *data, node_rec_t *rec) - { - if (strchr(rec->conn[0].address, '.')) - printf("%s:%d,%d %s\n", rec->conn[0].address, rec->conn[0].port, -@@ -701,17 +773,19 @@ int idbm_print_node_flat(idbm_t *db, void *data, node_rec_t *rec) - return 0; - } - --int idbm_print_node_tree(idbm_t *db, void *data, node_rec_t *rec) -+int idbm_print_node_tree(void *data, node_rec_t *rec) - { - node_rec_t *last_rec = data; - -- if (strcmp(last_rec->name, rec->name)) { -+ if (!last_rec || strcmp(last_rec->name, rec->name)) { - printf("Target: %s\n", rec->name); -- memset(last_rec, 0, sizeof(node_rec_t)); -+ if (last_rec) -+ memset(last_rec, 0, sizeof(node_rec_t)); - } - -- if ((strcmp(last_rec->conn[0].address, rec->conn[0].address) || -- last_rec->conn[0].port != rec->conn[0].port)) { -+ if (!last_rec || -+ ((strcmp(last_rec->conn[0].address, rec->conn[0].address) || -+ last_rec->conn[0].port != rec->conn[0].port))) { - if (strchr(rec->conn[0].address, '.')) - printf("\tPortal: %s:%d,%d\n", rec->conn[0].address, - rec->conn[0].port, rec->tpgt); -@@ -722,7 +796,8 @@ int idbm_print_node_tree(idbm_t *db, void *data, node_rec_t *rec) - - printf("\t\tIface Name: %s\n", rec->iface.name); - -- memcpy(last_rec, rec, sizeof(node_rec_t)); -+ if (last_rec) -+ memcpy(last_rec, rec, sizeof(node_rec_t)); - return 0; - } - -@@ -750,7 +825,7 @@ get_params_from_disc_link(char *link, char **target, char **tpgt, - return 0; - } - --static int idbm_lock(idbm_t *db) -+int idbm_lock(void) - { - int fd, i, ret; - -@@ -782,7 +857,7 @@ static int idbm_lock(idbm_t *db) - return 0; - } - --static void idbm_unlock(idbm_t *db) -+void idbm_unlock(void) - { - if (db->refs > 1) { - db->refs--; -@@ -794,551 +869,6 @@ static void idbm_unlock(idbm_t *db) - } - - /* -- * default is to use tcp through whatever the network layer -- * selects for us -- */ --void iface_init(struct iface_rec *iface) --{ -- sprintf(iface->netdev, DEFAULT_NETDEV); --// sprintf(iface->ipaddress, DEFAULT_IPADDRESS); -- sprintf(iface->hwaddress, DEFAULT_HWADDRESS); -- sprintf(iface->transport_name, DEFAULT_TRANSPORT); -- if (!strlen(iface->name)) -- sprintf(iface->name, DEFAULT_IFACENAME); --} -- --struct iface_rec *iface_alloc(char *ifname, int *err) --{ -- struct iface_rec *iface; -- -- if (!strlen(ifname) || strlen(ifname) + 1 > ISCSI_MAX_IFACE_LEN) { -- *err = EINVAL; -- return NULL; -- } -- -- iface = calloc(1, sizeof(*iface)); -- if (!iface) { -- *err = ENOMEM; -- return NULL; -- } -- -- strncpy(iface->name, ifname, ISCSI_MAX_IFACE_LEN); -- INIT_LIST_HEAD(&iface->list); -- return iface; --} -- --int iface_conf_read(struct iface_rec *iface) --{ -- char *iface_conf; -- recinfo_t *info; -- FILE *f; -- int rc = 0; -- -- iface_conf = calloc(1, PATH_MAX); -- if (!iface_conf) -- return ENOMEM; -- -- info = idbm_recinfo_alloc(MAX_KEYS); -- if (!info) { -- rc = ENOMEM; -- goto free_conf; -- } -- -- snprintf(iface_conf, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, -- iface->name); -- -- log_debug(5, "looking for iface conf %s", iface_conf); -- f = fopen(iface_conf, "r"); -- if (!f) { -- /* -- * if someone passes in default but has not defined -- * a iface with default then we do it for them -- */ -- if (!strcmp(iface->name, DEFAULT_IFACENAME)) { -- iface_init(iface); -- rc = 0; -- } else -- rc = errno; -- goto free_info; -- } -- -- iface_init(iface); -- idbm_recinfo_iface(iface, info); -- idbm_recinfo_config(info, f); -- fclose(f); -- --free_info: -- free(info); --free_conf: -- free(iface_conf); -- return rc; --} -- --int iface_conf_delete(struct iface_rec *iface) --{ -- char *iface_conf; -- int rc = 0; -- -- iface_conf = calloc(1, PATH_MAX); -- if (!iface_conf) -- return ENOMEM; -- -- sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name); -- if (unlink(iface_conf)) -- rc = errno; -- free(iface_conf); -- return rc; --} -- --int iface_conf_write(struct iface_rec *iface) --{ -- char *iface_conf; -- FILE *f; -- int rc = 0; -- -- iface_conf = calloc(1, PATH_MAX); -- if (!iface_conf) -- return ENOMEM; -- -- sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name); -- f = fopen(iface_conf, "w"); -- if (!f) { -- rc = errno; -- goto free_conf; -- } -- -- idbm_print(PRINT_TYPE_IFACE, iface, 1, f); -- fclose(f); --free_conf: -- free(iface_conf); -- return rc; --} -- --int iface_conf_update(struct db_set_param *param, struct iface_rec *iface) --{ -- recinfo_t *info; -- int rc = 0; -- -- info = idbm_recinfo_alloc(MAX_KEYS); -- if (!info) -- return ENOMEM; -- -- idbm_recinfo_iface(iface, info); -- rc = idbm_node_update_param(info, param->name, param->value, 0); -- if (rc) { -- rc = EIO; -- goto free_info; -- } -- -- rc = iface_conf_write(iface); --free_info: -- free(info); -- return rc; --} -- --static void idbm_read_def_ifaces(struct list_head *ifaces) --{ -- DIR *iface_dirfd; -- struct dirent *iface_dent; -- struct iface_rec *iface; -- int err = 0; -- -- iface_dirfd = opendir(IFACE_CONFIG_DIR); -- if (!iface_dirfd) -- return; -- -- while ((iface_dent = readdir(iface_dirfd))) { -- if (!strcmp(iface_dent->d_name, ".") || -- !strcmp(iface_dent->d_name, "..")) -- continue; -- -- log_debug(5, "idbm_read_def_ifaces found %s", -- iface_dent->d_name); -- iface = iface_alloc(iface_dent->d_name, &err); -- if (!iface || err) { -- if (err == EINVAL) -- log_error("Invalid iface name %s. Must be " -- "from 1 to %d characters.", -- iface_dent->d_name, -- ISCSI_MAX_IFACE_LEN - 1); -- else -- log_error("Could not add iface %s.", -- iface_dent->d_name); -- free(iface); -- continue; -- } -- -- err = iface_conf_read(iface); -- if (err) { -- log_error("Could not read def iface %s (err %d)", -- iface->name, err); -- free(iface); -- continue; -- } -- if (!iface_is_bound(iface)) { -- log_debug(5, "Default iface is not bound " -- "Iface settings " iface_fmt, -- iface_str(iface)); -- free(iface); -- continue; -- } -- -- log_debug(5, "idbm_read_def_ifaces added %s", iface->name); -- list_add_tail(&iface->list, ifaces); -- } -- closedir(iface_dirfd); --} -- --static int iface_get_next_id(void) --{ -- struct stat statb; -- char *iface_conf; -- int i, rc = ENOSPC; -- -- iface_conf = calloc(1, PATH_MAX); -- if (!iface_conf) -- return ENOMEM; -- -- for (i = 0; i < INT_MAX; i++) { -- memset(iface_conf, 0, PATH_MAX); -- /* check len */ -- snprintf(iface_conf, PATH_MAX, "iface%d", i); -- if (strlen(iface_conf) > ISCSI_MAX_IFACE_LEN - 1) { -- log_error("iface namespace is full. Remove unused " -- "iface definitions from %s or send mail " -- "to open-iscsi@googlegroups.com to report " -- "the problem", IFACE_CONFIG_DIR); -- rc = ENOSPC; -- break; -- } -- memset(iface_conf, 0, PATH_MAX); -- snprintf(iface_conf, PATH_MAX, "%s/iface%d", IFACE_CONFIG_DIR, -- i); -- -- if (!stat(iface_conf, &statb)) -- continue; -- if (errno == ENOENT) { -- rc = i; -- break; -- } -- } -- -- free(iface_conf); -- return rc; --} -- --struct iface_search { -- struct iface_rec *pattern; -- struct iface_rec *found; --}; -- --static int __iface_get_by_bind_info(void *data, struct iface_rec *iface) --{ -- struct iface_search *search = data; -- -- if (!strcmp(search->pattern->name, iface->name)) { -- iface_copy(search->found, iface); -- return 1; -- } -- -- if (iface_is_bound_by_hwaddr(search->pattern)) { -- if (!strcmp(iface->hwaddress, search->pattern->hwaddress)) { -- iface_copy(search->found, iface); -- return 1; -- } else -- return 0; -- } -- -- if (iface_is_bound_by_netdev(search->pattern)) { -- if (!strcmp(iface->netdev, search->pattern->netdev)) { -- iface_copy(search->found, iface); -- return 1; -- } else -- return 0; -- } -- --/* -- if (iface_is_bound_by_ipaddr(search->pattern)) { -- if (!strcmp(iface->ipaddress, search->pattern->ipaddress)) { -- iface_copy(search->found, iface); -- return 1; -- } else -- return 0; -- } --*/ -- return 0; --} -- --int iface_get_by_bind_info(idbm_t *db, struct iface_rec *pattern, -- struct iface_rec *out_rec) --{ -- int num_found = 0, rc; -- struct iface_search search; -- -- search.pattern = pattern; -- search.found = out_rec; -- -- rc = iface_for_each_iface(db, &search, &num_found, -- __iface_get_by_bind_info); -- if (rc == 1) -- return 0; -- -- if (iface_is_bound(pattern)) -- return ENODEV; -- -- /* -- * compat for default behavior -- */ -- if (!strlen(pattern->name) || -- !strcmp(pattern->name, DEFAULT_IFACENAME)) { -- iface_init(out_rec); -- return 0; -- } -- -- return ENODEV; --} -- --static int __iface_setup_host_bindings(void *data, struct host_info *info) --{ -- idbm_t *db = data; -- struct iface_rec iface; -- struct iscsi_transport *t; -- int id; -- -- if (!strlen(info->iface.hwaddress) || -- !strlen(info->iface.transport_name)) -- return 0; -- -- t = get_transport_by_hba(info->host_no); -- if (!t) -- return 0; -- /* -- * if software iscsi do not touch the bindngs. They do not -- * need it and may not support it -- */ -- if (!(t->caps & CAP_DATA_PATH_OFFLOAD)) -- return 0; -- -- if (iface_get_by_bind_info(db, &info->iface, &iface)) { -- /* Must be a new port */ -- id = iface_get_next_id(); -- if (id < 0) { -- log_error("Could not add iface for %s.", -- info->iface.hwaddress); -- return 0; -- } -- memset(&iface, 0, sizeof(struct iface_rec)); -- strcpy(iface.hwaddress, info->iface.hwaddress); -- strcpy(iface.transport_name, info->iface.transport_name); -- sprintf(iface.name, "iface%d", id); -- if (iface_conf_write(&iface)) -- log_error("Could not write iface conf for %s %s", -- iface.name, iface.hwaddress); -- /* fall through - will not be persistent */ -- } -- return 0; --} -- --/* -- * sync hw/offload iscsi scsi_hosts with iface values -- */ --void iface_setup_host_bindings(idbm_t *db) --{ -- int nr_found = 0; -- -- idbm_lock(db); -- if (access(IFACE_CONFIG_DIR, F_OK) != 0) { -- if (mkdir(IFACE_CONFIG_DIR, 0660) != 0) { -- log_error("Could not make %s. HW/OFFLOAD iscsi " -- "may not be supported", IFACE_CONFIG_DIR); -- idbm_unlock(db); -- return; -- } -- } -- idbm_unlock(db); -- -- if (sysfs_for_each_host(db, &nr_found, -- __iface_setup_host_bindings)) -- log_error("Could not scan scsi hosts. HW/OFFLOAD iscsi " -- "operations may not be supported."); --} -- --void iface_copy(struct iface_rec *dst, struct iface_rec *src) --{ -- if (strlen(src->name)) -- strcpy(dst->name, src->name); -- if (strlen(src->netdev)) -- strcpy(dst->netdev, src->netdev); --// if (strlen(src->ipaddress)) --// strcpy(dst->ipaddress, src->ipaddress); -- if (strlen(src->hwaddress)) -- strcpy(dst->hwaddress, src->hwaddress); -- if (strlen(src->transport_name)) -- strcpy(dst->transport_name, src->transport_name); --} -- --int iface_is_bound(struct iface_rec *iface) --{ -- if (!iface) -- return 0; -- -- if (iface_is_bound_by_hwaddr(iface)) -- return 1; -- -- if (iface_is_bound_by_netdev(iface)) -- return 1; -- --// if (iface_is_bound_by_ipaddr(iface)) --// return 1; -- -- return 0; --} -- --int iface_match_bind_info(struct iface_rec *pattern, struct iface_rec *iface) --{ -- if (!pattern || !iface) -- return 1; -- -- /* if no values set then we have a match */ -- if (!strlen(pattern->hwaddress) && --// !strlen(pattern->ipaddress) && -- !strlen(pattern->netdev) && -- !strlen(pattern->name)) -- return 1; -- -- /* -- * If both interfaces are not bound we return match. -- * This assumes we will not have two ifaces with different -- * names and no binding info. There should only be one -- * iface with no binding info for each transport and that -- * is the "default" which is used for backward compat from -- * when we did not have ifaces. -- */ -- if (!iface_is_bound(iface) && -- !iface_is_bound(pattern)) -- return 1; -- -- if (iface_is_bound_by_hwaddr(pattern) && -- !strcmp(pattern->hwaddress, iface->hwaddress)) -- return 1; -- -- if (iface_is_bound_by_netdev(iface) && -- !strcmp(pattern->netdev, iface->netdev)) -- return 1; -- --// if (iface_is_bound_by_ipaddr(iface) && --// !strcmp(pattern->ipaddress, iface->ipaddress)) --// return 1; -- -- if (strlen(pattern->name)) { -- if (!strcmp(pattern->name, iface->name)) -- return 1; -- } -- -- return 0; --} -- --int iface_is_bound_by_hwaddr(struct iface_rec *iface) --{ -- if (iface && strlen(iface->hwaddress) && -- strcmp(iface->hwaddress, DEFAULT_HWADDRESS)) -- return 1; -- return 0; --} -- --int iface_is_bound_by_netdev(struct iface_rec *iface) --{ -- if (iface && strlen(iface->netdev) && -- strcmp(iface->netdev, DEFAULT_NETDEV)) -- return 1; -- return 0; --} -- --int iface_is_bound_by_ipaddr(struct iface_rec *iface) --{ -- return 0; --/* if (iface && strlen(iface->ipaddress) && -- strcmp(iface->ipaddress, DEFAULT_NETDEV)) -- return 1; -- return 0; --*/ --} -- --int iface_print_flat(void *data, struct iface_rec *iface) --{ -- printf("%s %s,%s,%s\n", -- strlen(iface->name) ? iface->name : UNKNOWN_VALUE, -- strlen(iface->transport_name) ? iface->transport_name : -- UNKNOWN_VALUE, -- strlen(iface->hwaddress) ? iface->hwaddress : UNKNOWN_VALUE, -- strlen(iface->netdev) ? iface->netdev : UNKNOWN_VALUE); -- return 0; --} -- --static int iface_filter(const struct dirent *dir) --{ -- return strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."); --} -- --int iface_for_each_iface(idbm_t *db, void *data, int *nr_found, iface_op_fn *fn) --{ -- struct dirent **namelist; -- struct iface_rec *iface; -- int err = 0, n, i; -- -- n = scandir(IFACE_CONFIG_DIR, &namelist, iface_filter, versionsort); -- if (n <= 0) -- return 0; -- -- for (i = 0; i < n; i++) { -- log_debug(5, "iface_for_each_iface found %s", -- namelist[i]->d_name); -- iface = iface_alloc(namelist[i]->d_name, &err); -- if (!iface || err) { -- if (err == EINVAL) -- log_error("Invalid iface name %s. Must be " -- "from 1 to %d characters.", -- namelist[i]->d_name, -- ISCSI_MAX_IFACE_LEN - 1); -- else -- log_error("Could not add iface %s.", -- namelist[i]->d_name); -- free(iface); -- continue; -- } -- -- idbm_lock(db); -- err = iface_conf_read(iface); -- idbm_unlock(db); -- if (err) { -- log_error("Could not read def iface %s (err %d)", -- iface->name, err); -- free(iface); -- continue; -- } -- -- if (!iface_is_bound(iface)) { -- log_debug(5, "iface is not bound " -- "Iface settings " iface_fmt, -- iface_str(iface)); -- free(iface); -- continue; -- } -- -- err = fn(data, iface); -- free(iface); -- if (err) -- break; -- (*nr_found)++; -- } -- -- for (i = 0; i < n; i++) -- free(namelist[i]); -- free(namelist); -- return err; --} -- --/* - * Backwards Compat: - * If the portal is a file then we are doing the old style default - * session behavior (svn pre 780). -@@ -1361,7 +891,7 @@ static FILE *idbm_open_rec_r(char *portal, char *config) - return fopen(portal, "r"); - } - --static int __idbm_rec_read(idbm_t *db, node_rec_t *out_rec, char *conf) -+static int __idbm_rec_read(node_rec_t *out_rec, char *conf) - { - recinfo_t *info; - FILE *f; -@@ -1371,7 +901,7 @@ static int __idbm_rec_read(idbm_t *db, node_rec_t *out_rec, char *conf) - if (!info) - return ENOMEM; - -- idbm_lock(db); -+ idbm_lock(); - f = fopen(conf, "r"); - if (!f) { - log_debug(5, "Could not open %s err %d\n", conf, errno); -@@ -1386,13 +916,13 @@ static int __idbm_rec_read(idbm_t *db, node_rec_t *out_rec, char *conf) - fclose(f); - - unlock: -- idbm_unlock(db); -+ idbm_unlock(); - free(info); - return rc; - } - - int --idbm_rec_read(idbm_t *db, node_rec_t *out_rec, char *targetname, int tpgt, -+idbm_rec_read(node_rec_t *out_rec, char *targetname, int tpgt, - char *ip, int port, struct iface_rec *iface) - { - struct stat statb; -@@ -1425,7 +955,7 @@ idbm_rec_read(idbm_t *db, node_rec_t *out_rec, char *targetname, int tpgt, - } - - read: -- rc = __idbm_rec_read(db, out_rec, portal); -+ rc = __idbm_rec_read(out_rec, portal); - free_portal: - free(portal); - return rc; -@@ -1437,7 +967,7 @@ static int st_disc_filter(const struct dirent *dir) - strcmp(dir->d_name, ST_CONFIG_NAME); - } - --static int print_discovered(idbm_t *db, char *disc_path, int info_level) -+static int print_discovered(char *disc_path, int info_level) - { - char *tmp_port = NULL, *last_address = NULL, *last_target = NULL; - char *target = NULL, *address = NULL, *ifaceid = NULL, *tpgt = NULL; -@@ -1446,7 +976,7 @@ static int print_discovered(idbm_t *db, char *disc_path, int info_level) - struct dirent **namelist; - node_rec_t *rec; - -- n = scandir(disc_path, &namelist, st_disc_filter, versionsort); -+ n = scandir(disc_path, &namelist, st_disc_filter, direntcmp); - if (n < 0) - return 0; - -@@ -1469,7 +999,7 @@ static int print_discovered(idbm_t *db, char *disc_path, int info_level) - memset(portal, 0, PATH_MAX); - snprintf(portal, PATH_MAX, "%s/%s/%s,%s,%s/%s", NODE_CONFIG_DIR, - target, address, tmp_port, tpgt, ifaceid); -- if (__idbm_rec_read(db, rec, portal)) { -+ if (__idbm_rec_read(rec, portal)) { - log_error("Could not read node record for %s " - "%s %d %s", target, address, atoi(tmp_port), - ifaceid); -@@ -1519,7 +1049,7 @@ free_namelist: - return n; - } - --int idbm_print_discovered(idbm_t *db, discovery_rec_t *drec, int info_level) -+int idbm_print_discovered(discovery_rec_t *drec, int info_level) - { - char *disc_path; - int rc; -@@ -1545,13 +1075,13 @@ int idbm_print_discovered(idbm_t *db, discovery_rec_t *drec, int info_level) - goto done; - } - -- rc = print_discovered(db, disc_path, info_level); -+ rc = print_discovered(disc_path, info_level); - done: - free(disc_path); - return rc; - } - --static int idbm_print_all_st(idbm_t *db, int info_level) -+static int idbm_print_all_st(int info_level) - { - DIR *entity_dirfd; - struct dirent *entity_dent; -@@ -1578,7 +1108,7 @@ static int idbm_print_all_st(idbm_t *db, int info_level) - entity_dent->d_name); - - printf("DiscoveryAddress: %s\n", entity_dent->d_name); -- found += print_discovered(db, disc_dir, info_level); -+ found += print_discovered(disc_dir, info_level); - } else { - char *tmp_port; - -@@ -1598,13 +1128,13 @@ free_disc: - return found; - } - --int idbm_print_all_discovery(idbm_t *db, int info_level) -+int idbm_print_all_discovery(int info_level) - { - discovery_rec_t *drec; - int found = 0, tmp; - - if (info_level < 1) -- return idbm_print_all_st(db, info_level); -+ return idbm_print_all_st(info_level); - - drec = calloc(1, sizeof(*drec)); - if (!drec) -@@ -1612,7 +1142,7 @@ int idbm_print_all_discovery(idbm_t *db, int info_level) - - tmp = 0; - printf("SENDTARGETS:\n"); -- tmp = idbm_print_all_st(db, info_level); -+ tmp = idbm_print_all_st(info_level); - if (!tmp) - printf("No targets found.\n"); - found += tmp; -@@ -1620,7 +1150,7 @@ int idbm_print_all_discovery(idbm_t *db, int info_level) - - printf("iSNS:\n"); - drec->type = DISCOVERY_TYPE_ISNS; -- tmp = idbm_print_discovered(db, drec, info_level); -+ tmp = idbm_print_discovered(drec, info_level); - if (!tmp) - printf("No targets found.\n"); - found += tmp; -@@ -1628,7 +1158,7 @@ int idbm_print_all_discovery(idbm_t *db, int info_level) - - printf("STATIC:\n"); - drec->type = DISCOVERY_TYPE_STATIC; -- tmp = idbm_print_discovered(db, drec, info_level); -+ tmp = idbm_print_discovered(drec, info_level); - if (!tmp) - printf("No targets found.\n"); - found += tmp; -@@ -1641,7 +1171,7 @@ int idbm_print_all_discovery(idbm_t *db, int info_level) - * This iterates over the ifaces in use in the nodes dir. - * It does not iterate over the ifaces setup in /etc/iscsi/ifaces. - */ --static int idbm_for_each_iface(idbm_t *db, int *found, void *data, -+static int idbm_for_each_iface(int *found, void *data, - idbm_iface_op_fn *fn, - char *targetname, int tpgt, char *ip, int port) - { -@@ -1668,11 +1198,11 @@ static int idbm_for_each_iface(idbm_t *db, int *found, void *data, - goto free_portal; - } - -- rc = __idbm_rec_read(db, &rec, portal); -+ rc = __idbm_rec_read(&rec, portal); - if (rc) - goto free_portal; - -- rc = fn(db, data, &rec); -+ rc = fn(data, &rec); - if (!rc) - (*found)++; - else if (rc == -1) -@@ -1698,11 +1228,11 @@ read_iface: - memset(portal, 0, PATH_MAX); - snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d/%s", NODE_CONFIG_DIR, - targetname, ip, port, tpgt, iface_dent->d_name); -- if (__idbm_rec_read(db, &rec, portal)) -+ if (__idbm_rec_read(&rec, portal)) - continue; - - /* less than zero means it was not a match */ -- rc = fn(db, data, &rec); -+ rc = fn(data, &rec); - if (rc > 0) - break; - else if (rc == 0) -@@ -1721,8 +1251,8 @@ free_portal: - * backwards compat - * The portal could be a file or dir with interfaces - */ --int idbm_for_each_portal(idbm_t *db, int *found, void *data, -- idbm_portal_op_fn *fn, char *targetname) -+int idbm_for_each_portal(int *found, void *data, idbm_portal_op_fn *fn, -+ char *targetname) - { - DIR *portal_dirfd; - struct dirent *portal_dent; -@@ -1756,7 +1286,7 @@ int idbm_for_each_portal(idbm_t *db, int *found, void *data, - if (tmp_tpgt) - *tmp_tpgt++ = '\0'; - -- rc = fn(db, found, data, targetname, -+ rc = fn(found, data, targetname, - tmp_tpgt ? atoi(tmp_tpgt) : -1, - portal_dent->d_name, atoi(tmp_port)); - if (rc) -@@ -1768,7 +1298,7 @@ done: - return rc; - } - --int idbm_for_each_node(idbm_t *db, int *found, void *data, idbm_node_op_fn *fn) -+int idbm_for_each_node(int *found, void *data, idbm_node_op_fn *fn) - { - DIR *node_dirfd; - struct dirent *node_dent; -@@ -1787,7 +1317,7 @@ int idbm_for_each_node(idbm_t *db, int *found, void *data, idbm_node_op_fn *fn) - continue; - - log_debug(5, "searching %s\n", node_dent->d_name); -- rc = fn(db, found, data, node_dent->d_name); -+ rc = fn(found, data, node_dent->d_name); - if (rc) - break; - } -@@ -1796,26 +1326,26 @@ int idbm_for_each_node(idbm_t *db, int *found, void *data, idbm_node_op_fn *fn) - return rc; - } - --static int iface_fn(idbm_t *db, void *data, node_rec_t *rec) -+static int iface_fn(void *data, node_rec_t *rec) - { - struct rec_op_data *op_data = data; - -- return op_data->fn(db, op_data->data, rec); -+ return op_data->fn(op_data->data, rec); - } - --static int portal_fn(idbm_t *db, int *found, void *data, char *targetname, -+static int portal_fn(int *found, void *data, char *targetname, - int tpgt, char *ip, int port) - { -- return idbm_for_each_iface(db, found, data, iface_fn, targetname, -+ return idbm_for_each_iface(found, data, iface_fn, targetname, - tpgt, ip, port); - } - --static int node_fn(idbm_t *db, int *found, void *data, char *targetname) -+static int node_fn(int *found, void *data, char *targetname) - { -- return idbm_for_each_portal(db, found, data, portal_fn, targetname); -+ return idbm_for_each_portal(found, data, portal_fn, targetname); - } - --int idbm_for_each_rec(idbm_t *db, int *found, void *data, idbm_iface_op_fn *fn) -+int idbm_for_each_rec(int *found, void *data, idbm_iface_op_fn *fn) - { - struct rec_op_data op_data; - -@@ -1823,11 +1353,11 @@ int idbm_for_each_rec(idbm_t *db, int *found, void *data, idbm_iface_op_fn *fn) - op_data.data = data; - op_data.fn = fn; - -- return idbm_for_each_node(db, found, &op_data, node_fn); -+ return idbm_for_each_node(found, &op_data, node_fn); - } - - int --idbm_discovery_read(idbm_t *db, discovery_rec_t *out_rec, char *addr, int port) -+idbm_discovery_read(discovery_rec_t *out_rec, char *addr, int port) - { - recinfo_t *info; - char *portal; -@@ -1848,7 +1378,7 @@ idbm_discovery_read(idbm_t *db, discovery_rec_t *out_rec, char *addr, int port) - addr, port); - log_debug(5, "Looking for config file %s\n", portal); - -- idbm_lock(db); -+ idbm_lock(); - - f = idbm_open_rec_r(portal, ST_CONFIG_NAME); - if (!f) { -@@ -1863,7 +1393,7 @@ idbm_discovery_read(idbm_t *db, discovery_rec_t *out_rec, char *addr, int port) - fclose(f); - - unlock: -- idbm_unlock(db); -+ idbm_unlock(); - free_info: - free(portal); - free(info); -@@ -1914,7 +1444,7 @@ mkdir_portal: - return f; - } - --static int idbm_rec_write(idbm_t *db, node_rec_t *rec) -+static int idbm_rec_write(node_rec_t *rec) - { - struct stat statb; - FILE *f; -@@ -1949,7 +1479,7 @@ static int idbm_rec_write(idbm_t *db, node_rec_t *rec) - rec->name, rec->conn[0].address, rec->conn[0].port); - log_debug(5, "Looking for config file %s", portal); - -- idbm_lock(db); -+ idbm_lock(); - - rc = stat(portal, &statb); - if (rc) { -@@ -2004,17 +1534,17 @@ open_conf: - goto unlock; - } - -- idbm_print(PRINT_TYPE_NODE, rec, 1, f); -+ idbm_print(IDBM_PRINT_TYPE_NODE, rec, 1, f); - fclose(f); - unlock: -- idbm_unlock(db); -+ idbm_unlock(); - free_portal: - free(portal); - return rc; - } - - static int --idbm_discovery_write(idbm_t *db, discovery_rec_t *rec) -+idbm_discovery_write(discovery_rec_t *rec) - { - FILE *f; - char *portal; -@@ -2023,10 +1553,10 @@ idbm_discovery_write(idbm_t *db, discovery_rec_t *rec) - portal = malloc(PATH_MAX); - if (!portal) { - log_error("Could not alloc portal\n"); -- return -ENOMEM; -+ return ENOMEM; - } - -- idbm_lock(db); -+ idbm_lock(); - snprintf(portal, PATH_MAX, "%s", ST_CONFIG_DIR); - if (access(portal, F_OK) != 0) { - if (mkdir(portal, 0660) != 0) { -@@ -2046,27 +1576,29 @@ idbm_discovery_write(idbm_t *db, discovery_rec_t *rec) - goto free_portal; - } - -- idbm_print(PRINT_TYPE_DISCOVERY, rec, 1, f); -+ idbm_print(IDBM_PRINT_TYPE_DISCOVERY, rec, 1, f); - fclose(f); - free_portal: -- idbm_unlock(db); -+ idbm_unlock(); - free(portal); - return rc; - } - --static int --idbm_add_discovery(idbm_t *db, discovery_rec_t *newrec) -+int -+idbm_add_discovery(discovery_rec_t *newrec, int overwrite) - { - discovery_rec_t rec; - int rc; - -- if (!idbm_discovery_read(db, &rec, newrec->address, -+ if (!idbm_discovery_read(&rec, newrec->address, - newrec->port)) { -+ if (!overwrite) -+ return 0; - log_debug(7, "overwriting existing record"); - } else - log_debug(7, "adding new DB record"); - -- rc = idbm_discovery_write(db, newrec); -+ rc = idbm_discovery_write(newrec); - return rc; - } - -@@ -2106,8 +1638,8 @@ static int setup_disc_to_node_link(char *disc_portal, node_rec_t *rec) - } - } - -- snprintf(disc_portal, PATH_MAX, "%s/%s,%d/%s,%s,%d,%d,%s", -- ISNS_CONFIG_DIR, rec->disc_address, rec->disc_port, -+ snprintf(disc_portal, PATH_MAX, "%s/%s,%s,%d,%d,%s", -+ ISNS_CONFIG_DIR, - rec->name, rec->conn[0].address, - rec->conn[0].port, rec->tpgt, rec->iface.name); - break; -@@ -2119,16 +1651,19 @@ static int setup_disc_to_node_link(char *disc_portal, node_rec_t *rec) - return rc; - } - --int idbm_add_node(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec) -+int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, int overwrite) - { - node_rec_t rec; - char *node_portal, *disc_portal; - int rc; - -- if (!idbm_rec_read(db, &rec, newrec->name, newrec->tpgt, -+ if (!idbm_rec_read(&rec, newrec->name, newrec->tpgt, - newrec->conn[0].address, newrec->conn[0].port, - &newrec->iface)) { -- rc = idbm_delete_node(db, NULL, &rec); -+ if (!overwrite) -+ return 0; -+ -+ rc = idbm_delete_node(&rec); - if (rc) - return rc; - log_debug(7, "overwriting existing record"); -@@ -2141,7 +1676,7 @@ int idbm_add_node(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec) - strcpy(newrec->disc_address, drec->address); - } - -- rc = idbm_rec_write(db, newrec); -+ rc = idbm_rec_write(newrec); - /* - * if a old app passed in a bogus tpgt then we do not create links - * since it will set a different tpgt in another iscsiadm call -@@ -2164,7 +1699,7 @@ int idbm_add_node(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec) - log_debug(7, "node addition making link from %s to %s", node_portal, - disc_portal); - -- idbm_lock(db); -+ idbm_lock(); - if (symlink(node_portal, disc_portal)) { - if (errno == EEXIST) - log_debug(7, "link from %s to %s exists", node_portal, -@@ -2175,14 +1710,31 @@ int idbm_add_node(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec) - "node %s", disc_portal, node_portal); - } - } -- idbm_unlock(db); -+ idbm_unlock(); - free_portal: - free(node_portal); - return rc; - } - --int idbm_add_nodes(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec, -- struct list_head *ifaces) -+static int idbm_bind_iface_to_node(struct node_rec *new_rec, -+ struct iface_rec *iface, -+ struct list_head *bound_recs) -+{ -+ struct node_rec *clone_rec; -+ -+ clone_rec = calloc(1, sizeof(*clone_rec)); -+ if (!clone_rec) -+ return ENOMEM; -+ -+ memcpy(clone_rec, new_rec, sizeof(*clone_rec)); -+ INIT_LIST_HEAD(&clone_rec->list); -+ iface_copy(&clone_rec->iface, iface); -+ list_add_tail(&clone_rec->list, bound_recs); -+ return 0; -+} -+ -+int idbm_bind_ifaces_to_node(struct node_rec *new_rec, struct list_head *ifaces, -+ struct list_head *bound_recs) - { - struct iface_rec *iface, *tmp; - struct iscsi_transport *t; -@@ -2192,25 +1744,82 @@ int idbm_add_nodes(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec, - struct list_head def_ifaces; - - INIT_LIST_HEAD(&def_ifaces); -- idbm_lock(db); -- idbm_read_def_ifaces(&def_ifaces); -- idbm_unlock(db); -+ iface_link_ifaces(&def_ifaces); - - list_for_each_entry_safe(iface, tmp, &def_ifaces, list) { - list_del(&iface->list); - t = get_transport_by_name(iface->transport_name); -- if (!t) { -+ /* only auto bind to software iscsi */ -+ if (!t || t->caps & CAP_FW_DB || -+ t->caps & CAP_DATA_PATH_OFFLOAD) { - free(iface); - continue; - } - -- if (t->caps & CAP_FW_DB) { -+ rc = idbm_bind_iface_to_node(new_rec, iface, -+ bound_recs); -+ free(iface); -+ if (rc) -+ return rc; -+ found = 1; -+ } -+ -+ /* create default iface with old/default behavior */ -+ if (!found) { -+ struct iface_rec def_iface; -+ -+ iface_setup_defaults(&def_iface); -+ return idbm_bind_iface_to_node(new_rec, &def_iface, -+ bound_recs); -+ } -+ } else { -+ list_for_each_entry(iface, ifaces, list) { -+ if (strcmp(iface->name, DEFAULT_IFACENAME) && -+ !iface_is_valid(iface)) { -+ log_error("iface %s is not valid. Will not " -+ "bind node to it. Iface settings " -+ iface_fmt, iface->name, -+ iface_str(iface)); -+ continue; -+ } -+ -+ rc = idbm_bind_iface_to_node(new_rec, iface, -+ bound_recs); -+ if (rc) -+ return rc; -+ } -+ } -+ return 0; -+} -+ -+/* -+ * remove this when isns is converted -+ */ -+int idbm_add_nodes(node_rec_t *newrec, discovery_rec_t *drec, -+ struct list_head *ifaces, int update) -+{ -+ struct iface_rec *iface, *tmp; -+ struct iscsi_transport *t; -+ int rc = 0, found = 0; -+ -+ if (!ifaces || list_empty(ifaces)) { -+ struct list_head def_ifaces; -+ -+ INIT_LIST_HEAD(&def_ifaces); -+ iface_link_ifaces(&def_ifaces); -+ -+ list_for_each_entry_safe(iface, tmp, &def_ifaces, list) { -+ list_del(&iface->list); -+ t = get_transport_by_name(iface->transport_name); -+ /* only auto bind to software iscsi */ -+ if (!t || t->caps & CAP_FW_DB || -+ t->caps & CAP_DATA_PATH_OFFLOAD) { - free(iface); - continue; - } - - iface_copy(&newrec->iface, iface); -- rc = idbm_add_node(db, newrec, drec); -+ rc = idbm_add_node(newrec, drec, update); - free(iface); - if (rc) - return rc; -@@ -2219,13 +1828,22 @@ int idbm_add_nodes(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec, - - /* create default iface with old/default behavior */ - if (!found) { -- iface_init(&newrec->iface); -- return idbm_add_node(db, newrec, drec); -+ iface_setup_defaults(&newrec->iface); -+ return idbm_add_node(newrec, drec, update); - } - } else { - list_for_each_entry(iface, ifaces, list) { -+ if (strcmp(iface->name, DEFAULT_IFACENAME) && -+ !iface_is_valid(iface)) { -+ log_error("iface %s is not valid. Will not " -+ "bind node to it. Iface settings " -+ iface_fmt, iface->name, -+ iface_str(iface)); -+ continue; -+ } -+ - iface_copy(&newrec->iface, iface); -- rc = idbm_add_node(db, newrec, drec); -+ rc = idbm_add_node(newrec, drec, update); - if (rc) - return rc; - } -@@ -2233,14 +1851,7 @@ int idbm_add_nodes(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec, - return 0; - } - --void idbm_new_discovery(idbm_t *db, discovery_rec_t *drec) --{ -- idbm_delete_discovery(db, drec); -- if (idbm_add_discovery(db, drec)) -- log_error("can not update discovery record."); --} -- --static void idbm_rm_disc_node_links(idbm_t *db, char *disc_dir) -+static void idbm_rm_disc_node_links(char *disc_dir) - { - char *target = NULL, *tpgt = NULL, *port = NULL; - char *address = NULL, *iface_id = NULL; -@@ -2279,7 +1890,7 @@ static void idbm_rm_disc_node_links(idbm_t *db, char *disc_dir) - strncpy(rec->conn[0].address, address, NI_MAXHOST); - strncpy(rec->iface.name, iface_id, ISCSI_MAX_IFACE_LEN); - -- if (idbm_delete_node(db, NULL, rec)) -+ if (idbm_delete_node(rec)) - log_error("Could not delete node %s/%s/%s,%s/%s", - NODE_CONFIG_DIR, target, address, port, - iface_id); -@@ -2290,7 +1901,7 @@ free_rec: - free(rec); - } - --int idbm_delete_discovery(idbm_t *db, discovery_rec_t *drec) -+int idbm_delete_discovery(discovery_rec_t *drec) - { - char *portal; - struct stat statb; -@@ -2321,7 +1932,7 @@ int idbm_delete_discovery(idbm_t *db, discovery_rec_t *drec) - memset(portal, 0, PATH_MAX); - snprintf(portal, PATH_MAX, "%s/%s,%d", ST_CONFIG_DIR, - drec->address, drec->port); -- idbm_rm_disc_node_links(db, portal); -+ idbm_rm_disc_node_links(portal); - - /* rm portal dir */ - if (S_ISDIR(statb.st_mode)) { -@@ -2341,7 +1952,7 @@ free_portal: - * if there is no link then this is pre svn 780 version where - * we did not link the disc source and node - */ --static int idbm_remove_disc_to_node_link(idbm_t *db, node_rec_t *rec, -+static int idbm_remove_disc_to_node_link(node_rec_t *rec, - char *portal) - { - int rc = 0; -@@ -2357,7 +1968,7 @@ static int idbm_remove_disc_to_node_link(idbm_t *db, node_rec_t *rec, - rec->name, rec->conn[0].address, rec->conn[0].port, rec->tpgt, - rec->iface.name); - -- rc = __idbm_rec_read(db, tmprec, portal); -+ rc = __idbm_rec_read(tmprec, portal); - if (rc) { - /* old style recs will not have tpgt or a link so skip */ - rc = 0; -@@ -2372,7 +1983,7 @@ static int idbm_remove_disc_to_node_link(idbm_t *db, node_rec_t *rec, - if (rc) - goto done; - -- idbm_lock(db); -+ idbm_lock(); - if (!stat(portal, &statb)) { - if (unlink(portal)) { - log_error("Could not remove link %s err %d\n", -@@ -2382,14 +1993,14 @@ static int idbm_remove_disc_to_node_link(idbm_t *db, node_rec_t *rec, - log_debug(7, "rmd %s", portal); - } else - log_debug(7, "Could not stat %s", portal); -- idbm_unlock(db); -+ idbm_unlock(); - - done: - free(tmprec); - return rc; - } - --int idbm_delete_node(idbm_t *db, void *data, node_rec_t *rec) -+int idbm_delete_node(node_rec_t *rec) - { - struct stat statb; - char *portal; -@@ -2399,7 +2010,7 @@ int idbm_delete_node(idbm_t *db, void *data, node_rec_t *rec) - if (!portal) - return ENOMEM; - -- rc = idbm_remove_disc_to_node_link(db, rec, portal); -+ rc = idbm_remove_disc_to_node_link(rec, portal); - if (rc) - goto free_portal; - -@@ -2409,7 +2020,7 @@ int idbm_delete_node(idbm_t *db, void *data, node_rec_t *rec) - log_debug(5, "Removing config file %s iface id %s\n", - portal, rec->iface.name); - -- idbm_lock(db); -+ idbm_lock(); - if (!stat(portal, &statb)) - goto rm_conf; - -@@ -2445,7 +2056,7 @@ rm_conf: - snprintf(portal, PATH_MAX, "%s/%s/%s,%d,%d", NODE_CONFIG_DIR, - rec->name, rec->conn[0].address, rec->conn[0].port, - rec->tpgt); -- n = scandir(portal, &namelist, st_disc_filter, versionsort); -+ n = scandir(portal, &namelist, st_disc_filter, direntcmp); - if (n < 0) - goto free_portal; - if (n == 0) -@@ -2462,29 +2073,28 @@ rm_conf: - rmdir(portal); - } - unlock: -- idbm_unlock(db); -+ idbm_unlock(); - free_portal: - free(portal); - return rc; - } - - void --idbm_sendtargets_defaults(idbm_t *db, struct iscsi_sendtargets_config *cfg) -+idbm_sendtargets_defaults(struct iscsi_sendtargets_config *cfg) - { -- idbm_sync_config(db); -+ idbm_sync_config(); - memcpy(cfg, &db->drec_st.u.sendtargets, - sizeof(struct iscsi_sendtargets_config)); - } - - void --idbm_slp_defaults(idbm_t *db, struct iscsi_slp_config *cfg) -+idbm_slp_defaults(struct iscsi_slp_config *cfg) - { - memcpy(cfg, &db->drec_slp.u.slp, - sizeof(struct iscsi_slp_config)); - } - --int --idbm_node_set_param(idbm_t *db, void *data, node_rec_t *rec) -+int idbm_node_set_param(void *data, node_rec_t *rec) - { - struct db_set_param *param = data; - recinfo_t *info; -@@ -2496,51 +2106,46 @@ idbm_node_set_param(idbm_t *db, void *data, node_rec_t *rec) - - idbm_recinfo_node(rec, info); - -- /* -- * Another compat hack!!!!: in the future we will have a common -- * way to define node wide vs iface wide values and it will -- * nicely obey some hierd, but for now this one sits between both -- * and if someone tries to set it using the old values then -- * we update it for them. -- */ -- if (!strcmp("node.transport_name", param->name)) -- rc = idbm_node_update_param(info, "iface.transport_name", -- param->value, 0); -- else -- rc = idbm_node_update_param(info, param->name, param->value, 0); -- if (rc) { -- free(info); -- return EIO; -- } -+ rc = idbm_verify_param(info, param->name); -+ if (rc) -+ goto free_info; - -- rc = idbm_rec_write(param->db, rec); -- if (rc) { -- free(info); -- return rc; -- } -+ rc = idbm_rec_update_param(info, param->name, param->value, 0); -+ if (rc) -+ goto free_info; - -+ rc = idbm_rec_write(rec); -+ if (rc) -+ goto free_info; -+ -+free_info: - free(info); -- return 0; -+ return rc; - } - --idbm_t* --idbm_init(char *configfile) -+int idbm_init(idbm_get_config_file_fn *fn) - { -- idbm_t *db; -+ /* make sure root db dir is there */ -+ if (access(ISCSI_CONFIG_ROOT, F_OK) != 0) { -+ if (mkdir(ISCSI_CONFIG_ROOT, 0660) != 0) { -+ log_error("Could not make %s %d\n", ISCSI_CONFIG_ROOT, -+ errno); -+ return errno; -+ } -+ } - - db = malloc(sizeof(idbm_t)); - if (!db) { - log_error("out of memory on idbm allocation"); -- return NULL; -+ return ENOMEM; - } - memset(db, 0, sizeof(idbm_t)); -- db->configfile = strdup(configfile); -- return db; -+ db->get_config_file = fn; -+ return 0; - } - --void --idbm_terminate(idbm_t *db) -+void idbm_terminate(void) - { -- free(db->configfile); -- free(db); -+ if (db) -+ free(db); - } -diff --git a/usr/idbm.h b/usr/idbm.h -index 5d809f2..edd85ca 100644 ---- a/usr/idbm.h -+++ b/usr/idbm.h -@@ -27,7 +27,6 @@ - #include "config.h" - - #define NODE_CONFIG_DIR ISCSI_CONFIG_ROOT"nodes" --#define IFACE_CONFIG_DIR ISCSI_CONFIG_ROOT"ifaces" - #define SLP_CONFIG_DIR ISCSI_CONFIG_ROOT"slp" - #define ISNS_CONFIG_DIR ISCSI_CONFIG_ROOT"isns" - #define STATIC_CONFIG_DIR ISCSI_CONFIG_ROOT"static" -@@ -50,13 +49,22 @@ typedef struct recinfo { - int visible; - char* opts[OPTS_MAXVAL]; - int numopts; -+ /* -+ * bool indicating if we can change it or not. -+ * TODO: make it a enum that can indicate wheter it also requires -+ * a relogin to pick up if a session is running. -+ */ -+ int can_modify; - } recinfo_t; - -+typedef char *(idbm_get_config_file_fn)(void); -+ - typedef struct idbm { - void *discdb; - void *nodedb; - char *configfile; - int refs; -+ idbm_get_config_file_fn *get_config_file; - node_rec_t nrec; - recinfo_t ninfo[MAX_KEYS]; - discovery_rec_t drec_st; -@@ -70,13 +78,12 @@ typedef struct idbm { - struct db_set_param { - char *name; - char *value; -- struct idbm *db; - }; - --typedef int (idbm_iface_op_fn)(idbm_t *db, void *data, node_rec_t *rec); --typedef int (idbm_portal_op_fn)(idbm_t *db,int *found, void *data, -+typedef int (idbm_iface_op_fn)(void *data, node_rec_t *rec); -+typedef int (idbm_portal_op_fn)(int *found, void *data, - char *targetname, int tpgt, char *ip, int port); --typedef int (idbm_node_op_fn)(idbm_t *db, int *found, void *data, -+typedef int (idbm_node_op_fn)(int *found, void *data, - char *targetname); - - struct rec_op_data { -@@ -84,73 +91,64 @@ struct rec_op_data { - node_rec_t *match_rec; - idbm_iface_op_fn *fn; - }; --extern int idbm_for_each_portal(idbm_t *db, int *found, void *data, -+extern int idbm_for_each_portal(int *found, void *data, - idbm_portal_op_fn *fn, char *targetname); --extern int idbm_for_each_node(idbm_t *db, int *found, void *data, -+extern int idbm_for_each_node(int *found, void *data, - idbm_node_op_fn *fn); --extern int idbm_for_each_rec(idbm_t *db, int *found, void *data, -+extern int idbm_for_each_rec(int *found, void *data, - idbm_iface_op_fn *fn); - - extern char* get_iscsi_initiatorname(char *pathname); - extern char* get_iscsi_initiatoralias(char *pathname); --extern idbm_t* idbm_init(char *configfile); --extern void idbm_node_setup_from_conf(idbm_t *db, node_rec_t *rec); --extern void idbm_terminate(idbm_t *db); --extern int idbm_print_node_info(idbm_t *db, void *data, node_rec_t *rec); --extern int idbm_print_node_flat(idbm_t *db, void *data, node_rec_t *rec); --extern int idbm_print_node_tree(idbm_t *db, void *data, node_rec_t *rec); --extern int idbm_print_discovery_info(idbm_t *db, discovery_rec_t *rec, -- int show); --extern int idbm_print_all_discovery(idbm_t *db, int info_level); --extern int idbm_print_discovered(idbm_t *db, discovery_rec_t *drec, -- int info_level); --extern int idbm_delete_discovery(idbm_t *db, discovery_rec_t *rec); --extern void idbm_node_setup_defaults(node_rec_t *rec); --extern int idbm_delete_node(idbm_t *db, void *data, node_rec_t *rec); --extern int idbm_add_node(idbm_t *db, node_rec_t *newrec, discovery_rec_t *drec); -+extern int idbm_init(idbm_get_config_file_fn *fn); - -+extern void idbm_node_setup_from_conf(node_rec_t *rec); -+extern void idbm_terminate(void); -+extern int idbm_print_iface_info(void *data, struct iface_rec *iface); -+extern int idbm_print_node_info(void *data, node_rec_t *rec); -+extern int idbm_print_node_flat(void *data, node_rec_t *rec); -+extern int idbm_print_node_tree(void *data, node_rec_t *rec); -+extern int idbm_print_discovery_info(discovery_rec_t *rec, int show); -+extern int idbm_print_all_discovery(int info_level); -+extern int idbm_print_discovered(discovery_rec_t *drec, int info_level); -+extern int idbm_delete_discovery(discovery_rec_t *rec); -+extern void idbm_node_setup_defaults(node_rec_t *rec); -+extern int idbm_delete_node(node_rec_t *rec); -+extern int idbm_add_node(node_rec_t *newrec, discovery_rec_t *drec, -+ int overwrite); - struct list_head; --extern int idbm_add_nodes(idbm_t *db, node_rec_t *newrec, -- discovery_rec_t *drec, struct list_head *ifaces); --extern void idbm_new_discovery(idbm_t *db, discovery_rec_t *drec); --extern void idbm_sendtargets_defaults(idbm_t *db, -- struct iscsi_sendtargets_config *cfg); --extern void idbm_slp_defaults(idbm_t *db, struct iscsi_slp_config *cfg); --extern int idbm_discovery_read(idbm_t *db, discovery_rec_t *rec, char *addr, -+extern int idbm_bind_ifaces_to_node(struct node_rec *new_rec, -+ struct list_head *ifaces, -+ struct list_head *bound_recs); -+extern int idbm_add_nodes(node_rec_t *newrec, -+ discovery_rec_t *drec, struct list_head *ifaces, -+ int overwrite); -+extern int idbm_add_discovery(discovery_rec_t *newrec, int overwrite); -+extern void idbm_sendtargets_defaults(struct iscsi_sendtargets_config *cfg); -+extern void idbm_slp_defaults(struct iscsi_slp_config *cfg); -+extern int idbm_discovery_read(discovery_rec_t *rec, char *addr, - int port); --extern int idbm_rec_read(idbm_t *db, node_rec_t *out_rec, char *target_name, -+extern int idbm_rec_read(node_rec_t *out_rec, char *target_name, - int tpgt, char *addr, int port, - struct iface_rec *iface); --extern int idbm_node_set_param(idbm_t *db, void *data, node_rec_t *rec); -+extern int idbm_node_set_param(void *data, node_rec_t *rec); - --/* TODO: seperate iface, node and core idbm code */ --extern int iface_id_is_mac(char *iface_id); --extern void iface_copy(struct iface_rec *dst, struct iface_rec *src); --extern int iface_is_bound(struct iface_rec *iface); --extern int iface_match_bind_info(struct iface_rec *pattern, -- struct iface_rec *iface); --extern struct iface_rec *iface_alloc(char *ifname, int *err); --extern int iface_conf_read(struct iface_rec *iface); --extern void iface_init(struct iface_rec *iface); --extern int iface_is_bound_by_hwaddr(struct iface_rec *iface); --extern int iface_is_bound_by_netdev(struct iface_rec *iface); --extern int iface_is_bound_by_ipaddr(struct iface_rec *iface); --typedef int (iface_op_fn)(void *data, struct iface_rec *iface); --extern int iface_for_each_iface(idbm_t *db, void *data, int *nr_found, -- iface_op_fn *fn); --extern int iface_print_flat(void *data, struct iface_rec *iface); --extern void iface_setup_host_bindings(idbm_t *db); --extern int iface_get_by_bind_info(idbm_t *db, struct iface_rec *pattern, -- struct iface_rec *out_rec); --extern int iface_conf_update(struct db_set_param *set_param, -- struct iface_rec *iface); --extern int iface_conf_write(struct iface_rec *iface); --extern int iface_conf_delete(struct iface_rec *iface); --extern int iface_conf_read(struct iface_rec *iface); -+/* lower level idbm functions for use by iface.c */ -+extern void idbm_recinfo_config(recinfo_t *info, FILE *f); -+extern void idbm_recinfo_iface(struct iface_rec *r, recinfo_t *ri); -+extern int idbm_lock(void); -+extern void idbm_unlock(void); -+extern recinfo_t *idbm_recinfo_alloc(int max_keys); -+extern int idbm_verify_param(recinfo_t *info, char *name); -+extern int idbm_rec_update_param(recinfo_t *info, char *name, char *value, -+ int line_number); -+ -+enum { -+ IDBM_PRINT_TYPE_DISCOVERY, -+ IDBM_PRINT_TYPE_NODE, -+ IDBM_PRINT_TYPE_IFACE, -+}; - --#define iface_fmt "[hw=%s,ip=%s,net_if=%s,iscsi_if=%s]" --#define iface_str(_iface) \ -- (_iface)->hwaddress, (_iface)->ipaddress, (_iface)->netdev, \ -- (_iface)->name -+extern void idbm_print(int type, void *rec, int show, FILE *f); - - #endif /* IDBM_H */ -diff --git a/usr/iface.c b/usr/iface.c -new file mode 100644 -index 0000000..c23e5d8 ---- /dev/null -+++ b/usr/iface.c -@@ -0,0 +1,688 @@ -+/* -+ * iSCSI iface helpers -+ * -+ * Copyright (C) 2008 Mike Christie -+ * Copyright (C) 2008 Red Hat, Inc. All rights reserved. -+ * maintained by open-iscsi@@googlegroups.com -+ * -+ * 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. -+ * -+ * See the file COPYING included with this distribution for more details. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "log.h" -+#include "list.h" -+#include "iscsi_sysfs.h" -+#include "iscsi_settings.h" -+#include "config.h" -+#include "transport.h" -+#include "idbm.h" -+#include "iface.h" -+ -+/* -+ * Default ifaces for use with transports that do not bind to hardware -+ * by defaults (transports that let the interconnect layer to the routing -+ * by defaults). -+ */ -+ -+/* -+ * iSCSI over TCP/IP -+ */ -+static struct iface_rec iface_default = { -+ .name = "default", -+ .transport_name = "tcp", -+ .netdev = DEFAULT_NETDEV, -+ .hwaddress = DEFAULT_HWADDRESS, -+}; -+ -+/* -+ * iSER -+ */ -+static struct iface_rec iface_iser = { -+ .name = "iser", -+ .transport_name = "iser", -+ .netdev = DEFAULT_NETDEV, -+ .hwaddress = DEFAULT_HWADDRESS, -+}; -+ -+/* -+ * Broadcom bnx2i -+ */ -+static struct iface_rec iface_bnx2i = { -+ .name = "bnx2i", -+ .transport_name = "bnx2i", -+ .netdev = DEFAULT_NETDEV, -+ .hwaddress = DEFAULT_HWADDRESS, -+}; -+ -+static struct iface_rec *default_ifaces[] = { -+ &iface_default, -+ &iface_iser, -+ &iface_bnx2i, -+ NULL, -+}; -+ -+static struct iface_rec *iface_match_default(struct iface_rec *iface) -+{ -+ struct iface_rec *def_iface; -+ int i = 0; -+ -+ while ((def_iface = default_ifaces[i++])) { -+ if (!strcmp(iface->name, def_iface->name)) -+ return def_iface; -+ } -+ return NULL; -+} -+ -+static void iface_init(struct iface_rec *iface) -+{ -+ if (!strlen(iface->name)) -+ sprintf(iface->name, DEFAULT_IFACENAME); -+} -+ -+/* -+ * default is to use tcp through whatever the network layer -+ * selects for us with the /etc/iscsi/initiatorname.iscsi iname. -+ */ -+void iface_setup_defaults(struct iface_rec *iface) -+{ -+ sprintf(iface->netdev, DEFAULT_NETDEV); -+// sprintf(iface->ipaddress, DEFAULT_IPADDRESS); -+ sprintf(iface->hwaddress, DEFAULT_HWADDRESS); -+ sprintf(iface->transport_name, DEFAULT_TRANSPORT); -+ iface_init(iface); -+} -+ -+struct iface_rec *iface_alloc(char *ifname, int *err) -+{ -+ struct iface_rec *iface; -+ -+ if (!strlen(ifname) || strlen(ifname) + 1 > ISCSI_MAX_IFACE_LEN) { -+ *err = EINVAL; -+ return NULL; -+ } -+ -+ iface = calloc(1, sizeof(*iface)); -+ if (!iface) { -+ *err = ENOMEM; -+ return NULL; -+ } -+ -+ strncpy(iface->name, ifname, ISCSI_MAX_IFACE_LEN); -+ INIT_LIST_HEAD(&iface->list); -+ return iface; -+} -+ -+static int __iface_conf_read(struct iface_rec *iface) -+{ -+ char *iface_conf; -+ recinfo_t *info; -+ FILE *f; -+ int rc = 0; -+ -+ iface_conf = calloc(1, PATH_MAX); -+ if (!iface_conf) -+ return ENOMEM; -+ -+ info = idbm_recinfo_alloc(MAX_KEYS); -+ if (!info) { -+ rc = ENOMEM; -+ goto free_conf; -+ } -+ -+ snprintf(iface_conf, PATH_MAX, "%s/%s", IFACE_CONFIG_DIR, -+ iface->name); -+ -+ log_debug(5, "looking for iface conf %s", iface_conf); -+ f = fopen(iface_conf, "r"); -+ if (!f) { -+ /* -+ * if someone passes in default but has not defined -+ * a iface with default then we do it for them -+ */ -+ if (!strcmp(iface->name, DEFAULT_IFACENAME)) { -+ iface_setup_defaults(iface); -+ rc = 0; -+ } else -+ rc = errno; -+ goto free_info; -+ } -+ -+ iface_init(iface); -+ idbm_recinfo_iface(iface, info); -+ idbm_recinfo_config(info, f); -+ fclose(f); -+ -+free_info: -+ free(info); -+free_conf: -+ free(iface_conf); -+ return rc; -+} -+ -+int iface_conf_read(struct iface_rec *iface) -+{ -+ struct iface_rec *def_iface; -+ int rc; -+ -+ def_iface = iface_match_default(iface); -+ if (def_iface) { -+ iface_init(iface); -+ iface_copy(iface, def_iface); -+ return 0; -+ } -+ -+ idbm_lock(); -+ rc = __iface_conf_read(iface); -+ idbm_unlock(); -+ return rc; -+} -+ -+int iface_conf_delete(struct iface_rec *iface) -+{ -+ struct iface_rec *def_iface; -+ char *iface_conf; -+ int rc = 0; -+ -+ def_iface = iface_match_default(iface); -+ if (def_iface) { -+ log_error("iface %s is a special interface and " -+ "cannot be deleted.\n", iface->name); -+ return EINVAL; -+ } -+ -+ iface_conf = calloc(1, PATH_MAX); -+ if (!iface_conf) -+ return ENOMEM; -+ -+ sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name); -+ idbm_lock(); -+ if (unlink(iface_conf)) -+ rc = errno; -+ idbm_unlock(); -+ -+ free(iface_conf); -+ return rc; -+} -+ -+int iface_conf_write(struct iface_rec *iface) -+{ -+ struct iface_rec *def_iface; -+ char *iface_conf; -+ FILE *f; -+ int rc = 0; -+ -+ def_iface = iface_match_default(iface); -+ if (def_iface) { -+ log_error("iface %s is a special interface and " -+ "is not stored in %s.\n", iface->name, -+ IFACE_CONFIG_DIR); -+ return EINVAL; -+ } -+ -+ iface_conf = calloc(1, PATH_MAX); -+ if (!iface_conf) -+ return ENOMEM; -+ -+ sprintf(iface_conf, "%s/%s", IFACE_CONFIG_DIR, iface->name); -+ f = fopen(iface_conf, "w"); -+ if (!f) { -+ rc = errno; -+ goto free_conf; -+ } -+ -+ idbm_lock(); -+ idbm_print(IDBM_PRINT_TYPE_IFACE, iface, 1, f); -+ idbm_unlock(); -+ -+ fclose(f); -+free_conf: -+ free(iface_conf); -+ return rc; -+} -+ -+int iface_conf_update(struct db_set_param *param, -+ struct iface_rec *iface) -+{ -+ struct iface_rec *def_iface; -+ recinfo_t *info; -+ int rc = 0; -+ -+ def_iface = iface_match_default(iface); -+ if (def_iface) { -+ log_error("iface %s is a special interface and " -+ "cannot be modified.\n", iface->name); -+ return EINVAL; -+ } -+ -+ info = idbm_recinfo_alloc(MAX_KEYS); -+ if (!info) -+ return ENOMEM; -+ -+ idbm_recinfo_iface(iface, info); -+ rc = idbm_verify_param(info, param->name); -+ if (rc) -+ goto free_info; -+ -+ rc = idbm_rec_update_param(info, param->name, param->value, 0); -+ if (rc) { -+ rc = EIO; -+ goto free_info; -+ } -+ -+ rc = iface_conf_write(iface); -+free_info: -+ free(info); -+ return rc; -+} -+ -+static int iface_get_next_id(void) -+{ -+ struct stat statb; -+ char *iface_conf; -+ int i, rc = ENOSPC; -+ -+ iface_conf = calloc(1, PATH_MAX); -+ if (!iface_conf) -+ return ENOMEM; -+ -+ for (i = 0; i < INT_MAX; i++) { -+ memset(iface_conf, 0, PATH_MAX); -+ /* check len */ -+ snprintf(iface_conf, PATH_MAX, "iface%d", i); -+ if (strlen(iface_conf) > ISCSI_MAX_IFACE_LEN - 1) { -+ log_error("iface namespace is full. Remove unused " -+ "iface definitions from %s or send mail " -+ "to open-iscsi@googlegroups.com to report " -+ "the problem", IFACE_CONFIG_DIR); -+ rc = ENOSPC; -+ break; -+ } -+ memset(iface_conf, 0, PATH_MAX); -+ snprintf(iface_conf, PATH_MAX, "%s/iface%d", IFACE_CONFIG_DIR, -+ i); -+ -+ if (!stat(iface_conf, &statb)) -+ continue; -+ if (errno == ENOENT) { -+ rc = i; -+ break; -+ } -+ } -+ -+ free(iface_conf); -+ return rc; -+} -+ -+struct iface_search { -+ struct iface_rec *pattern; -+ struct iface_rec *found; -+}; -+ -+static int __iface_get_by_net_binding(void *data, struct iface_rec *iface) -+{ -+ struct iface_search *search = data; -+ -+ if (!strcmp(search->pattern->name, iface->name)) { -+ iface_copy(search->found, iface); -+ return 1; -+ } -+ -+ if (iface_is_bound_by_hwaddr(search->pattern)) { -+ if (!strcmp(iface->hwaddress, search->pattern->hwaddress)) { -+ iface_copy(search->found, iface); -+ return 1; -+ } else -+ return 0; -+ } -+ -+ if (iface_is_bound_by_netdev(search->pattern)) { -+ if (!strcmp(iface->netdev, search->pattern->netdev)) { -+ iface_copy(search->found, iface); -+ return 1; -+ } else -+ return 0; -+ } -+ -+/* -+ if (iface_is_bound_by_ipaddr(search->pattern)) { -+ if (!strcmp(iface->ipaddress, search->pattern->ipaddress)) { -+ iface_copy(search->found, iface); -+ return 1; -+ } else -+ return 0; -+ } -+*/ -+ return 0; -+} -+ -+/* -+ * Before 2.0.870, we only could bind by netdeivce or hwaddress, -+ * so we did a simple reverse lookup to go from sysfs info to -+ * the iface name. After 2.0.870 we added a lot of options to the -+ * iface binding so we added the ifacename to the kernel. -+ * -+ * This function is for older kernels that do not export the ifacename. -+ * If the user was doing iscsi_tcp session binding or using qla4xxx -+ * we will find the iface by matching the hwaddres or netdev. -+ */ -+int iface_get_by_net_binding(struct iface_rec *pattern, -+ struct iface_rec *out_rec) -+{ -+ int num_found = 0, rc; -+ struct iface_search search; -+ -+ search.pattern = pattern; -+ search.found = out_rec; -+ -+ rc = iface_for_each_iface(&search, &num_found, -+ __iface_get_by_net_binding); -+ if (rc == 1) -+ return 0; -+ -+ if (iface_is_bound_by_hwaddr(pattern) || -+ iface_is_bound_by_netdev(pattern)) -+ return ENODEV; -+ -+ /* -+ * compat for default behavior -+ */ -+ if (!strlen(pattern->name) || -+ !strcmp(pattern->name, DEFAULT_IFACENAME)) { -+ iface_setup_defaults(out_rec); -+ return 0; -+ } -+ -+ return ENODEV; -+} -+ -+static int __iface_setup_host_bindings(void *data, struct host_info *info) -+{ -+ struct iface_rec iface; -+ struct iscsi_transport *t; -+ int id; -+ -+ t = get_transport_by_hba(info->host_no); -+ if (!t) -+ return 0; -+ /* -+ * if software or partial offload do not touch the bindngs. -+ * They do not need it and may not support it -+ */ -+ if (!(t->caps & CAP_FW_DB)) -+ return 0; -+ -+ /* -+ * since this is only for qla4xxx we only care about finding -+ * a iface with a matching hwaddress. -+ */ -+ if (iface_get_by_net_binding(&info->iface, &iface)) { -+ /* Must be a new port */ -+ id = iface_get_next_id(); -+ if (id < 0) { -+ log_error("Could not add iface for %s.", -+ info->iface.hwaddress); -+ return 0; -+ } -+ memset(&iface, 0, sizeof(struct iface_rec)); -+ strcpy(iface.hwaddress, info->iface.hwaddress); -+ strcpy(iface.transport_name, info->iface.transport_name); -+ sprintf(iface.name, "iface%d", id); -+ if (iface_conf_write(&iface)) -+ log_error("Could not write iface conf for %s %s", -+ iface.name, iface.hwaddress); -+ /* fall through - will not be persistent */ -+ } -+ return 0; -+} -+ -+/* -+ * sync hw/offload iscsi scsi_hosts with iface values -+ */ -+void iface_setup_host_bindings(void) -+{ -+ int nr_found = 0; -+ -+ idbm_lock(); -+ if (access(IFACE_CONFIG_DIR, F_OK) != 0) { -+ if (mkdir(IFACE_CONFIG_DIR, 0660) != 0) { -+ log_error("Could not make %s. HW/OFFLOAD iscsi " -+ "may not be supported", IFACE_CONFIG_DIR); -+ idbm_unlock(); -+ return; -+ } -+ } -+ idbm_unlock(); -+ -+ if (sysfs_for_each_host(NULL, &nr_found, __iface_setup_host_bindings)) -+ log_error("Could not scan scsi hosts. HW/OFFLOAD iscsi " -+ "operations may not be supported."); -+} -+ -+void iface_copy(struct iface_rec *dst, struct iface_rec *src) -+{ -+ if (strlen(src->name)) -+ strcpy(dst->name, src->name); -+ if (strlen(src->netdev)) -+ strcpy(dst->netdev, src->netdev); -+// if (strlen(src->ipaddress)) -+// strcpy(dst->ipaddress, src->ipaddress); -+ if (strlen(src->hwaddress)) -+ strcpy(dst->hwaddress, src->hwaddress); -+ if (strlen(src->transport_name)) -+ strcpy(dst->transport_name, src->transport_name); -+ if (strlen(src->iname)) -+ strcpy(dst->iname, src->iname); -+} -+ -+int iface_is_valid(struct iface_rec *iface) -+{ -+ if (!iface) -+ return 0; -+ -+ if (!strlen(iface->name)) -+ return 0; -+ -+ if (!strlen(iface->transport_name)) -+ return 0; -+ -+ if (iface_is_bound_by_hwaddr(iface)) -+ return 1; -+ -+ if (iface_is_bound_by_netdev(iface)) -+ return 1; -+// if (iface_is_bound_by_ipaddr(iface)) -+// return 1; -+ -+ /* bound by transport name */ -+ return 1; -+} -+ -+int iface_match(struct iface_rec *pattern, struct iface_rec *iface) -+{ -+ if (!pattern || !iface) -+ return 1; -+ -+ if (!strlen(pattern->name)) -+ return 1; -+ -+ if (!strcmp(pattern->name, iface->name)) -+ return 1; -+ -+ return 0; -+} -+ -+int iface_is_bound_by_hwaddr(struct iface_rec *iface) -+{ -+ if (iface && strlen(iface->hwaddress) && -+ strcmp(iface->hwaddress, DEFAULT_HWADDRESS)) -+ return 1; -+ return 0; -+} -+ -+int iface_is_bound_by_netdev(struct iface_rec *iface) -+{ -+ if (iface && strlen(iface->netdev) && -+ strcmp(iface->netdev, DEFAULT_NETDEV)) -+ return 1; -+ return 0; -+} -+ -+int iface_is_bound_by_ipaddr(struct iface_rec *iface) -+{ -+ return 0; -+/* if (iface && strlen(iface->ipaddress) && -+ strcmp(iface->ipaddress, DEFAULT_NETDEV)) -+ return 1; -+ return 0; -+*/ -+} -+ -+/** -+ * iface_print_node_tree - print out binding info -+ * @iface: iface to print out -+ * -+ * Currently this looks like the iface conf print, because we only -+ * have the binding info. When we store the iface specific node settings -+ * in the iface record then it will look different. -+ */ -+int iface_print_tree(void *data, struct iface_rec *iface) -+{ -+ printf("Name: %s\n", iface->name); -+ printf("\tTransport Name: %s\n", -+ strlen(iface->transport_name) ? iface->transport_name : -+ UNKNOWN_VALUE); -+ printf("\tHW Address: %s\n", -+ strlen(iface->hwaddress) ? iface->hwaddress : UNKNOWN_VALUE); -+ printf("\tNetdev: %s\n", -+ strlen(iface->netdev) ? iface->netdev : UNKNOWN_VALUE); -+ printf("\tInitiator Name: %s\n", -+ strlen(iface->iname) ? iface->iname : UNKNOWN_VALUE); -+ return 0; -+} -+ -+int iface_print_flat(void *data, struct iface_rec *iface) -+{ -+ printf("%s %s,%s,%s,%s\n", -+ strlen(iface->name) ? iface->name : UNKNOWN_VALUE, -+ strlen(iface->transport_name) ? iface->transport_name : -+ UNKNOWN_VALUE, -+ strlen(iface->hwaddress) ? iface->hwaddress : UNKNOWN_VALUE, -+ strlen(iface->netdev) ? iface->netdev : UNKNOWN_VALUE, -+ strlen(iface->iname) ? iface->iname : UNKNOWN_VALUE); -+ return 0; -+} -+ -+int iface_for_each_iface(void *data, int *nr_found, iface_op_fn *fn) -+{ -+ DIR *iface_dirfd; -+ struct dirent *iface_dent; -+ struct iface_rec *iface, *def_iface; -+ int err = 0, i = 0; -+ -+ while ((def_iface = default_ifaces[i++])) { -+ iface = iface_alloc(def_iface->name, &err); -+ if (!iface) { -+ log_error("Could not add iface %s.", def_iface->name); -+ continue; -+ } -+ iface_copy(iface, def_iface); -+ err = fn(data, iface); -+ free(iface); -+ if (err) -+ return err; -+ (*nr_found)++; -+ } -+ -+ iface_dirfd = opendir(IFACE_CONFIG_DIR); -+ if (!iface_dirfd) -+ return errno; -+ -+ while ((iface_dent = readdir(iface_dirfd))) { -+ if (!strcmp(iface_dent->d_name, ".") || -+ !strcmp(iface_dent->d_name, "..")) -+ continue; -+ -+ log_debug(5, "iface_for_each_iface found %s", -+ iface_dent->d_name); -+ iface = iface_alloc(iface_dent->d_name, &err); -+ if (!iface || err) { -+ if (err == EINVAL) -+ log_error("Invalid iface name %s. Must be " -+ "from 1 to %d characters.", -+ iface_dent->d_name, -+ ISCSI_MAX_IFACE_LEN - 1); -+ else -+ log_error("Could not add iface %s.", -+ iface_dent->d_name); -+ continue; -+ } -+ -+ idbm_lock(); -+ err = __iface_conf_read(iface); -+ idbm_unlock(); -+ if (err) { -+ log_error("Could not read def iface %s (err %d)", -+ iface->name, err); -+ free(iface); -+ continue; -+ } -+ -+ if (!iface_is_valid(iface)) { -+ log_debug(5, "iface is not valid " -+ "Iface settings " iface_fmt, -+ iface_str(iface)); -+ free(iface); -+ continue; -+ } -+ -+ err = fn(data, iface); -+ free(iface); -+ if (err) -+ break; -+ (*nr_found)++; -+ } -+ -+ closedir(iface_dirfd); -+ return err; -+} -+ -+static int iface_link(void *data, struct iface_rec *iface) -+{ -+ struct list_head *ifaces = data; -+ struct iface_rec *iface_copy; -+ -+ iface_copy = calloc(1, sizeof(*iface_copy)); -+ if (!iface_copy) -+ return ENOMEM; -+ -+ memcpy(iface_copy, iface, sizeof(*iface_copy)); -+ INIT_LIST_HEAD(&iface_copy->list); -+ list_add_tail(&iface_copy->list, ifaces); -+ return 0; -+} -+ -+void iface_link_ifaces(struct list_head *ifaces) -+{ -+ int nr_found = 0; -+ -+ iface_for_each_iface(ifaces, &nr_found, iface_link); -+} -+ -+ -diff --git a/usr/iface.h b/usr/iface.h -new file mode 100644 -index 0000000..6da7d45 ---- /dev/null -+++ b/usr/iface.h -@@ -0,0 +1,56 @@ -+/* -+ * iSCSI iface helpers -+ * -+ * Copyright (C) 2008 Mike Christie -+ * Copyright (C) 2008 Red Hat, Inc. All rights reserved. -+ * maintained by open-iscsi@@googlegroups.com -+ * -+ * 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. -+ * -+ * See the file COPYING included with this distribution for more details. -+ */ -+#ifndef ISCSI_IFACE_H -+#define ISCSI_IFACE_H -+ -+#define IFACE_CONFIG_DIR ISCSI_CONFIG_ROOT"ifaces" -+ -+struct iface_rec; -+struct list_head; -+ -+extern void iface_copy(struct iface_rec *dst, struct iface_rec *src); -+extern int iface_match(struct iface_rec *pattern, struct iface_rec *iface); -+extern struct iface_rec *iface_alloc(char *ifname, int *err); -+extern int iface_conf_read(struct iface_rec *iface); -+extern void iface_setup_defaults(struct iface_rec *iface); -+extern int iface_is_bound_by_hwaddr(struct iface_rec *iface); -+extern int iface_is_bound_by_netdev(struct iface_rec *iface); -+extern int iface_is_bound_by_ipaddr(struct iface_rec *iface); -+typedef int (iface_op_fn)(void *data, struct iface_rec *iface); -+extern int iface_for_each_iface(void *data, int *nr_found, -+ iface_op_fn *fn); -+extern int iface_print_flat(void *data, struct iface_rec *iface); -+extern int iface_print_tree(void *data, struct iface_rec *iface); -+extern void iface_setup_host_bindings(void); -+extern int iface_get_by_net_binding(struct iface_rec *pattern, -+ struct iface_rec *out_rec); -+extern int iface_conf_update(struct db_set_param *set_param, -+ struct iface_rec *iface); -+extern int iface_conf_write(struct iface_rec *iface); -+extern int iface_conf_delete(struct iface_rec *iface); -+extern int iface_is_valid(struct iface_rec *iface); -+extern void iface_link_ifaces(struct list_head *ifaces); -+ -+#define iface_fmt "[hw=%s,ip=%s,net_if=%s,iscsi_if=%s]" -+#define iface_str(_iface) \ -+ (_iface)->hwaddress, (_iface)->ipaddress, (_iface)->netdev, \ -+ (_iface)->name -+ -+#endif -diff --git a/usr/initiator.c b/usr/initiator.c -index 4831ad8..96f4693 100644 ---- a/usr/initiator.c -+++ b/usr/initiator.c -@@ -20,20 +20,10 @@ - */ - - #include --#include - #include - #include - #include --#include - #include --#include --#include --#include --#include --#include --#include --#include --#include - - #include "initiator.h" - #include "transport.h" -@@ -44,13 +34,14 @@ - #include "idbm.h" - #include "log.h" - #include "util.h" -+#include "scsi.h" - #include "iscsi_sysfs.h" - #include "iscsi_settings.h" - --static void __session_mainloop(void *data); --static void __conn_error_handle(iscsi_session_t*, iscsi_conn_t*); -+#define ISCSI_CONN_ERR_REOPEN_DELAY 3 -+#define ISCSI_INTERNAL_ERR_REOPEN_DELAY 5 - --#define DEFAULT_TIME2WAIT 2 -+static void iscsi_login_timedout(void *data); - - /* - * calculate parameter's padding -@@ -69,65 +60,76 @@ __padding(unsigned int param) - return param + pad; - } - --static int --__recvpool_alloc(iscsi_conn_t *conn) -+static int iscsi_conn_context_alloc(iscsi_conn_t *conn) - { - int i; - -- for (i = 0; i < RECVPOOL_MAX; i++) { -- conn->recvpool[i] = calloc(1, ipc->ctldev_bufmax); -- if (!conn->recvpool[i]) { -+ for (i = 0; i < CONTEXT_POOL_MAX; i++) { -+ conn->context_pool[i] = calloc(1, -+ sizeof(struct iscsi_conn_context) + -+ ipc->ctldev_bufmax); -+ if (!conn->context_pool[i]) { - int j; - for (j = 0; j < i; j++) -- free(conn->recvpool[j]); -- return -ENOMEM; -+ free(conn->context_pool[j]); -+ return ENOMEM; - } -+ conn->context_pool[i]->conn = conn; - } - - return 0; - } - --static void --__recvpool_free(iscsi_conn_t *conn) -+static void iscsi_conn_context_free(iscsi_conn_t *conn) - { - int i; - -- for (i = 0; i < RECVPOOL_MAX; i++) { -- if (!conn->recvpool[i]) { -- log_error("recvpool leak: %d bytes", -- ipc->ctldev_bufmax); -- } else -- free(conn->recvpool[i]); -+ for (i = 0; i < CONTEXT_POOL_MAX; i++) { -+ if (!conn->context_pool[i]) -+ continue; -+ -+ if (conn->context_pool[i]->allocated) -+ /* missing flush on shutdown */ -+ log_error("BUG: context_pool leak %p", -+ conn->context_pool[i]); -+ free(conn->context_pool[i]); - } - } - --void* recvpool_get(iscsi_conn_t *conn, int ev_size) -+struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn, -+ int ev_size) - { -+ struct iscsi_conn_context *conn_context; - int i; - - if (ev_size > ipc->ctldev_bufmax) - return NULL; - -- for (i = 0; i < RECVPOOL_MAX; i++) { -- if (conn->recvpool[i]) { -- void *handle = conn->recvpool[i]; -- conn->recvpool[i] = NULL; -- return handle; -+ for (i = 0; i < CONTEXT_POOL_MAX; i++) { -+ if (!conn->context_pool[i]) -+ continue; -+ -+ if (!conn->context_pool[i]->allocated) { -+ conn_context = conn->context_pool[i]; -+ -+ memset(&conn_context->actor, 0, -+ sizeof(struct actor)); -+ conn_context->allocated = 1; -+ /* some callers abuse this pointer */ -+ conn_context->data = conn_context + -+ sizeof(struct iscsi_conn_context); -+ log_debug(7, "get conn context %p", -+ &conn_context->actor); -+ return conn_context; - } - } - return NULL; - } - --void recvpool_put(iscsi_conn_t *conn, void *handle) -+void iscsi_conn_context_put(struct iscsi_conn_context *conn_context) - { -- int i; -- -- for (i = 0; i < RECVPOOL_MAX; i++) { -- if (!conn->recvpool[i]) { -- conn->recvpool[i] = handle; -- break; -- } -- } -+ log_debug(7, "put conn context %p", &conn_context->actor); -+ conn_context->allocated = 0; - } - - static void session_online_devs(int host_no, int sid) -@@ -144,8 +146,9 @@ __login_response_status(iscsi_conn_t *conn, - case LOGIN_OK: - /* check the status class and detail */ - return CONN_LOGIN_SUCCESS; -+ case LOGIN_REDIRECT: -+ return CONN_LOGIN_IMM_REDIRECT_RETRY; - case LOGIN_IO_ERROR: -- case LOGIN_WRONG_PORTAL_GROUP: - case LOGIN_REDIRECTION_FAILED: - return CONN_LOGIN_RETRY; - default: -@@ -187,18 +190,10 @@ __check_iscsi_status_class(iscsi_session_t *session, int cid, - case ISCSI_STATUS_CLS_INITIATOR_ERR: - switch (status_detail) { - case ISCSI_LOGIN_STATUS_AUTH_FAILED: -- log_error("conn %d login rejected: Initiator " -- "failed authentication with target", conn->id); -- if ((session->num_auth_buffers < 5) && -- (session->username || session->password_length || -- session->bidirectional_auth)) -- /* -- * retry, and hope we can allocate the auth -- * structures next time. -- */ -- return CONN_LOGIN_RETRY; -- else -- return CONN_LOGIN_FAILED; -+ log_error("session %d login rejected: Initiator " -+ "failed authentication with target", -+ session->id); -+ return CONN_LOGIN_FAILED; - case ISCSI_LOGIN_STATUS_TGT_FORBIDDEN: - log_error("conn %d login rejected: initiator " - "failed authorization with target", conn->id); -@@ -326,7 +321,7 @@ setup_portal(iscsi_conn_t *conn, conn_rec_t *conn_rec) - if (resolve_address(conn_rec->address, port, &conn->saddr)) { - log_error("cannot resolve host name %s", - conn_rec->address); -- return -EINVAL; -+ return EINVAL; - } - conn->failback_saddr = conn->saddr; - -@@ -336,6 +331,80 @@ setup_portal(iscsi_conn_t *conn, conn_rec_t *conn_rec) - return 0; - } - -+static void -+iscsi_copy_operational_params(iscsi_conn_t *conn) -+{ -+ iscsi_session_t *session = conn->session; -+ conn_rec_t *conn_rec = &session->nrec.conn[conn->id]; -+ node_rec_t *rec = &session->nrec; -+ -+ /* -+ * iSCSI default, unless declared otherwise by the -+ * target during login -+ */ -+ conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; -+ conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest; -+ conn->datadgst_en = conn_rec->iscsi.DataDigest; -+ -+ conn->max_recv_dlength = -+ __padding(conn_rec->iscsi.MaxRecvDataSegmentLength); -+ if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || -+ conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { -+ log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " -+ "within %u and %u. Setting to %u\n", -+ ISCSI_MIN_MAX_RECV_SEG_LEN, -+ ISCSI_MAX_MAX_RECV_SEG_LEN, -+ DEF_INI_MAX_RECV_SEG_LEN); -+ conn_rec->iscsi.MaxRecvDataSegmentLength = -+ DEF_INI_MAX_RECV_SEG_LEN; -+ conn->max_recv_dlength = DEF_INI_MAX_RECV_SEG_LEN; -+ } -+ -+ /* session's operational parameters */ -+ session->initial_r2t_en = rec->session.iscsi.InitialR2T; -+ session->imm_data_en = rec->session.iscsi.ImmediateData; -+ session->first_burst = __padding(rec->session.iscsi.FirstBurstLength); -+ /* -+ * some targets like netapp fail the login if sent bad first_burst -+ * and max_burst lens, even when immediate data=no and -+ * initial r2t = Yes, so we always check the user values. -+ */ -+ if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN || -+ session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) { -+ log_error("Invalid iscsi.FirstBurstLength of %u. Must be " -+ "within %u and %u. Setting to %u\n", -+ session->first_burst, -+ ISCSI_MIN_FIRST_BURST_LEN, -+ ISCSI_MAX_FIRST_BURST_LEN, -+ DEF_INI_FIRST_BURST_LEN); -+ rec->session.iscsi.FirstBurstLength = DEF_INI_FIRST_BURST_LEN; -+ session->first_burst = DEF_INI_FIRST_BURST_LEN; -+ } -+ -+ session->max_burst = __padding(rec->session.iscsi.MaxBurstLength); -+ if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN || -+ session->max_burst > ISCSI_MAX_MAX_BURST_LEN) { -+ log_error("Invalid iscsi.MaxBurstLength of %u. Must be " -+ "within %u and %u. Setting to %u\n", -+ session->max_burst, ISCSI_MIN_MAX_BURST_LEN, -+ ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN); -+ rec->session.iscsi.MaxBurstLength = DEF_INI_MAX_BURST_LEN; -+ session->max_burst = DEF_INI_MAX_BURST_LEN; -+ } -+ -+ if (session->first_burst > session->max_burst) { -+ log_error("Invalid iscsi.FirstBurstLength of %u. Must be " -+ "less than iscsi.MaxBurstLength. Setting to %u\n", -+ session->first_burst, session->max_burst); -+ rec->session.iscsi.FirstBurstLength = session->max_burst; -+ session->first_burst = session->max_burst; -+ } -+ -+ session->def_time2wait = rec->session.iscsi.DefaultTime2Wait; -+ session->def_time2retain = rec->session.iscsi.DefaultTime2Retain; -+ session->erl = rec->session.iscsi.ERL; -+} -+ - static int - __session_conn_create(iscsi_session_t *session, int cid) - { -@@ -343,11 +412,13 @@ __session_conn_create(iscsi_session_t *session, int cid) - conn_rec_t *conn_rec = &session->nrec.conn[cid]; - int err; - -- if (__recvpool_alloc(conn)) { -- log_error("cannot allocate recvpool for conn cid %d", cid); -- return -ENOMEM; -+ if (iscsi_conn_context_alloc(conn)) { -+ log_error("cannot allocate context_pool for conn cid %d", cid); -+ return ENOMEM; - } - -+ conn->state = STATE_FREE; -+ conn->session = session; - conn->socket_fd = -1; - /* connection's timeouts */ - conn->id = cid; -@@ -367,6 +438,8 @@ __session_conn_create(iscsi_session_t *session, int cid) - conn->login_timeout = DEF_LOGIN_TIMEO; - } - -+ conn->auth_timeout = conn_rec->timeo.auth_timeout; -+ - /* noop-out setting */ - conn->noop_out_interval = conn_rec->timeo.noop_out_interval; - conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout; -@@ -381,38 +454,10 @@ __session_conn_create(iscsi_session_t *session, int cid) - log_error("Invalid timeo.noop_out_interval. Must be greater " - "than zero. Using default %d.\n", - DEF_NOOP_OUT_INTERVAL); -- conn->noop_out_timeout = DEF_NOOP_OUT_INTERVAL; -- } -- -- /* -- * currently not used (leftover from linux-iscsi which we -- * may do one day) -- */ -- conn->auth_timeout = conn_rec->timeo.auth_timeout; -- conn->active_timeout = conn_rec->timeo.active_timeout; -- conn->idle_timeout = conn_rec->timeo.idle_timeout; -- conn->ping_timeout = conn_rec->timeo.ping_timeout; -- -- /* operational parameters */ -- conn->max_recv_dlength = -- __padding(conn_rec->iscsi.MaxRecvDataSegmentLength); -- if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || -- conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { -- log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " -- "within %u and %u. Setting to %u\n", -- ISCSI_MIN_MAX_RECV_SEG_LEN, -- ISCSI_MAX_MAX_RECV_SEG_LEN, -- DEF_INI_MAX_RECV_SEG_LEN); -- conn->max_recv_dlength = DEF_INI_MAX_RECV_SEG_LEN; -+ conn->noop_out_interval = DEF_NOOP_OUT_INTERVAL; - } - -- /* -- * iSCSI default, unless declared otherwise by the -- * target during login -- */ -- conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; -- conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest; -- conn->datadgst_en = conn_rec->iscsi.DataDigest; -+ iscsi_copy_operational_params(conn); - - /* TCP options */ - conn->tcp_window_size = conn_rec->tcp.window_size; -@@ -422,89 +467,20 @@ __session_conn_create(iscsi_session_t *session, int cid) - err = setup_portal(conn, conn_rec); - if (err) - return err; -- -- conn->state = STATE_FREE; -- conn->session = session; -- - return 0; - } - - static void --__send_pdu_timedout(void *data) --{ -- queue_task_t *qtask = data; -- iscsi_conn_t *conn = qtask->conn; -- iscsi_session_t *session = conn->session; -- -- if (conn->send_pdu_in_progress) { -- /* -- * redirect timeout processing to __session_conn_timer() -- */ -- queue_produce(session->queue, EV_CONN_TIMER, qtask, 0, NULL); -- actor_schedule(&session->mainloop); -- log_debug(7, "send_pdu timer timedout!"); -- } --} -- --static void --__send_pdu_timer_add(struct iscsi_conn *conn, int timeout) --{ -- if (conn->state == STATE_IN_LOGIN) { -- iscsi_login_context_t *c = &conn->login_context; -- conn->send_pdu_in_progress = 1; -- actor_timer(&conn->send_pdu_timer, timeout*1000, -- __send_pdu_timedout, c->qtask); -- log_debug(7, "send_pdu timer added %d secs", timeout); -- } --} -- --static void --__send_pdu_timer_remove(struct iscsi_conn *conn) --{ -- if (conn->send_pdu_in_progress) { -- actor_delete(&conn->send_pdu_timer); -- conn->send_pdu_in_progress = 0; -- log_debug(7, "send_pdu timer removed"); -- } --} -- -- --static void --session_stop_conn_timers(iscsi_session_t *session, int cid) --{ -- iscsi_conn_t *conn = &session->conn[cid]; -- -- __send_pdu_timer_remove(conn); -- actor_delete(&conn->connect_timer); --} -- --static void - session_release(iscsi_session_t *session) - { - log_debug(2, "Releasing session %p", session); - - if (session->target_alias) - free(session->target_alias); -- __recvpool_free(&session->conn[0]); -- actor_delete(&session->mainloop); -- queue_destroy(session->queue); -+ iscsi_conn_context_free(&session->conn[0]); - free(session); - } - --static void --session_put(iscsi_session_t *session) --{ -- session->refcount--; -- if (session->refcount == 0) -- session_release(session); --} -- --static void --session_get(iscsi_session_t *session) --{ -- session->refcount++; --} -- - static iscsi_session_t* - __session_create(node_rec_t *rec, struct iscsi_transport *t) - { -@@ -518,62 +494,34 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) - log_debug(2, "Allocted session %p", session); - - INIT_LIST_HEAD(&session->list); -- session_get(session); - /* opened at daemon load time (iscsid.c) */ - session->ctrl_fd = control_fd; - session->t = t; - session->reopen_qtask.mgmt_ipc_fd = -1; -+ session->id = -1; - - /* save node record. we might need it for redirection */ - memcpy(&session->nrec, rec, sizeof(node_rec_t)); - -- /* initalize per-session queue */ -- session->queue = queue_create(4, 4, NULL, session); -- if (session->queue == NULL) { -- log_error("can not create session's queue"); -- free(session); -- return NULL; -- } -- -- /* initalize per-session event processor */ -- actor_new(&session->mainloop, __session_mainloop, session); -- actor_schedule(&session->mainloop); -- -- /* session's operational parameters */ -- session->initial_r2t_en = rec->session.iscsi.InitialR2T; -- session->imm_data_en = rec->session.iscsi.ImmediateData; -- session->first_burst = __padding(rec->session.iscsi.FirstBurstLength); -- if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN || -- session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) { -- log_error("Invalid iscsi.FirstBurstLength of %u. Must be " -- "within %u and %u. Setting to %u\n", -- session->first_burst, -- ISCSI_MIN_FIRST_BURST_LEN, -- ISCSI_MAX_FIRST_BURST_LEN, -- DEF_INI_FIRST_BURST_LEN); -- session->first_burst = DEF_INI_FIRST_BURST_LEN; -- } -- -- session->max_burst = __padding(rec->session.iscsi.MaxBurstLength); -- if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN || -- session->max_burst > ISCSI_MAX_MAX_BURST_LEN) { -- log_error("Invalid iscsi.MaxBurstLength of %u. Must be " -- "within %u and %u. Setting to %u\n", -- session->max_burst, ISCSI_MIN_MAX_BURST_LEN, -- ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN); -- session->max_burst = DEF_INI_MAX_BURST_LEN; -- } -- -- session->def_time2wait = rec->session.iscsi.DefaultTime2Wait; -- session->def_time2retain = rec->session.iscsi.DefaultTime2Retain; -- session->erl = rec->session.iscsi.ERL; - session->portal_group_tag = rec->tpgt; - session->type = ISCSI_SESSION_TYPE_NORMAL; - session->r_stage = R_STAGE_NO_CHANGE; -- session->initiator_name = dconfig->initiator_name; -- session->initiator_alias = dconfig->initiator_alias; - strncpy(session->target_name, rec->name, TARGET_NAME_MAXLEN); - -+ if (strlen(session->nrec.iface.iname)) -+ session->initiator_name = session->nrec.iface.iname; -+ else if (dconfig->initiator_name) -+ session->initiator_name = dconfig->initiator_name; -+ else { -+ log_error("No initiator name set. Cannot create session."); -+ free(session); -+ return NULL; -+ } -+ -+ if (strlen(session->nrec.iface.alias)) -+ session->initiator_alias = session->nrec.iface.alias; -+ else -+ session->initiator_alias = dconfig->initiator_alias; - - /* session's eh parameters */ - session->replacement_timeout = rec->session.timeo.replacement_timeout; -@@ -582,6 +530,10 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) - "120 seconds\n"); - session->replacement_timeout = DEF_REPLACEMENT_TIMEO; - } -+ session->fast_abort = rec->session.iscsi.FastAbort; -+ session->abort_timeout = rec->session.err_timeo.abort_timeout; -+ session->lu_reset_timeout = rec->session.err_timeo.lu_reset_timeout; -+ session->host_reset_timeout = rec->session.err_timeo.host_reset_timeout; - - /* OUI and uniqifying number */ - session->isid[0] = DRIVER_ISID_0; -@@ -594,36 +546,37 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) - /* setup authentication variables for the session*/ - __setup_authentication(session, &rec->session.auth); - -- session->param_mask = 0xFFFFFFFF; -+ session->param_mask = ~0ULL; - if (!(t->caps & CAP_MULTI_R2T)) -- session->param_mask &= ~(1 << ISCSI_PARAM_MAX_R2T); -+ session->param_mask &= ~ISCSI_MAX_R2T; - if (!(t->caps & CAP_HDRDGST)) -- session->param_mask &= ~(1 << ISCSI_PARAM_HDRDGST_EN); -+ session->param_mask &= ~ISCSI_HDRDGST_EN; - if (!(t->caps & CAP_DATADGST)) -- session->param_mask &= ~(1 << ISCSI_PARAM_DATADGST_EN); -+ session->param_mask &= ~ISCSI_DATADGST_EN; - if (!(t->caps & CAP_MARKERS)) { -- session->param_mask &= ~(1 << ISCSI_PARAM_IFMARKER_EN); -- session->param_mask &= ~(1 << ISCSI_PARAM_OFMARKER_EN); -+ session->param_mask &= ~ISCSI_IFMARKER_EN; -+ session->param_mask &= ~ISCSI_OFMARKER_EN; - } - - list_add_tail(&session->list, &t->sessions); - return session; - } - --static void iscsi_queue_flush(queue_t *queue) -+static void iscsi_flush_context_pool(struct iscsi_session *session) - { -- unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX]; -- queue_item_t *item = (queue_item_t *)(void *)item_buf; -+ struct iscsi_conn_context *conn_context; -+ struct iscsi_conn *conn = &session->conn[0]; -+ int i; - -- /* flush queue by consuming all enqueued items */ -- while (queue_consume(queue, EVENT_PAYLOAD_MAX, -- item) != QUEUE_IS_EMPTY) { -- uintptr_t recv_handle = *(uintptr_t *)queue_item_data(item); -+ for (i = 0; i < CONTEXT_POOL_MAX; i++) { -+ conn_context = conn->context_pool[i]; -+ if (!conn_context) -+ continue; - -- log_debug(7, "item %p(%d) data size %d flushed", item, -- item->event_type, item->data_size); -- if (item->data_size) -- recvpool_put(item->context, (void*)recv_handle); -+ if (conn_context->allocated) { -+ actor_delete(&(conn->context_pool[i]->actor)); -+ iscsi_conn_context_put(conn_context); -+ } - } - } - -@@ -632,210 +585,136 @@ __session_destroy(iscsi_session_t *session) - { - log_debug(1, "destroying session\n"); - list_del(&session->list); -- iscsi_queue_flush(session->queue); -- session_put(session); -+ iscsi_flush_context_pool(session); -+ session_release(session); - } - - static void --__conn_noop_out_delete(iscsi_conn_t *conn) -+conn_delete_timers(iscsi_conn_t *conn) - { -- if (conn->noop_out_interval) { -- actor_delete(&conn->noop_out_timer); -- actor_delete(&conn->noop_out_timeout_timer); -- log_debug(3, "conn noop out timer %p stopped\n", -- &conn->noop_out_timer); -- } -+ actor_delete(&conn->login_timer); -+ actor_delete(&conn->nop_out_timer); - } - --static void --session_conn_cleanup(queue_task_t *qtask, mgmt_ipc_err_e err) -+static mgmt_ipc_err_e -+session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask, -+ mgmt_ipc_err_e err) - { -- iscsi_conn_t *conn = qtask->conn; - iscsi_session_t *session = conn->session; - -- mgmt_ipc_write_rsp(qtask, err); -- session_stop_conn_timers(session, conn->id); -- __session_destroy(session); --} -+ if (session->id == -1) -+ goto disconnect_conn; - --static mgmt_ipc_err_e --__session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask, -- mgmt_ipc_err_e err) --{ -- iscsi_session_t *session = conn->session; -+ if (!sysfs_session_has_leadconn(session->id)) -+ goto disconnect_conn; - -- __conn_noop_out_delete(conn); -- actor_delete(&conn->connect_timer); -- iscsi_queue_flush(session->queue); -+ if (conn->state == STATE_IN_LOGIN || -+ conn->state == STATE_IN_LOGOUT || -+ conn->state == STATE_LOGGED_IN) { -+ log_debug(2, "stop conn (conn state %d)", conn->state); -+ if (ipc->stop_conn(session->t->handle, session->id, -+ conn->id, STOP_CONN_TERM)) { -+ log_error("can't stop connection %d:%d (%d)", -+ session->id, conn->id, errno); -+ return MGMT_IPC_ERR_INTERNAL; -+ } -+ } - -+ log_debug(2, "kdestroy conn"); - if (ipc->destroy_conn(session->t->handle, session->id, - conn->id)) { - log_error("can not safely destroy connection %d", conn->id); - return MGMT_IPC_ERR_INTERNAL; - } -+ -+disconnect_conn: -+ log_debug(2, "disconnect conn"); -+ /* this will check for a valid interconnect connection */ - conn->session->t->template->ep_disconnect(conn); - -- if (ipc->destroy_session(session->t->handle, session->id)) { -- log_error("can not safely destroy session %d", session->id); -- return MGMT_IPC_ERR_INTERNAL; -+ if (session->id != -1) { -+ log_debug(2, "kdestroy session %u", session->id); -+ if (ipc->destroy_session(session->t->handle, session->id)) { -+ log_error("can not safely destroy session %d", -+ session->id); -+ return MGMT_IPC_ERR_INTERNAL; -+ } - } - -- session_conn_cleanup(qtask, err); -+ mgmt_ipc_write_rsp(qtask, err); -+ conn_delete_timers(conn); -+ __session_destroy(session); - return MGMT_IPC_OK; - } - --static mgmt_ipc_err_e --session_conn_shutdown(iscsi_conn_t *conn, queue_task_t *qtask, -- mgmt_ipc_err_e err) --{ -- iscsi_session_t *session = conn->session; -- -- if (ipc->stop_conn(session->t->handle, session->id, -- conn->id, STOP_CONN_TERM)) { -- log_error("can't stop connection %d:%d (%d)", -- session->id, conn->id, errno); -- return MGMT_IPC_ERR_INTERNAL; -- } -- -- return __session_conn_shutdown(conn, qtask, err); --} -- --static int --__send_nopin_rsp(iscsi_conn_t *conn, struct iscsi_nopin *rhdr, char *data) --{ -- struct iscsi_nopout hdr; -- -- memset(&hdr, 0, sizeof(struct iscsi_nopout)); -- hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; -- hdr.flags = ISCSI_FLAG_CMD_FINAL; -- hdr.dlength[0] = rhdr->dlength[0]; -- hdr.dlength[1] = rhdr->dlength[1]; -- hdr.dlength[2] = rhdr->dlength[2]; -- memcpy(hdr.lun, rhdr->lun, 8); -- hdr.ttt = rhdr->ttt; -- hdr.itt = ISCSI_RESERVED_TAG; -- -- return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr, -- ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, 0); --} -- --static int --__send_nopout(iscsi_conn_t *conn) -+static void -+queue_delayed_reopen(queue_task_t *qtask, int delay) - { -- struct iscsi_nopout hdr; -- -- memset(&hdr, 0, sizeof(struct iscsi_nopout)); -- hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; -- hdr.flags = ISCSI_FLAG_CMD_FINAL; -- hdr.itt = 0; /* XXX: let kernel send_pdu set for us*/ -- hdr.ttt = ISCSI_RESERVED_TAG; -- /* we have hdr.lun reserved, and no data */ -- return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr, -- ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0); --} -+ iscsi_conn_t *conn = qtask->conn; - --void --__conn_noop_out_timeout(void *data) --{ -- iscsi_conn_t *conn = (iscsi_conn_t*)data; -- iscsi_session_t *session = conn->session; -+ log_debug(4, "Requeue reopen attempt in %d secs\n", delay); - -- log_warning("Nop-out timedout after %d seconds on connection %d:%d " -- "state (%d). Dropping session.", conn->noop_out_timeout, -- session->id, conn->id, conn->state); -- /* XXX: error handle */ -- __conn_error_handle(session, conn); -+ /* -+ * iscsi_login_eh can handle the login resched as -+ * if it were login time out -+ */ -+ actor_delete(&conn->login_timer); -+ actor_timer(&conn->login_timer, delay * 1000, -+ iscsi_login_timedout, qtask); - } - --void --__conn_noop_out(void *data) -+static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask) - { -- iscsi_conn_t *conn = (iscsi_conn_t*)data; -- -- if (conn->noop_out_timeout_timer.state == ACTOR_NOTSCHEDULED) { -- __send_nopout(conn); -+ struct iscsi_conn_context *conn_context; -+ int rc; - -- actor_timer(&conn->noop_out_timeout_timer, -- conn->noop_out_timeout*1000, -- __conn_noop_out_timeout, conn); -- log_debug(3, "noop out timeout timer %p start, timeout %d\n", -- &conn->noop_out_timeout_timer, conn->noop_out_timeout); -+ conn_context = iscsi_conn_context_get(conn, 0); -+ if (!conn_context) { -+ /* while reopening the recv pool should be full */ -+ log_error("BUG: __session_conn_reopen could not get conn " -+ "context for recv."); -+ return ENOMEM; - } --} -+ conn_context->data = qtask; - --static void --__connect_timedout(void *data) --{ -- queue_task_t *qtask = data; -- iscsi_conn_t *conn = qtask->conn; -- iscsi_session_t *session = conn->session; -+ rc = conn->session->t->template->ep_connect(conn, 1); -+ if (rc < 0 && errno != EINPROGRESS) { -+ char serv[NI_MAXSERV]; - -- if (conn->state == STATE_XPT_WAIT) { -- /* flush any polls or other events queued */ -- iscsi_queue_flush(session->queue); -- log_debug(3, "__connect_timedout queue EV_CONN_TIMER\n"); -- queue_produce(session->queue, EV_CONN_TIMER, qtask, 0, NULL); -- actor_schedule(&session->mainloop); -- } --} -+ getnameinfo((struct sockaddr *) &conn->saddr, -+ sizeof(conn->saddr), -+ conn->host, sizeof(conn->host), serv, sizeof(serv), -+ NI_NUMERICHOST|NI_NUMERICSERV); - --static void --queue_delayed_reopen(queue_task_t *qtask, int delay) --{ -- iscsi_conn_t *conn = qtask->conn; -+ log_error("cannot make a connection to %s:%s (%d)", -+ conn->host, serv, errno); -+ iscsi_conn_context_put(conn_context); -+ return ENOTCONN; -+ } - -- log_debug(4, "Requeue reopen attempt in %d secs\n", delay); -- actor_delete(&conn->connect_timer); -- actor_timer(&conn->connect_timer, delay * 1000, -- __connect_timedout, qtask); -+ iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL); -+ log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer, -+ conn->login_timeout); -+ actor_timer(&conn->login_timer, conn->login_timeout * 1000, -+ iscsi_login_timedout, qtask); -+ return 0; - } - - static void --reset_iscsi_params(iscsi_conn_t *conn) --{ -- iscsi_session_t *session = conn->session; -- conn_rec_t *conn_rec = &session->nrec.conn[conn->id]; -- node_rec_t *rec = &session->nrec; -- -- /* operational parameters */ -- conn->max_recv_dlength = -- __padding(conn_rec->iscsi.MaxRecvDataSegmentLength); -- /* -- * iSCSI default, unless declared otherwise by the -- * target during login -- */ -- conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; -- conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest; -- conn->datadgst_en = conn_rec->iscsi.DataDigest; -- -- /* session's operational parameters */ -- session->initial_r2t_en = rec->session.iscsi.InitialR2T; -- session->imm_data_en = rec->session.iscsi.ImmediateData; -- session->first_burst = __padding(rec->session.iscsi.FirstBurstLength); -- session->max_burst = __padding(rec->session.iscsi.MaxBurstLength); -- session->def_time2wait = rec->session.iscsi.DefaultTime2Wait; -- session->def_time2retain = rec->session.iscsi.DefaultTime2Retain; -- session->erl = rec->session.iscsi.ERL; --} -- --static int --__session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop) -+__session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop, -+ int redirected) - { -- int rc, delay; - iscsi_session_t *session = conn->session; -+ uint32_t delay; - - log_debug(1, "re-opening session %d (reopen_cnt %d)", session->id, - session->reopen_cnt); - -- reset_iscsi_params(conn); - qtask->conn = conn; - - /* flush stale polls or errors queued */ -- iscsi_queue_flush(session->queue); -- actor_delete(&conn->connect_timer); -- __conn_noop_out_delete(conn); -- -- __send_pdu_timer_remove(conn); -+ iscsi_flush_context_pool(session); -+ conn_delete_timers(conn); - conn->state = STATE_XPT_WAIT; - - if (do_stop) { -@@ -844,6 +723,7 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop) - conn->id, do_stop)) { - log_error("can't stop connection %d:%d (%d)", - session->id, conn->id, errno); -+ delay = ISCSI_INTERNAL_ERR_REOPEN_DELAY; - goto queue_reopen; - } - log_debug(3, "connection %d:%d is stopped for recovery", -@@ -851,50 +731,30 @@ __session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop) - } - conn->session->t->template->ep_disconnect(conn); - -- if (session->time2wait) -- goto queue_reopen; -+ if (!redirected) { -+ delay = session->def_time2wait; -+ session->def_time2wait = 0; -+ if (delay) -+ goto queue_reopen; -+ } - -- rc = conn->session->t->template->ep_connect(conn, 1); -- if (rc < 0 && errno != EINPROGRESS) { -- char serv[NI_MAXSERV]; -+ if (!redirected) -+ session->reopen_cnt++; - -- getnameinfo((struct sockaddr *) &conn->saddr, -- sizeof(conn->saddr), -- conn->host, sizeof(conn->host), serv, sizeof(serv), -- NI_NUMERICHOST|NI_NUMERICSERV); -- -- log_error("cannot make a connection to %s:%s (%d)", -- conn->host, serv, errno); -+ if (iscsi_conn_connect(conn, qtask)) { -+ delay = ISCSI_CONN_ERR_REOPEN_DELAY; - goto queue_reopen; - } -- -- queue_produce(session->queue, EV_CONN_POLL, qtask, 0, NULL); -- actor_schedule(&session->mainloop); -- -- actor_timer(&conn->connect_timer, conn->login_timeout*1000, -- __connect_timedout, qtask); -- -- return 0; -+ return; - - queue_reopen: -- if (session->time2wait) { -- rc = 0; -- delay = session->time2wait; -- } else { -- rc = -1; -- delay = DEFAULT_TIME2WAIT; -- } -- session->time2wait = 0; -+ log_debug(4, "Waiting %u seconds before trying to reconnect.\n", delay); - queue_delayed_reopen(qtask, delay); -- return rc; - } - --static int -+static void - session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop) - { -- iscsi_session_t *session = conn->session; -- -- session->reopen_cnt++; - /* - * If we were temporarily redirected, we need to fall back to - * the original address to see where the target will send us -@@ -903,28 +763,299 @@ session_conn_reopen(iscsi_conn_t *conn, queue_task_t *qtask, int do_stop) - memset(&conn->saddr, 0, sizeof(struct sockaddr_storage)); - conn->saddr = conn->failback_saddr; - -- return __session_conn_reopen(conn, qtask, do_stop); -+ __session_conn_reopen(conn, qtask, do_stop, 0); - } - --static int --iscsi_login_redirect(iscsi_conn_t *conn) -+static void iscsi_login_eh(struct iscsi_conn *conn, struct queue_task *qtask, -+ mgmt_ipc_err_e err) -+{ -+ struct iscsi_session *session = conn->session; -+ int initial_login_retry_max; -+ -+ initial_login_retry_max = session->nrec.session.initial_login_retry_max; -+ log_debug(3, "iscsi_login_eh"); -+ /* -+ * Flush polls and other events -+ */ -+ iscsi_flush_context_pool(conn->session); -+ -+ switch (conn->state) { -+ case STATE_XPT_WAIT: -+ switch (session->r_stage) { -+ case R_STAGE_NO_CHANGE: -+ log_debug(6, "login failed STATE_XPT_WAIT/" -+ "R_STAGE_NO_CHANGE (%d/%d)", -+ session->reopen_cnt, -+ initial_login_retry_max); -+ /* timeout during initial connect. -+ * clean connection. write ipc rsp or retry */ -+ if (initial_login_retry_max < session->reopen_cnt + 1) -+ session_conn_shutdown(conn, qtask, err); -+ else { -+ session->reopen_cnt++; -+ session->t->template->ep_disconnect(conn); -+ if (iscsi_conn_connect(conn, qtask)) -+ queue_delayed_reopen(qtask, -+ ISCSI_CONN_ERR_REOPEN_DELAY); -+ } -+ break; -+ case R_STAGE_SESSION_REDIRECT: -+ log_debug(6, "login failed STATE_XPT_WAIT/" -+ "R_STAGE_SESSION_REDIRECT (%d/%d)", -+ session->reopen_cnt, -+ initial_login_retry_max); -+ /* timeout during initial redirect connect -+ * clean connection. write ipc rsp or retry */ -+ if (initial_login_retry_max < session->reopen_cnt + 1) -+ session_conn_shutdown(conn, qtask, err); -+ else -+ session_conn_reopen(conn, qtask, 0); -+ break; -+ case R_STAGE_SESSION_REOPEN: -+ log_debug(6, "login failed STATE_XPT_WAIT/" -+ "R_STAGE_SESSION_REOPEN %d", -+ session->reopen_cnt); -+ /* timeout during reopen connect. try again */ -+ session_conn_reopen(conn, qtask, 0); -+ break; -+ case R_STAGE_SESSION_CLEANUP: -+ session_conn_shutdown(conn, qtask, err); -+ break; -+ default: -+ break; -+ } -+ -+ break; -+ case STATE_IN_LOGIN: -+ switch (session->r_stage) { -+ case R_STAGE_NO_CHANGE: -+ case R_STAGE_SESSION_REDIRECT: -+ log_debug(6, "login failed STATE_IN_LOGIN/" -+ "R_STAGE_NO_CHANGE %d", -+ session->reopen_cnt); -+ /* -+ * send pdu timeout during initial connect or -+ * initial redirected connect. Clean connection -+ * and write rsp or retry. -+ */ -+ if (initial_login_retry_max < session->reopen_cnt + 1) -+ session_conn_shutdown(conn, qtask, err); -+ else -+ session_conn_reopen(conn, qtask, -+ STOP_CONN_RECOVER); -+ break; -+ case R_STAGE_SESSION_REOPEN: -+ log_debug(6, "login failed STATE_IN_LOGIN/" -+ "R_STAGE_SESSION_REOPEN %d", -+ session->reopen_cnt); -+ /* -+ * If we get a retryable error during the initial -+ * login then we will hit this path and -+ * initial_login_retry_max is ignored. -+ */ -+ session_conn_reopen(conn, qtask, STOP_CONN_RECOVER); -+ break; -+ case R_STAGE_SESSION_CLEANUP: -+ session_conn_shutdown(conn, qtask, -+ MGMT_IPC_ERR_PDU_TIMEOUT); -+ break; -+ default: -+ break; -+ } -+ -+ break; -+ default: -+ log_error("Ignoring login error %d in conn state %d.\n", -+ err, conn->state); -+ break; -+ } -+} -+ -+static void -+__conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn) -+{ -+ int i; -+ -+ /* -+ * if we got an error while trying to logout for the user then -+ * just cleanup and return to the user. -+ */ -+ if (conn->logout_qtask) { -+ session_conn_shutdown(conn, conn->logout_qtask, MGMT_IPC_OK); -+ return; -+ } -+ -+ switch (conn->state) { -+ case STATE_IN_LOGOUT: -+ /* logout was from eh - fall down to cleanup */ -+ case STATE_LOGGED_IN: -+ /* mark failed connection */ -+ conn->state = STATE_CLEANUP_WAIT; -+ -+ if (session->erl > 0) { -+ /* check if we still have some logged in connections */ -+ for (i=0; iconn[i].state == STATE_LOGGED_IN) { -+ break; -+ } -+ } -+ if (i != ISCSI_CONN_MAX) { -+ /* FIXME: re-assign leading connection -+ * for ERL>0 */ -+ } -+ -+ break; -+ } -+ -+ /* mark all connections as failed */ -+ for (i=0; iconn[i].state == STATE_LOGGED_IN) -+ session->conn[i].state = STATE_CLEANUP_WAIT; -+ } -+ session->r_stage = R_STAGE_SESSION_REOPEN; -+ break; -+ case STATE_IN_LOGIN: -+ if (session->r_stage == R_STAGE_SESSION_REOPEN) { -+ queue_task_t *qtask; -+ -+ if (session->sync_qtask) -+ qtask = session->sync_qtask; -+ else -+ qtask = &session->reopen_qtask; -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_TRANS_FAILURE); -+ return; -+ } -+ log_debug(1, "ignoring conn error in login. " -+ "let it timeout"); -+ return; -+ case STATE_XPT_WAIT: -+ log_debug(1, "ignoring conn error in XPT_WAIT. " -+ "let connection fail on its own"); -+ return; -+ case STATE_CLEANUP_WAIT: -+ log_debug(1, "ignoring conn error in CLEANUP_WAIT. " -+ "let connection stop"); -+ return; -+ default: -+ log_debug(8, "invalid state %d\n", conn->state); -+ return; -+ } -+ -+ if (session->r_stage == R_STAGE_SESSION_REOPEN) { -+ session_conn_reopen(conn, &session->reopen_qtask, -+ STOP_CONN_RECOVER); -+ return; -+ } -+} -+ -+static void session_conn_error(void *data) -+{ -+ struct iscsi_conn_context *conn_context = data; -+ enum iscsi_err error = *(enum iscsi_err *)conn_context->data; -+ iscsi_conn_t *conn = conn_context->conn; -+ iscsi_session_t *session = conn->session; -+ -+ log_warning("Kernel reported iSCSI connection %d:%d error (%d) " -+ "state (%d)", session->id, conn->id, error, -+ conn->state); -+ iscsi_conn_context_put(conn_context); -+ __conn_error_handle(session, conn); -+} -+ -+static void iscsi_login_timedout(void *data) -+{ -+ struct queue_task *qtask = data; -+ struct iscsi_conn *conn = qtask->conn; -+ -+ switch (conn->state) { -+ case STATE_XPT_WAIT: -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_TRANS_TIMEOUT); -+ break; -+ case STATE_IN_LOGIN: -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_PDU_TIMEOUT); -+ break; -+ default: -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_INTERNAL); -+ break; -+ } -+} -+ -+static void iscsi_login_redirect(iscsi_conn_t *conn) - { - iscsi_session_t *session = conn->session; - iscsi_login_context_t *c = &conn->login_context; - - log_debug(3, "login redirect ...\n"); - -- iscsi_queue_flush(session->queue); -- - if (session->r_stage == R_STAGE_NO_CHANGE) - session->r_stage = R_STAGE_SESSION_REDIRECT; - -- if (__session_conn_reopen(conn, c->qtask, STOP_CONN_RECOVER)) { -- log_error("redirct __session_conn_reopen failed\n"); -- return 1; -- } -+ __session_conn_reopen(conn, c->qtask, STOP_CONN_RECOVER, 1); -+} - -- return 0; -+static int -+__send_nopin_rsp(iscsi_conn_t *conn, struct iscsi_nopin *rhdr, char *data) -+{ -+ struct iscsi_nopout hdr; -+ -+ memset(&hdr, 0, sizeof(struct iscsi_nopout)); -+ hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; -+ hdr.flags = ISCSI_FLAG_CMD_FINAL; -+ hdr.dlength[0] = rhdr->dlength[0]; -+ hdr.dlength[1] = rhdr->dlength[1]; -+ hdr.dlength[2] = rhdr->dlength[2]; -+ memcpy(hdr.lun, rhdr->lun, 8); -+ hdr.ttt = rhdr->ttt; -+ hdr.itt = ISCSI_RESERVED_TAG; -+ -+ return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr, -+ ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, 0); -+} -+ -+static int -+__send_nopout(iscsi_conn_t *conn) -+{ -+ struct iscsi_nopout hdr; -+ -+ memset(&hdr, 0, sizeof(struct iscsi_nopout)); -+ hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; -+ hdr.flags = ISCSI_FLAG_CMD_FINAL; -+ hdr.itt = 0; /* XXX: let kernel send_pdu set for us*/ -+ hdr.ttt = ISCSI_RESERVED_TAG; -+ /* we have hdr.lun reserved, and no data */ -+ return iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr, -+ ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0); -+} -+ -+static void conn_nop_out_timeout(void *data) -+{ -+ iscsi_conn_t *conn = (iscsi_conn_t*)data; -+ iscsi_session_t *session = conn->session; -+ -+ log_warning("Nop-out timedout after %d seconds on connection %d:%d " -+ "state (%d). Dropping session.", conn->noop_out_timeout, -+ session->id, conn->id, conn->state); -+ /* XXX: error handle */ -+ __conn_error_handle(session, conn); -+} -+ -+static void conn_send_nop_out(void *data) -+{ -+ iscsi_conn_t *conn = data; -+ -+ /* -+ * we cannot start new request during logout and the logout timer -+ * will figure things out. -+ */ -+ if (conn->state == STATE_IN_LOGOUT) -+ return; -+ -+ __send_nopout(conn); -+ -+ actor_timer(&conn->nop_out_timer, conn->noop_out_timeout*1000, -+ conn_nop_out_timeout, conn); -+ log_debug(3, "noop out timeout timer %p start, timeout %d\n", -+ &conn->nop_out_timer, conn->noop_out_timeout); - } - - static void -@@ -946,6 +1077,7 @@ void free_initiator(void) - list_for_each_entry(t, &transports, list) { - list_for_each_entry_safe(session, tmp, &t->sessions, list) { - list_del(&session->list); -+ iscsi_flush_context_pool(session); - session_release(session); - } - } -@@ -1000,7 +1132,7 @@ mgmt_ipc_err_e iscsi_host_set_param(int host_no, int param, char *value) - return MGMT_IPC_OK; - } - --#define MAX_SESSION_PARAMS 24 -+#define MAX_SESSION_PARAMS 30 - #define MAX_HOST_PARAMS 3 - - static void -@@ -1155,35 +1287,45 @@ setup_full_feature_phase(iscsi_conn_t *conn) - .value = session->password_in, - .type = ISCSI_STRING, - .conn_only = 0, -+ }, { -+ .param = ISCSI_PARAM_FAST_ABORT, -+ .value = &session->fast_abort, -+ .type = ISCSI_INT, -+ .conn_only = 0, -+ }, { -+ .param = ISCSI_PARAM_ABORT_TMO, -+ .value = &session->abort_timeout, -+ .type = ISCSI_INT, -+ .conn_only = 0, -+ }, { -+ .param = ISCSI_PARAM_LU_RESET_TMO, -+ .value = &session->lu_reset_timeout, -+ .type = ISCSI_INT, -+ .conn_only = 0, -+ }, { -+ .param = ISCSI_PARAM_PING_TMO, -+ .value = &conn->noop_out_timeout, -+ .type = ISCSI_INT, -+ .conn_only = 1, -+ }, { -+ .param = ISCSI_PARAM_RECV_TMO, -+ .value = &conn->noop_out_interval, -+ .type = ISCSI_INT, -+ .conn_only = 1, -+ }, { -+ .param = ISCSI_PARAM_IFACE_NAME, -+ .value = session->nrec.iface.name, -+ .type = ISCSI_STRING, - }, -- /* -- * FIXME: set these timeouts via set_param() API -- * -- * rec->session.timeo -- * rec->session.err_timeo -- */ - }; - -- /* almost! entered full-feature phase */ -- if (__login_response_status(conn, c->ret) != CONN_LOGIN_SUCCESS) { -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_LOGIN_FAILURE); -- return; -- } -- -- /* check the login status */ -- if (__check_iscsi_status_class(session, conn->id, c->status_class, -- c->status_detail) != CONN_LOGIN_SUCCESS) { -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_LOGIN_FAILURE); -- return; -- } -- -+ actor_delete(&conn->login_timer); - /* Entered full-feature phase! */ - for (i = 0; i < MAX_SESSION_PARAMS; i++) { - if (conn->id != 0 && !conntbl[i].conn_only) - continue; -- if (!(session->param_mask & (1 << conntbl[i].param))) -+ -+ if (!(session->param_mask & (1ULL << conntbl[i].param))) - continue; - - rc = ipc->set_param(session->t->handle, session->id, -@@ -1195,11 +1337,16 @@ setup_full_feature_phase(iscsi_conn_t *conn) - conntbl[i].param, session->id, conn->id, - rc, errno); - -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_LOGIN_FAILURE); -+ iscsi_login_eh(conn, c->qtask, -+ MGMT_IPC_ERR_LOGIN_FAILURE); - return; - } - -+ /* older kernels may not support nop handling in kernel */ -+ if (rc == -ENOSYS && -+ conntbl[i].param == ISCSI_PARAM_PING_TMO) -+ conn->userspace_nop = 1; -+ - print_param_value(conntbl[i].param, conntbl[i].value, - conntbl[i].type); - } -@@ -1208,8 +1355,8 @@ setup_full_feature_phase(iscsi_conn_t *conn) - if (__iscsi_host_set_param(session->t, session->hostno, - hosttbl[i].param, hosttbl[i].value, - hosttbl[i].type)) { -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_LOGIN_FAILURE); -+ iscsi_login_eh(conn, c->qtask, -+ MGMT_IPC_ERR_LOGIN_FAILURE); - return; - } - -@@ -1219,10 +1366,9 @@ setup_full_feature_phase(iscsi_conn_t *conn) - - if (ipc->start_conn(session->t->handle, session->id, conn->id, - &rc) || rc) { -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_INTERNAL); - log_error("can't start connection %d:%d retcode %d (%d)", - session->id, conn->id, rc, errno); -+ iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_INTERNAL); - return; - } - -@@ -1255,20 +1401,20 @@ setup_full_feature_phase(iscsi_conn_t *conn) - session->r_stage = R_STAGE_NO_CHANGE; - - /* noop_out */ -- if (conn->noop_out_interval) { -- actor_timer(&conn->noop_out_timer, conn->noop_out_interval*1000, -- __conn_noop_out, conn); -- actor_new(&conn->noop_out_timeout_timer, -- __conn_noop_out_timeout, conn); -+ if (conn->userspace_nop && conn->noop_out_interval) { -+ actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000, -+ conn_send_nop_out, conn); - log_debug(3, "noop out timer %p start\n", -- &conn->noop_out_timer); -+ &conn->nop_out_timer); - } - } - --static void iscsi_logout_timeout(void *data) -+static void iscsi_logout_timedout(void *data) - { -- iscsi_conn_t *conn = data; -+ struct iscsi_conn_context *conn_context = data; -+ struct iscsi_conn *conn = conn_context->conn; - -+ iscsi_conn_context_put(conn_context); - /* - * assume we were in STATE_IN_LOGOUT or there - * was some nasty error -@@ -1280,10 +1426,10 @@ static void iscsi_logout_timeout(void *data) - static int iscsi_send_logout(iscsi_conn_t *conn) - { - struct iscsi_logout hdr; -+ struct iscsi_conn_context *conn_context; - -- if (conn->state == STATE_IN_LOGOUT || -- conn->state != STATE_LOGGED_IN) -- return -EINVAL; -+ if (conn->state != STATE_LOGGED_IN) -+ return EINVAL; - - memset(&hdr, 0, sizeof(struct iscsi_logout)); - hdr.opcode = ISCSI_OP_LOGOUT | ISCSI_OP_IMMEDIATE; -@@ -1293,23 +1439,53 @@ static int iscsi_send_logout(iscsi_conn_t *conn) - - if (!iscsi_io_send_pdu(conn, (struct iscsi_hdr*)&hdr, - ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 0)) -- return -EIO; -+ return EIO; - conn->state = STATE_IN_LOGOUT; - -- actor_timer(&conn->logout_timer, conn->logout_timeout * 1000, -- iscsi_logout_timeout, conn); -- log_debug(3, "logout timeout timer %p start\n", &conn->logout_timer); -+ conn_context = iscsi_conn_context_get(conn, 0); -+ if (!conn_context) -+ /* unbounded logout */ -+ log_warning("Could not allocate conn context for logout."); -+ else { -+ iscsi_sched_conn_context(conn_context, conn, -+ conn->logout_timeout, -+ EV_CONN_LOGOUT_TIMER); -+ log_debug(3, "logout timeout timer %u\n", -+ conn->logout_timeout * 1000); -+ } -+ - return 0; - } - -+static void iscsi_stop(void *data) -+{ -+ struct iscsi_conn_context *conn_context = data; -+ struct iscsi_conn *conn = conn_context->conn; -+ int rc = 0; -+ -+ iscsi_conn_context_put(conn_context); -+ -+ if (!iscsi_send_logout(conn)) -+ return; -+ -+ rc = session_conn_shutdown(conn, conn->logout_qtask, MGMT_IPC_OK); -+ if (rc) -+ log_error("BUG: Could not shutdown session."); -+} -+ - static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - { -+ if (!conn->userspace_nop) { -+ log_error("Got nop in, but kernel supports nop handling."); -+ return; -+ } -+ - if (hdr->ttt == ISCSI_RESERVED_TAG) { - /* noop out rsp */ -- actor_delete(&conn->noop_out_timeout_timer); -+ actor_delete(&conn->nop_out_timer); - /* schedule a new ping */ -- actor_timer(&conn->noop_out_timer, conn->noop_out_interval*1000, -- __conn_noop_out, conn); -+ actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000, -+ conn_send_nop_out, conn); - } else /* noop in req */ - if (!__send_nopin_rsp(conn, (struct iscsi_nopin*)hdr, - conn->data)) { -@@ -1319,7 +1495,13 @@ static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - - static void iscsi_recv_logout_rsp(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - { -+ struct iscsi_logout_rsp *logout_rsp = (struct iscsi_logout_rsp *)hdr; - log_debug(3, "Recv: logout response\n"); -+ -+ -+ conn->session->def_time2wait = ntohl(logout_rsp->t2wait); -+ log_debug(4, "logout rsp returned time2wait %u", -+ conn->session->def_time2wait); - /* TODO process the hdr */ - __conn_error_handle(conn->session, conn); - } -@@ -1330,6 +1512,7 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - struct iscsi_async *async_hdr = (struct iscsi_async *)hdr; - char *buf = conn->data; - unsigned int senselen; -+ struct scsi_sense_hdr sshdr; - - log_debug(3, "Read AEN %d\n", async_hdr->async_event); - -@@ -1338,7 +1521,14 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - senselen = (buf[0] << 8) | buf[1]; - buf += 2; - -- /* TODO: do something with it */ -+ if (!scsi_normalize_sense((uint8_t *)buf, senselen, &sshdr)) { -+ log_error("Could not handle AEN %d. Invalid sense.", -+ async_hdr->async_event); -+ break; -+ } -+ -+ if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e) -+ session_scan_host(session->hostno, NULL); - break; - case ISCSI_ASYNC_MSG_REQUEST_LOGOUT: - log_warning("Target requests logout within %u seconds for " -@@ -1351,14 +1541,14 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - log_warning("Target dropping connection %u, reconnect min %u " - "max %u\n", ntohs(async_hdr->param1), - ntohs(async_hdr->param2), ntohs(async_hdr->param3)); -- session->time2wait = -+ session->def_time2wait = - (uint32_t)ntohs(async_hdr->param2) & 0x0000FFFFFL; - break; - case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS: - log_warning("Target dropping all connections, reconnect min %u " - "max %u\n", ntohs(async_hdr->param2), - ntohs(async_hdr->param3)); -- session->time2wait = -+ session->def_time2wait = - (uint32_t)ntohs(async_hdr->param2) & 0x0000FFFFFL; - break; - case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION: -@@ -1372,39 +1562,79 @@ static void iscsi_recv_async_msg(iscsi_conn_t *conn, struct iscsi_hdr *hdr) - } - } - --static void --__session_conn_recv_pdu(queue_item_t *item) -+static void iscsi_recv_login_rsp(struct iscsi_conn *conn) -+{ -+ struct iscsi_session *session = conn->session; -+ iscsi_login_context_t *c = &conn->login_context; -+ -+ if (iscsi_login_rsp(session, c)) { -+ log_debug(1, "login_rsp ret (%d)", c->ret); -+ -+ switch (__login_response_status(conn, c->ret)) { -+ case CONN_LOGIN_FAILED: -+ goto failed; -+ case CONN_LOGIN_RETRY: -+ goto retry; -+ case CONN_LOGIN_IMM_REDIRECT_RETRY: -+ iscsi_login_redirect(conn); -+ return; -+ default: -+ ; /* success - fall through */ -+ } -+ -+ /* check the login status */ -+ switch (__check_iscsi_status_class(session, conn->id, -+ c->status_class, -+ c->status_detail)) { -+ case CONN_LOGIN_FAILED: -+ goto failed; -+ case CONN_LOGIN_IMM_REDIRECT_RETRY: -+ iscsi_login_redirect(conn); -+ return; -+ case CONN_LOGIN_IMM_RETRY: -+ case CONN_LOGIN_RETRY: -+ goto retry; -+ default: -+ ; /* success - fall through */ -+ } -+ } -+ -+ if (conn->current_stage != ISCSI_FULL_FEATURE_PHASE) { -+ /* more nego. needed! */ -+ conn->state = STATE_IN_LOGIN; -+ if (iscsi_login_req(session, c)) { -+ iscsi_login_eh(conn, c->qtask, -+ MGMT_IPC_ERR_LOGIN_FAILURE); -+ return; -+ } -+ } else -+ setup_full_feature_phase(conn); -+ -+ return; -+retry: -+ /* force retry */ -+ session->r_stage = R_STAGE_SESSION_REOPEN; -+ iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_LOGIN_FAILURE); -+ return; -+failed: -+ /* force faulure */ -+ session->r_stage = R_STAGE_NO_CHANGE; -+ session->reopen_cnt = session->nrec.session.initial_login_retry_max; -+ iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_LOGIN_FAILURE); -+ return; -+} -+ -+static void session_conn_recv_pdu(void *data) - { -- iscsi_conn_t *conn = item->context; -- iscsi_session_t *session = conn->session; -- iscsi_login_context_t *c; -+ struct iscsi_conn_context *conn_context = data; -+ iscsi_conn_t *conn = conn_context->conn; - struct iscsi_hdr hdr; - -- conn->recv_handle = *(uintptr_t*)queue_item_data(item); -+ conn->recv_context = conn_context; - - switch (conn->state) { - case STATE_IN_LOGIN: -- c = &conn->login_context; -- -- if (iscsi_login_rsp(session, c)) { -- log_debug(1, "login_rsp ret (%d)", c->ret); -- if (c->ret != LOGIN_REDIRECT || -- iscsi_login_redirect(conn)) -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_LOGIN_FAILURE); -- return; -- } -- -- if (conn->current_stage != ISCSI_FULL_FEATURE_PHASE) { -- /* more nego. needed! */ -- conn->state = STATE_IN_LOGIN; -- if (iscsi_login_req(session, c)) { -- __session_conn_shutdown(conn, c->qtask, -- MGMT_IPC_ERR_LOGIN_FAILURE); -- return; -- } -- } else -- setup_full_feature_phase(conn); -+ iscsi_recv_login_rsp(conn); - break; - case STATE_LOGGED_IN: - case STATE_IN_LOGOUT: -@@ -1432,59 +1662,56 @@ __session_conn_recv_pdu(queue_item_t *item) - } - break; - case STATE_XPT_WAIT: -- recvpool_put(conn, (void *)conn->recv_handle); -+ iscsi_conn_context_put(conn_context); - log_debug(1, "ignoring incomming PDU in XPT_WAIT. " - "let connection re-establish or fail"); - break; - case STATE_CLEANUP_WAIT: -- recvpool_put(conn, (void *)conn->recv_handle); -+ iscsi_conn_context_put(conn_context); - log_debug(1, "ignoring incomming PDU in XPT_WAIT. " - "let connection cleanup"); - break; - default: -- recvpool_put(conn, (void *)conn->recv_handle); -+ iscsi_conn_context_put(conn_context); - log_error("Invalid state. Dropping PDU.\n"); - } - } - --static void --setup_kernel_io_callouts(iscsi_conn_t *conn) --{ -- conn->kernel_io = 1; -- conn->send_pdu_begin = ipc->send_pdu_begin; -- conn->send_pdu_end = ipc->send_pdu_end; -- conn->recv_pdu_begin = ipc->recv_pdu_begin; -- conn->recv_pdu_end = ipc->recv_pdu_end; -- conn->send_pdu_timer_add = __send_pdu_timer_add; -- conn->send_pdu_timer_remove = __send_pdu_timer_remove; --} -- --static void --__session_conn_poll(queue_item_t *item) -+static void session_conn_poll(void *data) - { -+ struct iscsi_conn_context *conn_context = data; -+ iscsi_conn_t *conn = conn_context->conn; -+ struct iscsi_session *session = conn->session; - mgmt_ipc_err_e err = MGMT_IPC_OK; -- queue_task_t *qtask = item->context; -- iscsi_conn_t *conn = qtask->conn; -+ queue_task_t *qtask = conn_context->data; - iscsi_login_context_t *c = &conn->login_context; -- iscsi_session_t *session = conn->session; - int rc; - -+ iscsi_conn_context_put(conn_context); -+ - if (conn->state != STATE_XPT_WAIT) - return; - - rc = session->t->template->ep_poll(conn, 1); - if (rc == 0) { -+ log_debug(4, "poll not connected %d", rc); - /* timedout: Poll again. */ -- queue_produce(session->queue, EV_CONN_POLL, qtask, 0, NULL); -- actor_schedule(&session->mainloop); -+ conn_context = iscsi_conn_context_get(conn, 0); -+ if (!conn_context) { -+ /* while polling the recv pool should be full */ -+ log_error("BUG: session_conn_poll could not get conn " -+ "context."); -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_INTERNAL); -+ return; -+ } -+ conn_context->data = qtask; -+ iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL); - } else if (rc > 0) { - /* connected! */ - memset(c, 0, sizeof(iscsi_login_context_t)); - -- actor_delete(&conn->connect_timer); -- - /* do not allocate new connection in case of reopen */ -- if (session->r_stage == R_STAGE_NO_CHANGE) { -+ if (session->id == -1) { - if (conn->id == 0 && - ipc->create_session(session->t->handle, - session->nrec.session.initial_cmdsn, -@@ -1503,12 +1730,13 @@ __session_conn_poll(queue_item_t *item) - log_error("can't create connection (%d)", - errno); - err = MGMT_IPC_ERR_INTERNAL; -- goto s_cleanup; -+ goto cleanup; - } - log_debug(3, "created new iSCSI connection " - "%d:%d", session->id, conn->id); - } - -+ iscsi_copy_operational_params(conn); - /* - * TODO: use the iface number or some other value - * so this will be persistent -@@ -1521,14 +1749,12 @@ __session_conn_poll(queue_item_t *item) - log_error("can't bind conn %d:%d to session %d, " - "retcode %d (%d)", session->id, conn->id, - session->id, rc, errno); -- err = MGMT_IPC_ERR_INTERNAL; -- goto c_cleanup; -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE); -+ return; - } - log_debug(3, "bound iSCSI connection %d:%d to session %d", - session->id, conn->id, session->id); - -- setup_kernel_io_callouts(conn); -- - c->qtask = qtask; - c->cid = conn->id; - c->buffer = conn->data; -@@ -1537,250 +1763,65 @@ __session_conn_poll(queue_item_t *item) - set_exp_statsn(conn); - - if (iscsi_login_begin(session, c)) { -- err = MGMT_IPC_ERR_LOGIN_FAILURE; -- goto c_cleanup; -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE); -+ return; - } - - conn->state = STATE_IN_LOGIN; - if (iscsi_login_req(session, c)) { -- err = MGMT_IPC_ERR_LOGIN_FAILURE; -- goto c_cleanup; -+ iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_LOGIN_FAILURE); -+ return; - } -- } else if (session->r_stage == R_STAGE_NO_CHANGE) { -- /* -- * poll failed during the initial connect. Give up -- */ -- /* error during connect */ -- err = MGMT_IPC_ERR_TRANS_FAILURE; -- goto cleanup; -- } else -- /* -- * poll failed on reopen -- */ -- queue_delayed_reopen(qtask, DEFAULT_TIME2WAIT); -+ } else { -+ log_debug(4, "poll error %d", rc); -+ queue_delayed_reopen(qtask, ISCSI_CONN_ERR_REOPEN_DELAY); -+ } -+ - return; - --c_cleanup: -- if (ipc->destroy_conn(session->t->handle, session->id, -- conn->id)) { -- log_error("can not safely destroy connection %d:%d", -- session->id, conn->id); -- } --s_cleanup: -- if (ipc->destroy_session(session->t->handle, session->id)) { -- log_error("can not safely destroy session %d", session->id); -- } - cleanup: -- session->t->template->ep_disconnect(conn); -- session_conn_cleanup(qtask, err); -+ session_conn_shutdown(conn, qtask, err); - } - --static void --__session_conn_timer(queue_item_t *item) -+void iscsi_sched_conn_context(struct iscsi_conn_context *conn_context, -+ struct iscsi_conn *conn, unsigned long tmo, -+ int event) - { -- queue_task_t *qtask = item->context; -- iscsi_conn_t *conn = qtask->conn; -- iscsi_session_t *session = conn->session; -- -- switch (conn->state) { -- case STATE_XPT_WAIT: -- switch (session->r_stage) { -- case R_STAGE_NO_CHANGE: -- session->t->template->ep_disconnect(conn); -- log_debug(6, "conn_timer popped at XPT_WAIT: login"); -- /* timeout during initial connect. -- * clean connection. write ipc rsp */ -- session_conn_cleanup(qtask, -- MGMT_IPC_ERR_TRANS_TIMEOUT); -- break; -- case R_STAGE_SESSION_REDIRECT: -- log_debug(6, "conn_timer popped at XPT_WAIT: " -- "login redirect"); -- /* timeout during initial redirect connect -- * clean connection. write ipc rsp */ -- __session_conn_shutdown(conn, qtask, -- MGMT_IPC_ERR_TRANS_TIMEOUT); -- break; -- case R_STAGE_SESSION_REOPEN: -- log_debug(6, "conn_timer popped at XPT_WAIT: reopen"); -- /* timeout during reopen connect. try again */ -- session_conn_reopen(conn, qtask, 0); -- break; -- case R_STAGE_SESSION_CLEANUP: -- __session_conn_shutdown(conn, qtask, -- MGMT_IPC_ERR_TRANS_TIMEOUT); -- break; -- default: -- break; -- } -- -+ log_debug(7, "sched conn context %p event %d, tmo %lu", -+ &conn_context->actor, event, tmo); -+ -+ conn_context->conn = conn; -+ switch (event) { -+ case EV_CONN_RECV_PDU: -+ actor_new(&conn_context->actor, session_conn_recv_pdu, -+ conn_context); -+ actor_schedule(&conn_context->actor); - break; -- case STATE_IN_LOGIN: -- switch (session->r_stage) { -- case R_STAGE_NO_CHANGE: -- case R_STAGE_SESSION_REDIRECT: -- log_debug(6, "conn_timer popped at IN_LOGIN: cleanup"); -- /* -- * send pdu timeout during initial connect or -- * initial redirected connect. Clean connection -- * and write rsp. -- */ -- session_conn_shutdown(conn, qtask, -- MGMT_IPC_ERR_PDU_TIMEOUT); -- break; -- case R_STAGE_SESSION_REOPEN: -- log_debug(6, "conn_timer popped at IN_LOGIN: reopen"); -- session_conn_reopen(conn, qtask, STOP_CONN_RECOVER); -- break; -- case R_STAGE_SESSION_CLEANUP: -- session_conn_shutdown(conn, qtask, -- MGMT_IPC_ERR_PDU_TIMEOUT); -- break; -- default: -- break; -- } -- -+ case EV_CONN_ERROR: -+ actor_new(&conn_context->actor, session_conn_error, -+ conn_context); -+ actor_schedule(&conn_context->actor); - break; -- default: -- log_debug(8, "ignoring timeout in conn state %d\n", -- conn->state); -+ case EV_CONN_POLL: -+ actor_new(&conn_context->actor, session_conn_poll, -+ conn_context); -+ actor_schedule(&conn_context->actor); - break; -- } --} -- --static void --__conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn) --{ -- int i; -- -- switch (conn->state) { -- case STATE_IN_LOGOUT: -- actor_delete(&conn->logout_timer); -- /* logout was requested by user */ -- if (conn->logout_qtask) { -- session_conn_shutdown(conn, conn->logout_qtask, -- MGMT_IPC_OK); -- return; -- } -- /* logout was from eh - fall down to cleanup */ -- case STATE_LOGGED_IN: -- /* mark failed connection */ -- conn->state = STATE_CLEANUP_WAIT; -- -- if (session->erl > 0) { -- /* check if we still have some logged in connections */ -- for (i=0; iconn[i].state == STATE_LOGGED_IN) { -- break; -- } -- } -- if (i != ISCSI_CONN_MAX) { -- /* FIXME: re-assign leading connection -- * for ERL>0 */ -- } -- -- break; -- } -- -- /* mark all connections as failed */ -- for (i=0; iconn[i].state == STATE_LOGGED_IN) -- session->conn[i].state = STATE_CLEANUP_WAIT; -- } -- session->r_stage = R_STAGE_SESSION_REOPEN; -+ case EV_CONN_LOGOUT_TIMER: -+ actor_timer(&conn_context->actor, tmo * 1000, -+ iscsi_logout_timedout, conn_context); -+ break; -+ case EV_CONN_STOP: -+ actor_new(&conn_context->actor, iscsi_stop, -+ conn_context); -+ actor_schedule(&conn_context->actor); - break; -- case STATE_IN_LOGIN: -- if (session->r_stage == R_STAGE_SESSION_REOPEN) { -- queue_task_t *qtask; -- -- if (session->sync_qtask) -- qtask = session->sync_qtask; -- else -- qtask = &session->reopen_qtask; -- -- session_conn_reopen(conn, qtask, STOP_CONN_RECOVER); -- return; -- } -- -- log_debug(1, "ignoring conn error in login. " -- "let it timeout"); -- return; -- case STATE_XPT_WAIT: -- log_debug(1, "ignoring conn error in XPT_WAIT. " -- "let connection fail on its own"); -- return; -- case STATE_CLEANUP_WAIT: -- log_debug(1, "ignoring conn error in CLEANUP_WAIT. " -- "let connection stop"); -- return; - default: -- log_debug(8, "invalid state %d\n", conn->state); -- return; -- } -- -- if (session->r_stage == R_STAGE_SESSION_REOPEN) { -- session_conn_reopen(conn, &session->reopen_qtask, -- STOP_CONN_RECOVER); -+ log_error("Invalid event type %d.", event); - return; - } - } - --static void --__session_conn_error(queue_item_t *item) --{ -- uintptr_t recv_handle = *(uintptr_t *)queue_item_data(item); -- enum iscsi_err error = *(enum iscsi_err *)recv_handle; -- iscsi_conn_t *conn = item->context; -- iscsi_session_t *session = conn->session; -- -- log_warning("Kernel reported iSCSI connection %d:%d error (%d) " -- "state (%d)", session->id, conn->id, error, -- conn->state); -- __conn_error_handle(session, conn); -- recvpool_put(conn, (void*)recv_handle); --} -- --static void --__session_mainloop(void *data) --{ -- iscsi_session_t *session = data; -- unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX]; -- queue_item_t *item = (queue_item_t *)(void *)item_buf; -- int count = session->queue->count, i; -- -- /* -- * grab a reference in case one of these events destroys -- * the session -- */ -- session_get(session); -- for (i = 0; i < count; i++) { -- if (queue_consume(session->queue, EVENT_PAYLOAD_MAX, -- item) == QUEUE_IS_EMPTY) { -- log_debug(4, "%d items flushed while mainloop " -- "was processing", count - i); -- break; -- } -- -- switch (item->event_type) { -- case EV_CONN_RECV_PDU: -- __session_conn_recv_pdu(item); -- break; -- case EV_CONN_POLL: -- __session_conn_poll(item); -- break; -- case EV_CONN_TIMER: -- __session_conn_timer(item); -- break; -- case EV_CONN_ERROR: -- __session_conn_error(item); -- break; -- default: -- break; -- } -- } -- session_put(session); --} -- - iscsi_session_t* - session_find_by_sid(int sid) - { -@@ -1796,8 +1837,7 @@ session_find_by_sid(int sid) - return NULL; - } - --iscsi_session_t* --session_find_by_rec(node_rec_t *rec) -+static iscsi_session_t* session_find_by_rec(node_rec_t *rec) - { - struct iscsi_transport *t; - iscsi_session_t *session; -@@ -1834,11 +1874,16 @@ int session_is_running(node_rec_t *rec) - int - session_login_task(node_rec_t *rec, queue_task_t *qtask) - { -- int rc; - iscsi_session_t *session; - iscsi_conn_t *conn; - struct iscsi_transport *t; - -+ if (session_is_running(rec)) { -+ log_error("session [%s,%s,%d] already running.", rec->name, -+ rec->conn[0].address, rec->conn[0].port); -+ return MGMT_IPC_ERR_EXISTS; -+ } -+ - t = get_transport_by_name(rec->iface.transport_name); - if (!t) - return MGMT_IPC_ERR_TRANS_NOT_FOUND; -@@ -1904,32 +1949,14 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) - conn = &session->conn[0]; - qtask->conn = conn; - -- rc = session->t->template->ep_connect(conn, 1); -- if (rc < 0 && errno != EINPROGRESS) { -- char serv[NI_MAXSERV]; -- -- getnameinfo((struct sockaddr *) &conn->saddr, -- sizeof(conn->saddr), -- conn->host, sizeof(conn->host), serv, sizeof(serv), -- NI_NUMERICHOST|NI_NUMERICSERV); -- -- log_error("cannot make a connection to %s:%s (%d)", -- conn->host, serv, errno); -- session_stop_conn_timers(session, 0); -+ conn->state = STATE_XPT_WAIT; -+ if (iscsi_conn_connect(conn, qtask)) { - __session_destroy(session); - return MGMT_IPC_ERR_TRANS_FAILURE; - } - -- conn->state = STATE_XPT_WAIT; -- queue_produce(session->queue, EV_CONN_POLL, qtask, 0, NULL); -- actor_schedule(&session->mainloop); -- -- actor_timer(&conn->connect_timer, conn->login_timeout*1000, -- __connect_timedout, qtask); -- - qtask->rsp.command = MGMT_IPC_SESSION_LOGIN; - qtask->rsp.err = MGMT_IPC_OK; -- - return MGMT_IPC_OK; - } - -@@ -1939,10 +1966,9 @@ sync_conn(iscsi_session_t *session, uint32_t cid) - iscsi_conn_t *conn; - - if (__session_conn_create(session, cid)) -- return -ENOMEM; -+ return ENOMEM; - conn = &session->conn[cid]; - -- setup_kernel_io_callouts(conn); - /* TODO: must export via sysfs so we can pick this up */ - conn->state = STATE_CLEANUP_WAIT; - return 0; -@@ -1963,7 +1989,7 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid) - - session = __session_create(rec, t); - if (!session) -- return MGMT_IPC_ERR_NOMEM; -+ return MGMT_IPC_ERR_LOGIN_FAILURE; - - session->id = sid; - session->hostno = get_host_no_from_sid(sid, &err); -@@ -1977,10 +2003,8 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid) - - err = sync_conn(session, 0); - if (err) { -- if (err == -ENOMEM) -+ if (err == ENOMEM) - err = MGMT_IPC_ERR_NOMEM; -- else if (err == -ENODEV) -- err = MGMT_IPC_ERR_NOT_FOUND; - else - err = MGMT_IPC_ERR_INVAL; - goto destroy_session; -@@ -1999,17 +2023,40 @@ destroy_session: - return err; - } - -+static int session_unbind(struct iscsi_session *session) -+{ -+ int err; -+ -+ err = ipc->unbind_session(session->t->handle, session->id); -+ if (err) -+ /* older kernels did not support unbind */ -+ log_debug(2, "Could not unbind session %d.\n", err); -+ return err; -+} -+ - int --session_logout_task(iscsi_session_t *session, queue_task_t *qtask) -+session_logout_task(int sid, queue_task_t *qtask) - { -+ iscsi_session_t *session; - iscsi_conn_t *conn; - mgmt_ipc_err_e rc = MGMT_IPC_OK; - -+ session = session_find_by_sid(sid); -+ if (!session) { -+ log_debug(1, "session sid %d not found.\n", sid); -+ return MGMT_IPC_ERR_NOT_FOUND; -+ } - conn = &session->conn[0]; -+ /* -+ * If syncing up or if this is the initial login and mgmt_ipc -+ * has not been notified of that result fail the logout request -+ */ - if (session->sync_qtask || -- (conn->state == STATE_XPT_WAIT && -+ ((conn->state == STATE_XPT_WAIT || -+ conn->state == STATE_IN_LOGIN) && - (session->r_stage == R_STAGE_NO_CHANGE || - session->r_stage == R_STAGE_SESSION_REDIRECT))) { -+invalid_state: - log_error("session in invalid state for logout. " - "Try again later\n"); - return MGMT_IPC_ERR_INTERNAL; -@@ -2018,6 +2065,8 @@ session_logout_task(iscsi_session_t *session, queue_task_t *qtask) - /* FIXME: logout all active connections */ - conn = &session->conn[0]; - /* FIXME: implement Logout Request */ -+ if (conn->logout_qtask) -+ goto invalid_state; - - qtask->conn = conn; - qtask->rsp.command = MGMT_IPC_SESSION_LOGOUT; -@@ -2025,18 +2074,16 @@ session_logout_task(iscsi_session_t *session, queue_task_t *qtask) - - switch (conn->state) { - case STATE_LOGGED_IN: -+ if (!session_unbind(session)) -+ return MGMT_IPC_OK; -+ -+ /* unbind is not supported so just do old logout */ - if (!iscsi_send_logout(conn)) - return MGMT_IPC_OK; - log_error("Could not send logout pdu. Dropping session\n"); - /* fallthrough */ -- case STATE_IN_LOGIN: -- rc = session_conn_shutdown(conn, qtask, MGMT_IPC_OK); -- break; -- case STATE_IN_LOGOUT: -- rc = MGMT_IPC_ERR_LOGOUT_FAILURE; -- break; - default: -- rc = __session_conn_shutdown(conn, qtask, MGMT_IPC_OK); -+ rc = session_conn_shutdown(conn, qtask, MGMT_IPC_OK); - break; - } - -@@ -2072,6 +2119,15 @@ iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login, - */ - void iscsi_async_session_creation(uint32_t host_no, uint32_t sid) - { -+ struct iscsi_transport *transport; -+ -+ transport = get_transport_by_hba(host_no); -+ if (!transport) -+ return; -+ -+ if (!(transport->caps & CAP_FW_DB)) -+ return; -+ - log_debug(3, "session created sid %u host no %d", sid, host_no); - session_online_devs(host_no, sid); - session_scan_host(host_no, NULL); -diff --git a/usr/initiator.h b/usr/initiator.h -index 70b5581..594e8f8 100644 ---- a/usr/initiator.h -+++ b/usr/initiator.h -@@ -30,7 +30,6 @@ - #include "mgmt_ipc.h" - #include "config.h" - #include "actor.h" --#include "queue.h" - #include "list.h" - - #define ISCSI_CONFIG_ROOT "/etc/iscsi/" -@@ -43,6 +42,23 @@ - #define LOCK_FILE "/var/lock/iscsi/lock" - #define LOCK_WRITE_FILE "/var/lock/iscsi/lock.write" - -+typedef enum iscsi_conn_state_e { -+ STATE_FREE, -+ STATE_XPT_WAIT, -+ STATE_IN_LOGIN, -+ STATE_LOGGED_IN, -+ STATE_IN_LOGOUT, -+ STATE_LOGOUT_REQUESTED, -+ STATE_CLEANUP_WAIT, -+} iscsi_conn_state_e; -+ -+typedef enum iscsi_session_r_stage_e { -+ R_STAGE_NO_CHANGE, -+ R_STAGE_SESSION_CLEANUP, -+ R_STAGE_SESSION_REOPEN, -+ R_STAGE_SESSION_REDIRECT, -+} iscsi_session_r_stage_e; -+ - typedef enum conn_login_status_e { - CONN_LOGIN_SUCCESS = 0, - CONN_LOGIN_FAILED = 1, -@@ -59,25 +75,20 @@ enum iscsi_login_status { - LOGIN_VERSION_MISMATCH = 3, - LOGIN_NEGOTIATION_FAILED = 4, - LOGIN_AUTHENTICATION_FAILED = 5, -- LOGIN_WRONG_PORTAL_GROUP = 6, -- LOGIN_REDIRECTION_FAILED = 7, -- LOGIN_INVALID_PDU = 8, -- LOGIN_REDIRECT = 9, -+ LOGIN_REDIRECTION_FAILED = 6, -+ LOGIN_INVALID_PDU = 7, -+ LOGIN_REDIRECT = 8, - }; - - typedef enum iscsi_event_e { -- EV_UNKNOWN = 0, -- EV_CONN_RECV_PDU = 1, -- EV_CONN_POLL = 2, -- EV_CONN_TIMER = 3, -- EV_CONN_ERROR = 4, -+ EV_UNKNOWN, -+ EV_CONN_RECV_PDU, -+ EV_CONN_POLL, -+ EV_CONN_ERROR, -+ EV_CONN_LOGOUT_TIMER, -+ EV_CONN_STOP, - } iscsi_event_e; - --typedef struct iscsi_event { -- queue_item_t item; -- char payload[EVENT_PAYLOAD_MAX]; --} iscsi_event_t; -- - struct queue_task; - - typedef struct iscsi_login_context { -@@ -100,49 +111,25 @@ typedef struct iscsi_login_context { - - struct iscsi_session; - struct iscsi_conn; -- --typedef void (*send_pdu_begin_f) (uint64_t transport_handle, uint32_t sid, -- uint32_t cid, int hdr_size, int data_size); --typedef int (*send_pdu_end_f) (uint64_t transport_handle, uint32_t sid, -- uint32_t cid, int *retcode); --typedef int (*recv_pdu_begin_f) (uint64_t transport_handle, -- uintptr_t recv_handle, uintptr_t *pdu_handle, -- int *pdu_size); --typedef int (*recv_pdu_end_f) (uint64_t transport_handle, uintptr_t conn_handle, -- uintptr_t pdu_handle); --typedef void (*send_pdu_timer_add_f)(struct iscsi_conn *conn, int timeout); --typedef void (*send_pdu_timer_remove_f)(struct iscsi_conn *conn); -+struct iscsi_conn_context; - - /* daemon's connection structure */ - typedef struct iscsi_conn { -- struct qelem item; /* must stay at the top */ - uint32_t id; -- uintptr_t recv_handle; - struct iscsi_session *session; - iscsi_login_context_t login_context; -+ struct iscsi_conn_context *recv_context; - struct queue_task *logout_qtask; - char data[ISCSI_DEF_MAX_RECV_SEG_LEN]; - char host[NI_MAXHOST]; /* scratch */ - iscsi_conn_state_e state; -- actor_t connect_timer; -- actor_t send_pdu_timer; -- actor_t logout_timer; -- -- actor_t noop_out_timer; -- actor_t noop_out_timeout_timer; -- -- int send_pdu_in_progress; -+ int userspace_nop; - -- int kernel_io; -- send_pdu_begin_f send_pdu_begin; -- send_pdu_end_f send_pdu_end; -- recv_pdu_begin_f recv_pdu_begin; -- recv_pdu_end_f recv_pdu_end; -- send_pdu_timer_add_f send_pdu_timer_add; -- send_pdu_timer_remove_f send_pdu_timer_remove; -+ actor_t login_timer; -+ actor_t nop_out_timer; - --#define RECVPOOL_MAX 32 -- void* recvpool[RECVPOOL_MAX]; -+#define CONTEXT_POOL_MAX 32 -+ struct iscsi_conn_context *context_pool[CONTEXT_POOL_MAX]; - - /* login state machine */ - int current_stage; -@@ -169,8 +156,6 @@ typedef struct iscsi_conn { - int logout_timeout; - int auth_timeout; - int active_timeout; -- int idle_timeout; -- int ping_timeout; - - int noop_out_interval; - int noop_out_timeout; -@@ -185,11 +170,22 @@ typedef struct iscsi_conn { - uint32_t max_xmit_dlength; /* the value declared by the target */ - } iscsi_conn_t; - -+struct iscsi_conn_context { -+ struct actor actor; -+ struct iscsi_conn *conn; -+ int allocated; -+ void *data; -+}; -+ - typedef struct queue_task { - iscsi_conn_t *conn; - iscsiadm_req_t req; - iscsiadm_rsp_t rsp; - int mgmt_ipc_fd; -+ int allocated : 1; -+ /* Newer request types include a -+ * variable-length payload */ -+ void *payload; - } queue_task_t; - - struct iscsi_transport_template; -@@ -200,7 +196,6 @@ typedef struct iscsi_session { - struct list_head list; - uint32_t id; - uint32_t hostno; -- int refcount; - char netdev[IFNAMSIZ]; - struct iscsi_transport *t; - node_rec_t nrec; /* copy of original Node record in database */ -@@ -213,19 +208,17 @@ typedef struct iscsi_session { - int erl; - uint32_t imm_data_en; - uint32_t initial_r2t_en; -+ uint32_t fast_abort; - uint32_t first_burst; - uint32_t max_burst; - uint32_t pdu_inorder_en; - uint32_t dataseq_inorder_en; -- uint32_t time2wait; - uint32_t def_time2wait; - uint32_t def_time2retain; - int type; - int portal_group_tag; - uint8_t isid[6]; - uint16_t tsih; -- int channel; -- int target_id; - char target_name[TARGET_NAME_MAXLEN + 1]; - char *target_alias; - char *initiator_name; -@@ -247,7 +240,7 @@ typedef struct iscsi_session { - int password_in_length; - iscsi_conn_t conn[ISCSI_CONN_MAX]; - int ctrl_fd; -- uint32_t param_mask; -+ uint64_t param_mask; - - /* connection reopens during recovery */ - int reopen_cnt; -@@ -255,41 +248,14 @@ typedef struct iscsi_session { - iscsi_session_r_stage_e r_stage; - uint32_t replacement_timeout; - -+ int host_reset_timeout; -+ int lu_reset_timeout; -+ int abort_timeout; -+ - /* sync up fields */ - queue_task_t *sync_qtask; -- -- /* session's processing */ -- actor_t mainloop; -- queue_t *queue; -- queue_t *splice_queue; -- - } iscsi_session_t; - --/* -- * TODO: replace session_info and host_info with -- * node_rec and iface_rec -- */ --struct session_info { -- struct list_head list; -- /* local info */ -- struct iface_rec iface; -- int sid; -- -- /* remote info */ -- char targetname[TARGET_NAME_MAXLEN + 1]; -- int tpgt; -- char address[NI_MAXHOST + 1]; -- int port; -- char persistent_address[NI_MAXHOST + 1]; -- int persistent_port; --}; -- --struct host_info { -- char iname[TARGET_NAME_MAXLEN + 1]; -- struct iface_rec iface; -- int host_no; --}; -- - /* login.c */ - - #define ISCSI_SESSION_TYPE_NORMAL 0 -@@ -359,12 +325,14 @@ extern int iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - - /* initiator.c */ - extern int session_login_task(node_rec_t *rec, queue_task_t *qtask); --extern int session_logout_task(iscsi_session_t *session, queue_task_t *qtask); -+extern int session_logout_task(int sid, queue_task_t *qtask); - extern iscsi_session_t *session_find_by_sid(int sid); --extern iscsi_session_t *session_find_by_rec(node_rec_t *rec); --extern int session_is_running(node_rec_t *rec); --extern void* recvpool_get(iscsi_conn_t *conn, int ev_size); --extern void recvpool_put(iscsi_conn_t *conn, void *handle); -+extern struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn, -+ int ev_size); -+extern void iscsi_conn_context_put(struct iscsi_conn_context *conn_context); -+extern void iscsi_sched_conn_context(struct iscsi_conn_context *context, -+ struct iscsi_conn *conn, unsigned long tmo, -+ int event); - extern mgmt_ipc_err_e iscsi_sync_session(node_rec_t *rec, queue_task_t - *tsk, uint32_t sid); - extern mgmt_ipc_err_e iscsi_host_send_targets(queue_task_t *qtask, -diff --git a/usr/io.c b/usr/io.c -index bb06411..4efcb6f 100644 ---- a/usr/io.c -+++ b/usr/io.c -@@ -24,14 +24,8 @@ - #include - #include - #include --#include --#include - #include - #include --#include --#include --#include --#include - #include - #include - -@@ -39,11 +33,10 @@ - #include "iscsi_proto.h" - #include "initiator.h" - #include "iscsi_ipc.h" --#include "iscsi_sysfs.h" - #include "log.h" - #include "transport.h" --#include "iscsi_settings.h" - #include "idbm.h" -+#include "iface.h" - - #define LOG_CONN_CLOSED(conn) \ - do { \ -@@ -82,6 +75,8 @@ set_non_blocking(int fd) - - } - -+#if 0 -+/* not used by anyone */ - static int get_hwaddress_from_netdev(char *netdev, char *hwaddress) - { - struct ifaddrs *ifap, *ifa; -@@ -159,6 +154,7 @@ free_ifap: - freeifaddrs(ifap); - return found; - } -+#endif - - /* - * In this mode we do not support interfaces like a bond or alias because -@@ -166,15 +162,14 @@ free_ifap: - */ - static int get_netdev_from_hwaddress(char *hwaddress, char *netdev) - { -- struct ifaddrs *ifap, *ifa; -- struct sockaddr_in *s4; -- struct sockaddr_in6 *s6; -+ struct if_nameindex *ifni; - struct ifreq if_hwaddr; -- int found = 0, sockfd; -+ int found = 0, sockfd, i = 0; - unsigned char *hwaddr; -- char buf[INET6_ADDRSTRLEN], tmp_hwaddress[ISCSI_MAX_IFACE_LEN]; -+ char tmp_hwaddress[ISCSI_MAX_IFACE_LEN]; - -- if (getifaddrs(&ifap)) { -+ ifni = if_nameindex(); -+ if (ifni == NULL) { - log_error("Could not match hwaddress %s to netdev. " - "getifaddrs failed %d", hwaddress, errno); - return 0; -@@ -184,34 +179,13 @@ static int get_netdev_from_hwaddress(char *hwaddress, char *netdev) - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - log_error("Could not open socket for ioctl."); -- goto free_ifap; -+ goto free_ifni; - } - -- for (ifa = ifap; ifa; ifa = ifa->ifa_next) { -- if (!ifa->ifa_addr) -- continue; -+ for (i = 0; ifni[i].if_index && ifni[i].if_name; i++) { -+ struct if_nameindex *n = &ifni[i]; - -- switch (ifa->ifa_addr->sa_family) { -- case AF_INET: -- s4 = (struct sockaddr_in *)(ifa->ifa_addr); -- if (!inet_ntop(ifa->ifa_addr->sa_family, -- (void *)&(s4->sin_addr), buf, -- INET_ADDRSTRLEN)) -- continue; -- log_debug(4, "name %s addr %s\n", ifa->ifa_name, buf); -- break; -- case AF_INET6: -- s6 = (struct sockaddr_in6 *)(ifa->ifa_addr); -- if (!inet_ntop(ifa->ifa_addr->sa_family, -- (void *)&(s6->sin6_addr), buf, INET6_ADDRSTRLEN)) -- continue; -- log_debug(4, "name %s addr %s\n", ifa->ifa_name, buf); -- break; -- default: -- continue; -- } -- -- strncpy(if_hwaddr.ifr_name, ifa->ifa_name, IFNAMSIZ); -+ strncpy(if_hwaddr.ifr_name, n->if_name, IFNAMSIZ); - if (ioctl(sockfd, SIOCGIFHWADDR, &if_hwaddr) < 0) { - log_error("Could not match %s to netdevice.", - hwaddress); -@@ -231,17 +205,17 @@ static int get_netdev_from_hwaddress(char *hwaddress, char *netdev) - log_debug(4, "Found hardware address %s", tmp_hwaddress); - if (!strcasecmp(tmp_hwaddress, hwaddress)) { - log_debug(4, "Matches %s to %s", hwaddress, -- ifa->ifa_name); -+ n->if_name); - memset(netdev, 0, IFNAMSIZ); -- strncpy(netdev, ifa->ifa_name, IFNAMSIZ); -+ strncpy(netdev, n->if_name, IFNAMSIZ); - found = 1; - break; - } - } - - close(sockfd); --free_ifap: -- freeifaddrs(ifap); -+free_ifni: -+ if_freenameindex(ifni); - return found; - } - -@@ -569,7 +543,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - /* set a timeout, since the socket calls may take a long time - * to timeout on their own - */ -- if (!conn->kernel_io) { -+ if (!ipc) { - memset(&action, 0, sizeof (struct sigaction)); - memset(&old, 0, sizeof (struct sigaction)); - action.sa_sigaction = NULL; -@@ -627,23 +601,21 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - end = header + sizeof (*hdr) + hdr->hlength; - - /* send all the data and any padding */ -- if (pdu_length % PAD_WORD_LEN) -- pad_bytes = PAD_WORD_LEN - (pdu_length % PAD_WORD_LEN); -+ if (pdu_length % ISCSI_PAD_LEN) -+ pad_bytes = ISCSI_PAD_LEN - (pdu_length % ISCSI_PAD_LEN); - else - pad_bytes = 0; - -- if (conn->kernel_io) { -- conn->send_pdu_begin(session->t->handle, session->id, -- conn->id, end - header, -- ntoh24(hdr->dlength) + pad_bytes); -- conn->send_pdu_timer_add(conn, timeout); -- } -+ if (ipc) -+ ipc->send_pdu_begin(session->t->handle, session->id, -+ conn->id, end - header, -+ ntoh24(hdr->dlength) + pad_bytes); - - while (header < end) { - vec[0].iov_base = header; - vec[0].iov_len = end - header; - -- if (!conn->kernel_io) -+ if (!ipc) - rc = writev(session->ctrl_fd, vec, 1); - else - rc = ipc->writev(0, vec, 1); -@@ -671,7 +643,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - vec[1].iov_base = (void *) &pad; - vec[1].iov_len = pad_bytes; - -- if (!conn->kernel_io) -+ if (!ipc) - rc = writev(session->ctrl_fd, vec, 2); - else - rc = ipc->writev(0, vec, 2); -@@ -695,10 +667,9 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - } - } - -- if (conn->kernel_io) { -- if (conn->send_pdu_end(session->t->handle, session->id, -- conn->id, &rc)) { -- conn->send_pdu_timer_remove(conn); -+ if (ipc) { -+ if (ipc->send_pdu_end(session->t->handle, session->id, -+ conn->id, &rc)) { - ret = 0; - goto done; - } -@@ -707,7 +678,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - ret = 1; - - done: -- if (!conn->kernel_io) { -+ if (!ipc) { - alarm(0); - sigaction(SIGALRM, &old, NULL); - timedout = 0; -@@ -732,8 +703,6 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - char *end = data + max_data_length; - struct sigaction action; - struct sigaction old; -- uintptr_t pdu_handle; -- int pdu_size; - iscsi_session_t *session = conn->session; - - memset(data, 0, max_data_length); -@@ -741,7 +710,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - /* set a timeout, since the socket calls may take a long - * time to timeout on their own - */ -- if (!conn->kernel_io) { -+ if (!ipc) { - memset(&action, 0, sizeof (struct sigaction)); - memset(&old, 0, sizeof (struct sigaction)); - action.sa_sigaction = NULL; -@@ -751,8 +720,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - timedout = 0; - alarm(timeout); - } else { -- if (conn->recv_pdu_begin(session->ctrl_fd, -- conn->recv_handle, &pdu_handle, &pdu_size)) { -+ if (ipc->recv_pdu_begin(conn)) { - failed = 1; - goto done; - } -@@ -760,7 +728,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - - /* read a response header */ - do { -- if (!conn->kernel_io) -+ if (!ipc) - rlen = read(session->ctrl_fd, header, - sizeof (*hdr) - h_bytes); - else -@@ -807,7 +775,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - if (dlength == 0) - goto done; - -- if (data + dlength >= end) { -+ if (data + dlength > end) { - log_warning("buffer size %u too small for data length %u", - max_data_length, dlength); - failed = 1; -@@ -817,7 +785,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - /* read the rest into our buffer */ - d_bytes = 0; - while (d_bytes < dlength) { -- if (!conn->kernel_io) -+ if (!ipc) - rlen = read(session->ctrl_fd, data + d_bytes, - dlength - d_bytes); - else -@@ -843,10 +811,10 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - - /* handle PDU data padding. - * data is padded in case of kernel_io */ -- pad = dlength % PAD_WORD_LEN; -- if (pad && !conn->kernel_io) { -- int pad_bytes = pad = PAD_WORD_LEN - pad; -- char bytes[PAD_WORD_LEN]; -+ pad = dlength % ISCSI_PAD_LEN; -+ if (pad && !ipc) { -+ int pad_bytes = pad = ISCSI_PAD_LEN - pad; -+ char bytes[ISCSI_PAD_LEN]; - - while (pad_bytes > 0) { - rlen = read(conn->socket_fd, &bytes, pad_bytes); -@@ -900,16 +868,14 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, - } - - done: -- if (!conn->kernel_io) { -+ if (!ipc) { - alarm(0); - sigaction(SIGALRM, &old, NULL); - } else { - /* finalyze receive transaction */ -- if (conn->recv_pdu_end(session->ctrl_fd, (uintptr_t)conn, -- pdu_handle)) { -+ if (ipc->recv_pdu_end(conn)) { - failed = 1; - } -- conn->send_pdu_timer_remove(conn); - } - - if (timedout || failed) { -diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h -index 87324db..76c485f 100644 ---- a/usr/iscsi_ipc.h -+++ b/usr/iscsi_ipc.h -@@ -33,6 +33,8 @@ enum { - ISCSI_STRING, - }; - -+struct iscsi_conn; -+ - /** - * struct iscsi_ipc - Open-iSCSI Interface for Kernel IPC - * -@@ -60,6 +62,8 @@ struct iscsi_ipc { - - int (*destroy_session) (uint64_t transport_handle, uint32_t sid); - -+ int (*unbind_session) (uint64_t transport_handle, uint32_t sid); -+ - int (*create_conn) (uint64_t transport_handle, - uint32_t sid, uint32_t cid, uint32_t *out_cid); - -@@ -102,11 +106,9 @@ struct iscsi_ipc { - - int (*writev) (enum iscsi_uevent_e type, struct iovec *iovp, int count); - -- int (*recv_pdu_begin) (uint64_t transport_handle, uintptr_t recv_handle, -- uintptr_t *pdu_handle, int *pdu_size); -+ int (*recv_pdu_begin) (struct iscsi_conn *conn); - -- int (*recv_pdu_end) (uint64_t transport_handle, uintptr_t conn_handle, -- uintptr_t pdu_handle); -+ int (*recv_pdu_end) (struct iscsi_conn *conn); - }; - - #endif /* ISCSI_IPC_H */ -diff --git a/usr/iscsi_settings.h b/usr/iscsi_settings.h -index f1d7a1e..56f505f 100644 ---- a/usr/iscsi_settings.h -+++ b/usr/iscsi_settings.h -@@ -3,12 +3,16 @@ - * in the RFC. See iscsi_proto.h for those. - */ - /* timeouts in seconds */ --#define DEF_LOGIN_TIMEO 15 -+#define DEF_LOGIN_TIMEO 30 - #define DEF_LOGOUT_TIMEO 15 --#define DEF_NOOP_OUT_INTERVAL 10 --#define DEF_NOOP_OUT_TIMEO 15 -+#define DEF_NOOP_OUT_INTERVAL 5 -+#define DEF_NOOP_OUT_TIMEO 5 - #define DEF_REPLACEMENT_TIMEO 120 - -+#define DEF_ABORT_TIMEO 15 -+#define DEF_LU_RESET_TIMEO 30 -+#define DEF_HOST_RESET_TIMEO 60 -+ - /* q depths */ - #define CMDS_MAX 128 - #define QUEUE_DEPTH 32 -diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c -index c614192..7b65d6d 100644 ---- a/usr/iscsi_sysfs.c -+++ b/usr/iscsi_sysfs.c -@@ -21,40 +21,61 @@ - #include - #include - #include --#include - #include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include - - #include "log.h" - #include "initiator.h" - #include "transport.h" -+#include "idbm.h" - #include "version.h" - #include "iscsi_sysfs.h" --#include "list.h" - #include "iscsi_settings.h" -+#include "iface.h" - - #define ISCSI_TRANSPORT_DIR "/sys/class/iscsi_transport" - #define ISCSI_SESSION_DIR "/sys/class/iscsi_session" - #define ISCSI_CONN_DIR "/sys/class/iscsi_connection" - #define ISCSI_HOST_DIR "/sys/class/iscsi_host" -+#define SCSI_HOST_DIR "/sys/class/scsi_host" -+#define SCSI_DEVICE_DIR "/sys/bus/scsi/devices" - - #define ISCSI_MAX_SYSFS_BUFFER NI_MAXHOST - -+/* -+ * TODO: make this into a real API and check inputs better and add doc. -+ * We should also use a common lib and search sysfs according to the sysfs -+ * doc in the kernel documetnation. -+ */ -+ - /* tmp buffer used by sysfs functions */ - static char sysfs_file[PATH_MAX]; --int num_transports = 0; -+static int num_transports; - LIST_HEAD(transports); - -+/* mini implementation of versionsort for uclibc compatility */ -+int direntcmp(const void *d1, const void *d2) -+{ -+ const char *a = (*(const struct dirent **)d1)->d_name; -+ const char *b = (*(const struct dirent **)d2)->d_name; -+ while (*a && *b) { -+ int mask = (isdigit(*a) != 0) | ((isdigit(*b) != 0) << 1); -+ switch (mask) { -+ case 0: /* none are digit */ -+ if (*a == *b) -+ break; -+ return (*a - *b); -+ case 1: /* a is digit but not b */ -+ return -1; -+ case 2: /* b is digit but not a */ -+ return 1; -+ case 3: /* both ar digits */ -+ return atoi(a) - atoi(b); -+ } -+ a++; b++; -+ } -+ return *a - *b; -+} -+ - int read_sysfs_file(char *filename, void *value, char *format) - { - FILE *file; -@@ -64,7 +85,7 @@ int read_sysfs_file(char *filename, void *value, char *format) - file = fopen(filename, "r"); - if (file) { - line = fgets(buffer, sizeof(buffer), file); -- if (line) -+ if (line && strncmp(line, "", 6)) - sscanf(buffer, format, value); - else { - log_debug(5, "Could not read %s.\n", filename); -@@ -88,6 +109,21 @@ void free_transports(void) - } - } - -+int free_transport_by_handle(uint64_t handle) -+{ -+ struct iscsi_transport *t; -+ -+ list_for_each_entry(t, &transports, list) { -+ if (t->handle == handle) { -+ list_del(&t->list); -+ free(t); -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ - static int trans_filter(const struct dirent *dir) - { - return strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."); -@@ -103,7 +139,7 @@ static int read_transports(void) - log_debug(7, "in %s", __FUNCTION__); - - n = scandir(ISCSI_TRANSPORT_DIR, &namelist, trans_filter, -- versionsort); -+ direntcmp); - if (n < 0) { - log_error("Could not scan %s.", ISCSI_TRANSPORT_DIR); - return n; -@@ -373,19 +409,97 @@ uint32_t get_host_no_from_iface(struct iface_rec *iface, int *rc) - return host_no; - } - -+static int sysfs_read_iface(struct iface_rec *iface, int host_no, int sid) -+{ -+ struct iscsi_transport *t; -+ int ret; -+ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_HOST_DIR"/host%u/hwaddress", host_no); -+ /* -+ * backward compat -+ * If we cannot get the address we assume we are doing the old -+ * style and use default. -+ */ -+ sprintf(iface->hwaddress, DEFAULT_HWADDRESS); -+ ret = read_sysfs_file(sysfs_file, iface->hwaddress, "%s\n"); -+ if (ret) -+ log_debug(7, "could not read hwaddress for %s", sysfs_file); -+ -+ t = get_transport_by_hba(host_no); -+ if (!t) -+ log_debug(7, "could not get transport name for host %d", -+ host_no); -+ else -+ strcpy(iface->transport_name, t->name); -+ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_HOST_DIR"/host%u/ipaddress", host_no); -+ /* if not found just print out default */ -+ sprintf(iface->ipaddress, DEFAULT_IPADDRESS); -+ ret = read_sysfs_file(sysfs_file, iface->ipaddress, "%s\n"); -+ if (ret) -+ log_debug(7, "could not read local address for %s", -+ sysfs_file); -+ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_HOST_DIR"/host%u/netdev", host_no); -+ /* if not found just print out default */ -+ sprintf(iface->netdev, DEFAULT_NETDEV); -+ ret = read_sysfs_file(sysfs_file, iface->netdev, "%s\n"); -+ if (ret) -+ log_debug(7, "could not read netdev for %s", -+ sysfs_file); -+ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_HOST_DIR"/host%d/initiatorname", host_no); -+ ret = read_sysfs_file(sysfs_file, iface->iname, "%s\n"); -+ if (ret) -+ log_debug(7, "Could not read initiatorname for %s", -+ sysfs_file); -+ -+ /* -+ * this is on the session, because we support multiple bindings -+ * per device. -+ */ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_SESSION_DIR"/session%u/ifacename", sid); -+ /* -+ * this was added after 2.0.869 so we could be doing iscsi_tcp -+ * session binding, but there may not be a ifacename set -+ */ -+ memset(iface->name, 0, sizeof(iface->name)); -+ ret = read_sysfs_file(sysfs_file, iface->name, "%s\n"); -+ if (ret) { -+ log_debug(7, "could not read iface name for %s", -+ sysfs_file); -+ /* -+ * if the ifacename file is not there then we are using a older -+ * kernel and can try to find the binding by the net info -+ * which was used on these older kernels. -+ */ -+ if (!iface_is_bound_by_hwaddr(iface) && -+ !iface_is_bound_by_netdev(iface)) -+ sprintf(iface->name, DEFAULT_IFACENAME); -+ else if (iface_get_by_net_binding(iface, iface)) -+ log_debug(7, "Could not find iface for session bound " -+ "to:" iface_fmt "\n", iface_str(iface)); -+ } -+ return ret; -+} -+ - int sysfs_for_each_host(void *data, int *nr_found, sysfs_host_op_fn *fn) - { - struct dirent **namelist; - int rc = 0, i, n; - struct host_info *info; -- struct iscsi_transport *t; - - info = calloc(1, sizeof(*info)); - if (!info) - return ENOMEM; - - n = scandir(ISCSI_HOST_DIR, &namelist, trans_filter, -- versionsort); -+ direntcmp); - if (n <= 0) - goto free_info; - -@@ -397,44 +511,7 @@ int sysfs_for_each_host(void *data, int *nr_found, sysfs_host_op_fn *fn) - break; - } - -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/%s/initiatorname", -- namelist[i]->d_name); -- rc = read_sysfs_file(sysfs_file, info->iname, "%s\n"); -- if (rc) -- log_debug(4, "Could not read initiatorname for host " -- "%u. Error %d\n", info->host_no, rc); -- -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/%s/ipaddress", -- namelist[i]->d_name); -- rc = read_sysfs_file(sysfs_file, info->iface.ipaddress, "%s\n"); -- if (rc) -- log_debug(4, "Could not read ipaddress for host %u. " -- "Error %d\n", info->host_no, rc); -- -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/%s/hwaddress", -- namelist[i]->d_name); -- rc = read_sysfs_file(sysfs_file, info->iface.hwaddress, "%s\n"); -- if (rc) -- log_debug(4, "Could not read hwaddress for host %u. " -- "Error %d\n", info->host_no, rc); -- -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/%s/netdev", -- namelist[i]->d_name); -- rc = read_sysfs_file(sysfs_file, info->iface.netdev, "%s\n"); -- if (rc) -- log_debug(4, "Could not read netdev for host %u. " -- "Error %d\n", info->host_no, rc); -- -- t = get_transport_by_hba(info->host_no); -- if (!t) -- log_debug(4, "could not get transport name for host %d", -- info->host_no); -- else -- strcpy(info->iface.transport_name, t->name); -+ sysfs_read_iface(&info->iface, info->host_no, -1); - - rc = fn(data, info); - if (rc != 0) -@@ -451,11 +528,28 @@ free_info: - return rc; - } - -+/** -+ * sysfs_session_has_leadconn - checks if session has lead conn in kernel -+ * @sid: session id -+ * -+ * return 1 if session has lead conn and 0 if not. -+ */ -+int sysfs_session_has_leadconn(uint32_t sid) -+{ -+ struct stat statb; -+ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_CONN_DIR"/connection%u:0", sid); -+ if (!stat(sysfs_file, &statb)) -+ return 1; -+ else -+ return 0; -+} -+ - int get_sessioninfo_by_sysfs_id(struct session_info *info, char *session) - { - int ret, pers_failed = 0; - uint32_t host_no; -- struct iscsi_transport *t; - - if (sscanf(session, "session%d", &info->sid) != 1) { - log_error("invalid session '%s'", session); -@@ -542,51 +636,17 @@ int get_sessioninfo_by_sysfs_id(struct session_info *info, char *session) - return ret; - } - -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/host%u/hwaddress", host_no); -- /* -- * backward compat -- * If we cannot get the address we assume we are doing the old -- * style and use default. -- */ -- sprintf(info->iface.hwaddress, DEFAULT_HWADDRESS); -- ret = read_sysfs_file(sysfs_file, info->iface.hwaddress, "%s\n"); -- if (ret) -- log_debug(7, "could not read hwaddress for %s", sysfs_file); -- -- t = get_transport_by_sid(info->sid); -- if (!t) -- log_debug(7, "could not get transport name for session %d", -- info->sid); -- else -- strcpy(info->iface.transport_name, t->name); -- -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/host%u/ipaddress", host_no); -- /* if not found just print out default */ -- sprintf(info->iface.ipaddress, DEFAULT_IPADDRESS); -- ret = read_sysfs_file(sysfs_file, info->iface.ipaddress, "%s\n"); -- if (ret) -- log_debug(7, "could not read local address for %s", -- sysfs_file); -- -- memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, ISCSI_HOST_DIR"/host%u/netdev", host_no); -- /* if not found just print out default */ -- sprintf(info->iface.netdev, DEFAULT_NETDEV); -- ret = read_sysfs_file(sysfs_file, info->iface.netdev, "%s\n"); -- if (ret) -- log_debug(7, "could not read netdev for %s", -- sysfs_file); -- -+ sysfs_read_iface(&info->iface, host_no, info->sid); -+ - log_debug(7, "found targetname %s address %s pers address %s port %d " -- "pers port %d driver %s iface ipaddress %s " -- "netdev %s hwaddress %s", -+ "pers port %d driver %s iface name %s ipaddress %s " -+ "netdev %s hwaddress %s iname %s", - info->targetname, info->address ? info->address : "NA", - info->persistent_address ? info->persistent_address : "NA", -- info->port, info->persistent_port, -- info->iface.transport_name, info->iface.ipaddress, -- info->iface.netdev, info->iface.hwaddress); -+ info->port, info->persistent_port, info->iface.transport_name, -+ info->iface.name, info->iface.ipaddress, -+ info->iface.netdev, info->iface.hwaddress, -+ info->iface.iname); - return 0; - } - -@@ -602,7 +662,7 @@ int sysfs_for_each_session(void *data, int *nr_found, sysfs_session_op_fn *fn) - - sprintf(sysfs_file, ISCSI_SESSION_DIR); - n = scandir(sysfs_file, &namelist, trans_filter, -- versionsort); -+ direntcmp); - if (n <= 0) - goto free_info; - -@@ -633,17 +693,24 @@ free_info: - return rc; - } - -+int get_session_state(char *state, int sid) -+{ -+ memset(sysfs_file, 0, PATH_MAX); -+ sprintf(sysfs_file, ISCSI_SESSION_DIR"/session%d/state", sid); -+ return read_sysfs_file(sysfs_file, state, "%s\n"); -+} -+ - int get_host_state(char *state, int host_no) - { - memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, "/sys/class/scsi_host/host%d/state", host_no); -+ sprintf(sysfs_file, SCSI_HOST_DIR"/host%d/state", host_no); - return read_sysfs_file(sysfs_file, state, "%s\n"); - } - - int get_device_state(char *state, int host_no, int target, int lun) - { - memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, "/sys/bus/scsi/devices/%d:0:%d:%d/state", -+ sprintf(sysfs_file, SCSI_DEVICE_DIR"/%d:0:%d:%d/state", - host_no, target, lun); - return read_sysfs_file(sysfs_file, state, "%s\n"); - } -@@ -654,7 +721,7 @@ char *get_blockdev_from_lun(int host_no, int target, int lun) - struct dirent *dent; - char *blockdev, *blockdup = NULL; - -- sprintf(sysfs_file, "/sys/bus/scsi/devices/%d:0:%d:%d", -+ sprintf(sysfs_file, SCSI_DEVICE_DIR"/%d:0:%d:%d", - host_no, target, lun); - dirfd = opendir(sysfs_file); - if (!dirfd) -@@ -741,7 +808,7 @@ struct iscsi_transport *get_transport_by_hba(long host_no) - return NULL; - - memset(sysfs_file, 0, PATH_MAX); -- sprintf(sysfs_file, "/sys/class/scsi_host/host%lu/proc_name", host_no); -+ sprintf(sysfs_file, SCSI_HOST_DIR"/host%lu/proc_name", host_no); - rc = read_sysfs_file(sysfs_file, name, "%s\n"); - if (rc) { - log_error("Could not read %s rc %d.", sysfs_file, rc); -@@ -802,7 +869,7 @@ int sysfs_for_each_device(int host_no, uint32_t sid, - sprintf(sysfs_file, ISCSI_SESSION_DIR"/session%d/device/target%d:0:%d", - sid, host_no, target); - n = scandir(sysfs_file, &namelist, trans_filter, -- versionsort); -+ direntcmp); - if (n <= 0) - return 0; - -@@ -824,7 +891,7 @@ void set_device_online(int hostno, int target, int lun) - { - int fd; - -- sprintf(sysfs_file, "/sys/bus/scsi/devices/%d:0:%d:%d/state", -+ sprintf(sysfs_file, SCSI_DEVICE_DIR"/%d:0:%d:%d/state", - hostno, target, lun); - fd = open(sysfs_file, O_WRONLY); - if (fd < 0) -@@ -841,7 +908,7 @@ void delete_device(int hostno, int target, int lun) - { - int fd; - -- sprintf(sysfs_file, "/sys/bus/scsi/devices/%d:0:%d:%d/delete", -+ sprintf(sysfs_file, SCSI_DEVICE_DIR"/%d:0:%d:%d/delete", - hostno, target, lun); - fd = open(sysfs_file, O_WRONLY); - if (fd < 0) -@@ -851,12 +918,26 @@ void delete_device(int hostno, int target, int lun) - close(fd); - } - -+void rescan_device(int hostno, int target, int lun) -+{ -+ int fd; -+ -+ sprintf(sysfs_file, SCSI_DEVICE_DIR"/%d:0:%d:%d/rescan", -+ hostno, target, lun); -+ fd = open(sysfs_file, O_WRONLY); -+ if (fd < 0) -+ return; -+ log_debug(4, "rescanning device using %s", sysfs_file); -+ write(fd, "1", 1); -+ close(fd); -+} -+ - pid_t scan_host(int hostno, int async) - { - pid_t pid = 0; - int fd; - -- sprintf(sysfs_file, "/sys/class/scsi_host/host%d/scan", -+ sprintf(sysfs_file, SCSI_HOST_DIR"/host%d/scan", - hostno); - fd = open(sysfs_file, O_WRONLY); - if (fd < 0) { -@@ -907,7 +988,7 @@ int get_iscsi_kernel_version(char *buf) - return 0; - } - --void check_class_version(void) -+int check_class_version(void) - { - char version[20]; - int i; -@@ -933,12 +1014,12 @@ void check_class_version(void) - if (!strncmp(version, ISCSI_VERSION_STR, i) || - /* support 2.6.18 */ - !strncmp(version, "1.1", 3)) -- return; -+ return 0; - - fail: - log_error( "Missing or Invalid version from %s. Make sure a up " - "to date scsi_transport_iscsi module is loaded and a up to" - "date version of iscsid is running. Exiting...", - ISCSI_VERSION_FILE); -- exit(1); -+ return -1; - } -diff --git a/usr/iscsi_sysfs.h b/usr/iscsi_sysfs.h -index 827b7c1..4282a16 100644 ---- a/usr/iscsi_sysfs.h -+++ b/usr/iscsi_sysfs.h -@@ -17,9 +17,12 @@ - #ifndef ISCSI_SYSFS_H - #define ISCSI_SYSFS_H - --#include - #include - -+#include "types.h" -+#include "iscsi_proto.h" -+#include "config.h" -+ - struct iscsi_session; - struct iscsi_conn; - struct iscsi_session_operational_config; -@@ -28,11 +31,33 @@ struct iscsi_auth_config; - - #define SCSI_MAX_STATE_VALUE 32 - -+struct session_info { -+ struct list_head list; -+ /* local info */ -+ struct iface_rec iface; -+ int sid; -+ -+ /* remote info */ -+ char targetname[TARGET_NAME_MAXLEN + 1]; -+ int tpgt; -+ char address[NI_MAXHOST + 1]; -+ int port; -+ char persistent_address[NI_MAXHOST + 1]; -+ int persistent_port; -+}; -+ -+struct host_info { -+ struct iface_rec iface; -+ int host_no; -+}; -+ -+extern int direntcmp(const void *d1, const void *d2); - extern void free_transports(void); - extern int get_iscsi_kernel_version(char *buf); --extern void check_class_version(void); -+extern int check_class_version(void); - extern int get_sessioninfo_by_sysfs_id(struct session_info *info, - char *sys_session); -+extern int sysfs_session_has_leadconn(uint32_t sid); - - typedef int (sysfs_session_op_fn)(void *, struct session_info *); - typedef int (sysfs_host_op_fn)(void *, struct host_info *); -@@ -56,18 +81,20 @@ extern void get_negotiated_session_conf(int sid, - extern void get_negotiated_conn_conf(int sid, - struct iscsi_conn_operational_config *conf); - extern pid_t scan_host(int hostno, int async); -+extern int get_session_state(char *state, int sid); - extern int get_host_state(char *state, int host_no); - extern int get_device_state(char *state, int host_no, int target, int lun); - extern void set_device_online(int hostno, int target, int lun); - extern void delete_device(int hostno, int target, int lun); -+extern void rescan_device(int hostno, int target, int lun); - extern int sysfs_for_each_device(int host_no, uint32_t sid, - void (* fn)(int host_no, int target, int lun)); - extern struct iscsi_transport *get_transport_by_hba(long host_no); - extern struct iscsi_transport *get_transport_by_session(char *sys_session); - extern struct iscsi_transport *get_transport_by_sid(uint32_t sid); - extern struct iscsi_transport *get_transport_by_name(char *transport_name); -+extern int free_transport_by_handle(uint64_t handle); - - extern struct list_head transports; --extern int num_transports; - - #endif -diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c -index 1e706b9..5b5aa71 100644 ---- a/usr/iscsiadm.c -+++ b/usr/iscsiadm.c -@@ -19,20 +19,14 @@ - * See the file COPYING included with this distribution for more details. - */ - --#include - #include --#include - #include - #include - #include - #include - #include - #include --#include --#include - #include --#include --#include - - #include "initiator.h" - #include "iscsiadm.h" -@@ -45,13 +39,12 @@ - #include "iscsi_sysfs.h" - #include "list.h" - #include "iscsi_settings.h" -+#include "fw_context.h" -+#include "iface.h" - - struct iscsi_ipc *ipc = NULL; /* dummy */ - static char program_name[] = "iscsiadm"; -- --char initiator_name[TARGET_NAME_MAXLEN]; --char initiator_alias[TARGET_NAME_MAXLEN]; --char config_file[TARGET_NAME_MAXLEN]; -+static char config_file[TARGET_NAME_MAXLEN]; - - enum iscsiadm_mode { - MODE_DISCOVERY, -@@ -59,13 +52,15 @@ enum iscsiadm_mode { - MODE_SESSION, - MODE_HOST, - MODE_IFACE, -+ MODE_FW, - }; - - enum iscsiadm_op { -- OP_NEW, -- OP_DELETE, -- OP_UPDATE, -- OP_SHOW, -+ OP_NOOP = 0x0, -+ OP_NEW = 0x1, -+ OP_DELETE = 0x2, -+ OP_UPDATE = 0x4, -+ OP_SHOW = 0x8, - }; - - static struct option const long_options[] = -@@ -86,13 +81,14 @@ static struct option const long_options[] = - {"logout", no_argument, NULL, 'u'}, - {"logoutall", required_argument, NULL, 'U'}, - {"stats", no_argument, NULL, 's'}, -- {"debug", required_argument, NULL, 'g'}, -+ {"killiscsid", required_argument, NULL, 'k'}, -+ {"debug", required_argument, NULL, 'd'}, - {"show", no_argument, NULL, 'S'}, - {"version", no_argument, NULL, 'V'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}, - }; --static char *short_options = "RlVhm:p:P:T:H:I:U:L:d:r:n:v:o:sSt:u"; -+static char *short_options = "RlVhm:p:P:T:H:I:U:k:L:d:r:n:v:o:sSt:u"; - - static void usage(int status) - { -@@ -101,12 +97,14 @@ static void usage(int status) - program_name); - else { - printf("\ --iscsiadm -m discovery [ -dhV ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -l ] ] | [ -p ip:port ] \ -+iscsiadm -m discovery [ -hV ] [ -d debug_level ] [-P printlevel] [ -t type -p ip:port -I ifaceN ... [ -l ] ] | [ -p ip:port ] \ - [ -o operation ] [ -n name ] [ -v value ]\n\ --iscsiadm -m node [ -dhV ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I ifaceN ] [ -l | -u | -R | -s] ] \ -+iscsiadm -m node [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port -I ifaceN ] [ -l | -u | -R | -s] ] \ - [ [ -o operation ] [ -n name ] [ -v value ] ]\n\ --iscsiadm -m session [ -dhV ] [ -P printlevel] [ -r sessionid | sysfsdir [ -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n\ --iscsiadm -m iface [ -dhV ] [ -P printlevel ] [ -I ifacename ] [ [ -o operation ] [ -n name ] [ -v value ] ]\n"); -+iscsiadm -m session [ -hV ] [ -d debug_level ] [ -P printlevel] [ -r sessionid | sysfsdir [ -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n\ -+iscsiadm -m iface [ -hV ] [ -d debug_level ] [ -P printlevel ] [ -I ifacename ] [ [ -o operation ] [ -n name ] [ -v value ] ]\n\ -+iscsiadm -m fw [ -l ]\n\ -+iscsiadm -k priority\n"); - } - exit(status == 0 ? 0 : -1); - } -@@ -125,7 +123,7 @@ str_to_op(char *str) - else if (!strcmp("show", str)) - op = OP_SHOW; - else -- op = -1; -+ op = OP_NOOP; - - return op; - } -@@ -143,6 +141,8 @@ str_to_mode(char *str) - mode = MODE_SESSION; - else if (!strcmp("iface", str)) - mode = MODE_IFACE; -+ else if (!strcmp("fw", str)) -+ mode = MODE_FW; - else - mode = -1; - -@@ -161,28 +161,70 @@ str_to_type(char *str) - type = DISCOVERY_TYPE_SLP; - else if (!strcmp("isns", str)) - type = DISCOVERY_TYPE_ISNS; -+ else if (!strcmp("fw", str)) -+ type = DISCOVERY_TYPE_FWBOOT; - else - type = -1; - - return type; - } - -+static void kill_iscsid(int priority) -+{ -+ iscsiadm_req_t req; -+ iscsiadm_rsp_t rsp; -+ int rc; -+ -+ /* -+ * We only support SIGTERM like stoppage of iscsid for now. -+ * In the future we can do something where we try go finish -+ * up operations like login, error handling, etc, before -+ * iscsid is stopped, and we can add different values to indicate -+ * that the user wants iscsid to log out existing sessions before -+ * exiting. -+ */ -+ if (priority != 0) { -+ log_error("Invalid iscsid priority %d. Priority must be 0.", -+ priority); -+ return; -+ } -+ -+ memset(&req, 0, sizeof(req)); -+ req.command = MGMT_IPC_IMMEDIATE_STOP; -+ rc = do_iscsid(&req, &rsp); -+ if (rc) { -+ iscsid_handle_error(rc); -+ log_error("Could not stop iscsid. Trying sending iscsid " -+ "SIGTERM or SIGKILL signals manually\n"); -+ } -+} -+ - /* -- * TODO: when we get more time we can make add what sessions -- * are connected to the host. For now you can see this in session -- * mode although with -P 3, althought it is not nicely structured -- * like how you would want -+ * TODO: we can display how the ifaces are related to node records. -+ * And we can add a scsi_host mode which would display how -+ * sessions are related to hosts -+ * (scsi_host and iscsi_sessions are the currently running instance of -+ * a iface or node record). - */ --static int print_ifaces(idbm_t *db, int info_level) -+static int print_ifaces(int info_level) - { - int err, num_found = 0; - -- if (info_level > 0) { -- log_error("Invalid info level %d. Try 0.", info_level); -+ switch (info_level) { -+ case 0: -+ case -1: -+ err = iface_for_each_iface(NULL, &num_found, -+ iface_print_flat); -+ break; -+ case 1: -+ err = iface_for_each_iface(NULL, &num_found, -+ iface_print_tree); -+ break; -+ default: -+ log_error("Invalid info level %d. Try 0 or 1.", info_level); - return EINVAL; - } - -- err = iface_for_each_iface(db, NULL, &num_found, iface_print_flat); - if (!num_found) { - log_error("No interfaces found."); - err = ENODEV; -@@ -191,59 +233,6 @@ static int print_ifaces(idbm_t *db, int info_level) - } - - static int --session_login(node_rec_t *rec) --{ -- iscsiadm_req_t req; -- iscsiadm_rsp_t rsp; -- -- memset(&req, 0, sizeof(iscsiadm_req_t)); -- req.command = MGMT_IPC_SESSION_LOGIN; -- memcpy(&req.u.session.rec, rec, sizeof(node_rec_t)); -- -- return do_iscsid(&req, &rsp); --} -- --static int --__delete_target(void *data, struct session_info *info) --{ -- node_rec_t *rec = data; -- uint32_t host_no; -- int err; -- -- log_debug(6, "looking for session [%s,%s,%d]", -- rec->name, rec->conn[0].address, rec->conn[0].port); -- -- if (iscsi_match_session(rec, info)) { -- host_no = get_host_no_from_sid(info->sid, &err); -- if (err) { -- log_error("Could not properly delete target\n"); -- return EIO; -- } -- -- sysfs_for_each_device(host_no, info->sid, delete_device); -- return 0; -- } -- -- /* keep on looking */ -- return 0; --} -- --static int --session_logout(node_rec_t *rec) --{ -- iscsiadm_req_t req; -- iscsiadm_rsp_t rsp; -- int num_found = 0; -- -- memset(&req, 0, sizeof(req)); -- req.command = MGMT_IPC_SESSION_LOGOUT; -- memcpy(&req.u.session.rec, rec, sizeof(node_rec_t)); -- -- sysfs_for_each_session(rec, &num_found, __delete_target); -- return do_iscsid(&req, &rsp); --} -- --static int - match_startup_mode(node_rec_t *rec, char *mode) - { - /* -@@ -271,37 +260,243 @@ match_startup_mode(node_rec_t *rec, char *mode) - return -1; - } - --struct session_mgmt_fn { -- idbm_t *db; -- char *mode; -+struct iscsid_async_req { -+ struct list_head list; -+ void *data; -+ int fd; - }; - -+static int iscsid_login_reqs_wait(struct list_head *list) -+{ -+ struct iscsid_async_req *tmp, *curr; -+ struct node_rec *rec; -+ int ret = 0; -+ -+ list_for_each_entry_safe(curr, tmp, list, list) { -+ int err; -+ -+ rec = curr->data; -+ err = iscsid_req_wait(MGMT_IPC_SESSION_LOGIN, curr->fd); -+ if (err) { -+ log_error("Could not login to [iface: %s, target: %s, " -+ "portal: %s,%d]: ", rec->iface.name, -+ rec->name, rec->conn[0].address, -+ rec->conn[0].port); -+ iscsid_handle_error(err); -+ ret = err; -+ } else -+ printf("Login to [iface: %s, target: %s, portal: " -+ "%s,%d]: successful\n", rec->iface.name, -+ rec->name, rec->conn[0].address, -+ rec->conn[0].port); -+ list_del(&curr->list); -+ free(curr); -+ } -+ return ret; -+} -+static int iscsid_logout_reqs_wait(struct list_head *list) -+{ -+ struct iscsid_async_req *tmp, *curr; -+ struct session_info *info; -+ int ret = 0; -+ -+ list_for_each_entry_safe(curr, tmp, list, list) { -+ int err; -+ -+ info = curr->data; -+ err = iscsid_req_wait(MGMT_IPC_SESSION_LOGOUT, curr->fd); -+ if (err) { -+ log_error("Could not logout of [sid: %d, target: %s, " -+ "portal: %s,%d]: ", info->sid, -+ info->targetname, -+ info->persistent_address, info->port); -+ iscsid_handle_error(err); -+ ret = err; -+ } else -+ printf("Logout of [sid: %d, target: %s, " -+ "portal: %s,%d]: successful\n", -+ info->sid, info->targetname, -+ info->persistent_address, info->port); -+ list_del(&curr->list); -+ free(curr); -+ } -+ return ret; -+} -+ - static int --__logout_by_startup(void *data, struct session_info *info) -+for_each_session(struct node_rec *rec, sysfs_session_op_fn *fn) - { -- struct session_mgmt_fn *mgmt = data; -- char *mode = mgmt->mode; -- idbm_t *db = mgmt->db; -- node_rec_t rec; -- int rc = 0; -+ int err, num_found = 0; - -- if (iface_get_by_bind_info(db, &info->iface, &rec.iface)) { -- /* -- * If someone removed the /etc/iscsi/ifaces file -- * between logins then this will fail. -- * -- * To support that, we would have to throw our ifacename -- * into the kernel. -- */ -- log_debug(7, "could not read data for [%s,%s.%d]\n", -- info->targetname, info->persistent_address, -- info->persistent_port); -- return -1; -+ err = sysfs_for_each_session(rec, &num_found, fn); -+ if (err) -+ log_error("Could not execute operation on all sessions. Err " -+ "%d.", err); -+ else if (!num_found) { -+ log_error("No portal found."); -+ err = ENODEV; -+ } -+ -+ return err; -+} -+ -+struct session_link_info { -+ struct list_head *list; -+ struct session_info *match_info; -+}; -+ -+static int link_sessions(void *data, struct session_info *info) -+{ -+ struct session_link_info *link_info = data; -+ struct list_head *list = link_info->list; -+ struct session_info *new, *curr, *match = NULL; -+ -+ if (link_info->match_info && link_info->match_info->sid != info->sid) -+ return 0; -+ -+ new = calloc(1, sizeof(*new)); -+ if (!new) -+ return ENOMEM; -+ memcpy(new, info, sizeof(*new)); -+ INIT_LIST_HEAD(&new->list); -+ -+ if (list_empty(list)) { -+ list_add_tail(&new->list, list); -+ return 0; -+ } -+ -+ list_for_each_entry(curr, list, list) { -+ if (!strcmp(curr->targetname, info->targetname)) { -+ match = curr; -+ -+ if (!strcmp(curr->address, info->address)) { -+ match = curr; -+ -+ if (curr->port == info->port) { -+ match = curr; -+ break; -+ } -+ } -+ } -+ } -+ -+ list_add_tail(&new->list, match ? match->list.next : list); -+ return 0; -+} -+ -+static int link_recs(void *data, struct node_rec *rec) -+{ -+ struct list_head *list = data; -+ struct node_rec *rec_copy; -+ -+ rec_copy = calloc(1, sizeof(*rec_copy)); -+ if (!rec_copy) -+ return ENOMEM; -+ memcpy(rec_copy, rec, sizeof(*rec_copy)); -+ INIT_LIST_HEAD(&rec_copy->list); -+ list_add_tail(&rec_copy->list, list); -+ return 0; -+} -+ -+static int __logout_portal(struct session_info *info, struct list_head *list) -+{ -+ struct iscsid_async_req *async_req = NULL; -+ int fd, rc; -+ -+ /* TODO: add fn to add session prefix info like dev_printk */ -+ printf("Logging out of session [sid: %d, target: %s, portal: %s,%d]\n", -+ info->sid, info->targetname, info->persistent_address, -+ info->port); -+ -+ async_req = calloc(1, sizeof(*async_req)); -+ if (!async_req) { -+ log_error("Could not allocate memory for async logout " -+ "handling. Using sequential logout instead."); -+ rc = iscsid_req_by_sid(MGMT_IPC_SESSION_LOGOUT, info->sid); -+ } else { -+ INIT_LIST_HEAD(&async_req->list); -+ rc = iscsid_req_by_sid_async(MGMT_IPC_SESSION_LOGOUT, -+ info->sid, &fd); - } - -- if (idbm_rec_read(db, &rec, info->targetname, info->tpgt, -+ /* we raced with another app or instance of iscsiadm */ -+ switch (rc) { -+ case MGMT_IPC_ERR_NOT_FOUND: -+ rc = 0; -+ break; -+ case MGMT_IPC_OK: -+ if (async_req) { -+ list_add_tail(&async_req->list, list); -+ async_req->fd = fd; -+ async_req->data = info; -+ } -+ return 0; -+ default: -+ iscsid_handle_error(rc); -+ rc = EIO; -+ break; -+ } -+ -+ if (async_req) -+ free(async_req); -+ return rc; -+} -+ -+static int -+__logout_portals(void *data, int *nr_found, -+ int (*logout_fn)(void *, struct list_head *, -+ struct session_info *)) -+{ -+ struct session_info *curr_info, *tmp; -+ struct session_link_info link_info; -+ struct list_head session_list, logout_list; -+ int ret = 0, err; -+ -+ INIT_LIST_HEAD(&session_list); -+ INIT_LIST_HEAD(&logout_list); -+ -+ memset(&link_info, 0, sizeof(link_info)); -+ link_info.list = &session_list; -+ link_info.match_info = NULL; -+ *nr_found = 0; -+ -+ err = sysfs_for_each_session(&link_info, nr_found, -+ link_sessions); -+ if (err || !*nr_found) -+ return err; -+ -+ *nr_found = 0; -+ list_for_each_entry(curr_info, &session_list, list) { -+ err = logout_fn(data, &logout_list, curr_info); -+ if (err > 0 && !ret) -+ ret = err; -+ if (!err) -+ (*nr_found)++; -+ } -+ -+ err = iscsid_logout_reqs_wait(&logout_list); -+ if (err) -+ ret = err; -+ -+ list_for_each_entry_safe(curr_info, tmp, &session_list, list) { -+ list_del(&curr_info->list); -+ free(curr_info); -+ } -+ return ret; -+} -+ -+static int -+__logout_by_startup(void *data, struct list_head *list, -+ struct session_info *info) -+{ -+ char *mode = data; -+ node_rec_t rec; -+ int rc = 0; -+ -+ memset(&rec, 0, sizeof(node_rec_t)); -+ if (idbm_rec_read(&rec, info->targetname, info->tpgt, - info->persistent_address, -- info->persistent_port, &rec.iface)) { -+ info->persistent_port, &info->iface)) { - /* - * this is due to a HW driver or some other driver - * not hooked in -@@ -313,9 +508,8 @@ __logout_by_startup(void *data, struct session_info *info) - } - - /* multiple drivers could be connected to the same portal */ -- if (!iscsi_match_session(&rec, info)) -+ if (strcmp(rec.iface.transport_name, info->iface.transport_name)) - return -1; -- - /* - * we always skip on boot because if the user killed this on - * they would not be able to do anything -@@ -323,29 +517,15 @@ __logout_by_startup(void *data, struct session_info *info) - if (rec.startup == ISCSI_STARTUP_ONBOOT) - return -1; - -- if (!match_startup_mode(&rec, mode)) { -- printf("Logout session [sid: %d, target: %s, portal: " -- "%s,%d]\n", info->sid, info->targetname, -- info->persistent_address, info->port); -- -- rc = session_logout(&rec); -- /* we raced with another app or instance of iscsiadm */ -- if (rc == MGMT_IPC_ERR_NOT_FOUND) -- rc = 0; -- if (rc) { -- iscsid_handle_error(rc); -- rc = EIO; -- } -- } -- -+ if (!match_startup_mode(&rec, mode)) -+ rc = __logout_portal(info, list); - return rc; - } - - static int --logout_by_startup(idbm_t *db, char *mode) -+logout_by_startup(char *mode) - { -- int num_found; -- struct session_mgmt_fn mgmt; -+ int nr_found; - - if (!mode || !(!strcmp(mode, "automatic") || !strcmp(mode, "all") || - !strcmp(mode,"manual"))) { -@@ -354,24 +534,20 @@ logout_by_startup(idbm_t *db, char *mode) - return EINVAL; - } - -- mgmt.mode = mode; -- mgmt.db = db; -- -- return sysfs_for_each_session(&mgmt, &num_found, __logout_by_startup); -+ return __logout_portals(mode, &nr_found, __logout_by_startup); - } - - static int --logout_portal(void *data, struct session_info *info) -+logout_portal(void *data, struct list_head *list, struct session_info *info) - { -- node_rec_t tmprec, *rec = data; -+ struct node_rec *pattern_rec = data; - struct iscsi_transport *t; -- int rc; - - t = get_transport_by_sid(info->sid); - if (!t) - return -1; - -- if (!iscsi_match_session(rec, info)) -+ if (!iscsi_match_session(pattern_rec, info)) - return -1; - - /* we do not support this yet */ -@@ -383,33 +559,19 @@ logout_portal(void *data, struct session_info *info) - log_error("Logout not supported for driver: %s.", t->name); - return -1; - } -+ return __logout_portal(info, list); -+} - -- /* TODO: add fn to add session prefix info like dev_printk */ -- printf("Logout session [sid: %d, target: %s, portal: %s,%d]\n", -- info->sid, info->targetname, info->persistent_address, -- info->port); -- -- memset(&tmprec, 0, sizeof(node_rec_t)); -- strncpy(tmprec.name, info->targetname, TARGET_NAME_MAXLEN); -- tmprec.conn[0].port = info->persistent_port; -- strncpy(tmprec.conn[0].address, info->persistent_address, NI_MAXHOST); -- memcpy(&tmprec.iface, &info->iface, sizeof(struct iface_rec)); -- -- rc = session_logout(&tmprec); -- /* we raced with another app or instance of iscsiadm */ -- if (rc == MGMT_IPC_ERR_NOT_FOUND) -- rc = 0; -- if (rc) { -- iscsid_handle_error(rc); -- rc = EIO; -- } -+static int logout_portals(struct node_rec *pattern_rec) -+{ -+ int nr_found; - -- return rc; -+ return __logout_portals(pattern_rec, &nr_found, logout_portal); - } - - static struct node_rec * --create_node_record(idbm_t *db, char *targetname, int tpgt, char *ip, int port, -- struct iface_rec *iface) -+create_node_record(char *targetname, int tpgt, char *ip, int port, -+ struct iface_rec *iface, int verbose) - { - struct node_rec *rec; - -@@ -429,10 +591,14 @@ create_node_record(idbm_t *db, char *targetname, int tpgt, char *ip, int port, - strncpy(rec->conn[0].address, ip, NI_MAXHOST); - memset(&rec->iface, 0, sizeof(struct iface_rec)); - if (iface) { -- if (iface_get_by_bind_info(db, iface, &rec->iface)) { -- log_error("Could not find iface info for %s.", -- iface->name); -- goto free_rec; -+ iface_copy(&rec->iface, iface); -+ if (strlen(iface->name)) { -+ if (iface_conf_read(&rec->iface)) { -+ if (verbose) -+ log_error("Could not read iface info " -+ "for %s.", iface->name); -+ goto free_rec; -+ } - } - } - return rec; -@@ -441,58 +607,89 @@ free_rec: - return NULL; - } - --static int --for_each_session(struct node_rec *rec, sysfs_session_op_fn *fn) -+static int login_portal(void *data, struct list_head *list, -+ struct node_rec *rec) - { -- int err, num_found = 0; -+ struct iscsid_async_req *async_req = NULL; -+ int rc = 0, fd; - -- err = sysfs_for_each_session(rec, &num_found, fn); -- if (err) -- log_error("Could not execute operation on all sessions. Err " -- "%d.", err); -- else if (!num_found) { -- log_error("No portal found."); -- err = ENODEV; -+ printf("Logging in to [iface: %s, target: %s, portal: %s,%d]\n", -+ rec->iface.name, rec->name, rec->conn[0].address, -+ rec->conn[0].port); -+ -+ if (list) { -+ async_req = calloc(1, sizeof(*async_req)); -+ if (!async_req) -+ log_error("Could not allocate memory for async login " -+ "handling. Using sequential login instead."); -+ else -+ INIT_LIST_HEAD(&async_req->list); - } - -- return err; -+ if (async_req) -+ rc = iscsid_req_by_rec_async(MGMT_IPC_SESSION_LOGIN, -+ rec, &fd); -+ else -+ rc = iscsid_req_by_rec(MGMT_IPC_SESSION_LOGIN, rec); -+ /* we raced with another app or instance of iscsiadm */ -+ if (rc == MGMT_IPC_ERR_EXISTS) { -+ if (async_req) -+ free(async_req); -+ return 0; -+ } else if (rc) { -+ iscsid_handle_error(rc); -+ if (async_req) -+ free(async_req); -+ return ENOTCONN; -+ } -+ -+ if (async_req) { -+ list_add_tail(&async_req->list, list); -+ async_req->fd = fd; -+ async_req->data = rec; -+ } -+ return 0; - } - --static int login_portal(idbm_t *db, void *data, node_rec_t *rec) -+static int __login_portals(void *data, int *nr_found, -+ struct list_head *rec_list, -+ int (* login_fn)(void *, struct list_head *, -+ struct node_rec *)) - { -- int rc = 0, i; -+ struct node_rec *curr_rec, *tmp; -+ struct list_head login_list; -+ int ret = 0, err; - -- printf("Login session [iface: %s, target: %s, portal: %s,%d]\n", -- rec->iface.name, rec->name, rec->conn[0].address, -- rec->conn[0].port); -+ *nr_found = 0; -+ INIT_LIST_HEAD(&login_list); - -- for (i = 0; i < rec->session.initial_login_retry_max; i++) { -- rc = session_login(rec); -- if (!rc) -- break; -- /* we raced with another app or instance of iscsiadm */ -- if (rc == MGMT_IPC_ERR_EXISTS) { -- rc = 0; -- break; -- } -- -- if (i + 1 != rec->session.initial_login_retry_max) -- sleep(1); -+ list_for_each_entry(curr_rec, rec_list, list) { -+ err = login_fn(data, &login_list, curr_rec); -+ if (err > 0 && !ret) -+ ret = err; -+ if (!err) -+ (*nr_found)++; - } - -- if (rc) { -- iscsid_handle_error(rc); -- return ENOTCONN; -- } else -- return 0; -+ err = iscsid_login_reqs_wait(&login_list); -+ if (err && !ret) -+ ret = err; -+ -+ list_for_each_entry_safe(curr_rec, tmp, rec_list, list) { -+ list_del(&curr_rec->list); -+ free(curr_rec); -+ } -+ return ret; - } - -+/* -+ * TODO: merged this and logout into the common for_each_rec by making -+ * the matching more generic -+ */ - static int --__login_by_startup(idbm_t *db, void *data, node_rec_t *rec) -+__login_by_startup(void *data, struct list_head *list, struct node_rec *rec) - { - char *mode = data; -- int rc = -1; -- - /* - * we always skip onboot because this should be handled by - * something else -@@ -500,15 +697,18 @@ __login_by_startup(idbm_t *db, void *data, node_rec_t *rec) - if (rec->startup == ISCSI_STARTUP_ONBOOT) - return -1; - -- if (!match_startup_mode(rec, mode)) -- rc = login_portal(NULL, NULL, rec); -- return rc; -+ if (match_startup_mode(rec, mode)) -+ return -1; -+ -+ login_portal(NULL, list, rec); -+ return 0; - } - - static int --login_by_startup(idbm_t *db, char *mode) -+login_by_startup(char *mode) - { -- int nr_found = 0, rc; -+ int nr_found = 0, rc, err; -+ struct list_head rec_list; - - if (!mode || !(!strcmp(mode, "automatic") || !strcmp(mode, "all") || - !strcmp(mode,"manual"))) { -@@ -517,7 +717,13 @@ login_by_startup(idbm_t *db, char *mode) - return EINVAL; - } - -- rc = idbm_for_each_rec(db, &nr_found, mode, __login_by_startup); -+ INIT_LIST_HEAD(&rec_list); -+ rc = idbm_for_each_rec(&nr_found, &rec_list, link_recs); -+ err = __login_portals(mode, &nr_found, &rec_list, -+ __login_by_startup); -+ if (err && !rc) -+ rc = err; -+ - if (rc) - log_error("Could not log into all portals. Err %d.", rc); - else if (!nr_found) { -@@ -527,7 +733,7 @@ login_by_startup(idbm_t *db, char *mode) - return rc; - } - --static int iface_fn(idbm_t *db, void *data, node_rec_t *rec) -+static int iface_fn(void *data, node_rec_t *rec) - { - struct rec_op_data *op_data = data; - -@@ -535,10 +741,10 @@ static int iface_fn(idbm_t *db, void *data, node_rec_t *rec) - rec->conn[0].address, rec->conn[0].port, - &rec->iface)) - return -1; -- return op_data->fn(db, op_data->data, rec); -+ return op_data->fn(op_data->data, rec); - } - --static int __for_each_rec(idbm_t *db, int verbose, struct node_rec *rec, -+static int __for_each_rec(int verbose, struct node_rec *rec, - void *data, idbm_iface_op_fn *fn) - { - struct rec_op_data op_data; -@@ -549,7 +755,7 @@ static int __for_each_rec(idbm_t *db, int verbose, struct node_rec *rec, - op_data.match_rec = rec; - op_data.fn = fn; - -- rc = idbm_for_each_rec(db, &nr_found, &op_data, iface_fn); -+ rc = idbm_for_each_rec(&nr_found, &op_data, iface_fn); - if (rc) { - if (verbose) - log_error("Could not execute operation on all " -@@ -563,13 +769,28 @@ static int __for_each_rec(idbm_t *db, int verbose, struct node_rec *rec, - return rc; - } - --static int for_each_rec(idbm_t *db, struct node_rec *rec, void *data, -+static int for_each_rec(struct node_rec *rec, void *data, - idbm_iface_op_fn *fn) - { -- return __for_each_rec(db, 1, rec, data, fn); -+ return __for_each_rec(1, rec, data, fn); -+} -+ -+ -+static int login_portals(struct node_rec *pattern_rec) -+{ -+ struct list_head rec_list; -+ int err, ret, nr_found; -+ -+ INIT_LIST_HEAD(&rec_list); -+ ret = for_each_rec(pattern_rec, &rec_list, link_recs); -+ err = __login_portals(NULL, &nr_found, &rec_list, -+ login_portal); -+ if (err && !ret) -+ ret = err; -+ return ret; - } - --static int print_nodes(idbm_t *db, int info_level, struct node_rec *rec) -+static int print_nodes(int info_level, struct node_rec *rec) - { - struct node_rec tmp_rec; - int rc = 0; -@@ -577,12 +798,12 @@ static int print_nodes(idbm_t *db, int info_level, struct node_rec *rec) - switch (info_level) { - case 0: - case -1: -- if (for_each_rec(db, rec, NULL, idbm_print_node_flat)) -+ if (for_each_rec(rec, NULL, idbm_print_node_flat)) - rc = -1; - break; - case 1: - memset(&tmp_rec, 0, sizeof(node_rec_t)); -- if (for_each_rec(db, rec, &tmp_rec, idbm_print_node_tree)) -+ if (for_each_rec(rec, &tmp_rec, idbm_print_node_tree)) - rc = -1; - break; - default: -@@ -593,53 +814,35 @@ static int print_nodes(idbm_t *db, int info_level, struct node_rec *rec) - return rc; - } - --static int --config_init(void) -+static char *get_config_file(void) - { - int rc; - iscsiadm_req_t req; - iscsiadm_rsp_t rsp; - - memset(&req, 0, sizeof(req)); -- req.command = MGMT_IPC_CONFIG_INAME; -- -- rc = do_iscsid(&req, &rsp); -- if (rc) -- return EIO; -- -- if (rsp.u.config.var[0] != '\0') { -- strcpy(initiator_name, rsp.u.config.var); -- } -- -- memset(&req, 0, sizeof(req)); -- req.command = MGMT_IPC_CONFIG_IALIAS; -- -- rc = do_iscsid(&req, &rsp); -- if (rc) -- return EIO; -- -- if (rsp.u.config.var[0] != '\0') { -- strcpy(initiator_alias, rsp.u.config.var); -- } -- -- memset(&req, 0, sizeof(req)); - req.command = MGMT_IPC_CONFIG_FILE; - - rc = do_iscsid(&req, &rsp); - if (rc) -- return EIO; -+ return NULL; - - if (rsp.u.config.var[0] != '\0') { - strcpy(config_file, rsp.u.config.var); -+ return config_file; - } - -- return 0; -+ return NULL; - } - - static int print_session_flat(void *data, struct session_info *info) - { -+ struct session_info *match_info = data; - struct iscsi_transport *t = get_transport_by_sid(info->sid); - -+ if (match_info && match_info->sid != info->sid) -+ return 0; -+ - if (strchr(info->persistent_address, '.')) - printf("%s: [%d] %s:%d,%d %s\n", - t ? t->name : UNKNOWN_VALUE, -@@ -653,45 +856,13 @@ static int print_session_flat(void *data, struct session_info *info) - return 0; - } - --static int link_sessions(void *data, struct session_info *info) --{ -- struct list_head *list = data; -- struct session_info *new, *curr, *match = NULL; -- -- new = calloc(1, sizeof(*new)); -- if (!new) -- return ENOMEM; -- memcpy(new, info, sizeof(*new)); -- -- if (list_empty(list)) { -- list_add_tail(&new->list, list); -- return 0; -- } -- -- list_for_each_entry(curr, list, list) { -- if (!strcmp(curr->targetname, info->targetname)) { -- match = curr; -- -- if (!strcmp(curr->address, info->address)) { -- match = curr; -- -- if (curr->port == info->port) { -- match = curr; -- break; -- } -- } -- } -- } -- -- list_add_tail(&new->list, match ? match->list.next : list); -- return 0; --} -- - static int print_iscsi_state(int sid) - { - iscsiadm_req_t req; - iscsiadm_rsp_t rsp; -+ int err; - char *state = NULL; -+ char state_buff[SCSI_MAX_STATE_VALUE]; - static char *conn_state[] = { - "FREE", - "TRANSPORT WAIT", -@@ -712,23 +883,24 @@ static int print_iscsi_state(int sid) - req.command = MGMT_IPC_SESSION_INFO; - req.u.session.sid = sid; - -- if (do_iscsid(&req, &rsp)) { -- printf("\t\tiSCSI Connection State: Unknown\n"); -- printf("\t\tInternal iscsid Session State: Unknown\n"); -- return ENODEV; -- } -- -+ err = do_iscsid(&req, &rsp); - /* - * for drivers like qla4xxx, iscsid does not display - * anything here since it does not know about it. - */ -- if (rsp.u.session_state.conn_state >= 0 && -+ if (!err && rsp.u.session_state.conn_state >= 0 && - rsp.u.session_state.conn_state <= STATE_CLEANUP_WAIT) - state = conn_state[rsp.u.session_state.conn_state]; - printf("\t\tiSCSI Connection State: %s\n", state ? state : "Unknown"); - state = NULL; - -- if (rsp.u.session_state.session_state >= 0 && -+ memset(state_buff, 0, SCSI_MAX_STATE_VALUE); -+ if (!get_session_state(state_buff, sid)) -+ printf("\t\tiSCSI Session State: %s\n", state_buff); -+ else -+ printf("\t\tiSCSI Session State: Unknown\n"); -+ -+ if (!err && rsp.u.session_state.session_state >= 0 && - rsp.u.session_state.session_state <= R_STAGE_SESSION_REDIRECT) - state = session_state[rsp.u.session_state.session_state]; - printf("\t\tInternal iscsid Session State: %s\n", -@@ -818,12 +990,10 @@ static int print_scsi_state(int sid) - return 0; - } - --static void print_sessions_tree(idbm_t *db, struct list_head *list, int level) -+static void print_sessions_tree(struct list_head *list, int level) - { - struct session_info *curr, *prev = NULL, *tmp; - struct iscsi_transport *t; -- struct iface_rec iface; -- int rc; - - list_for_each_entry(curr, list, list) { - if (!prev || strcmp(prev->targetname, curr->targetname)) { -@@ -856,16 +1026,21 @@ static void print_sessions_tree(idbm_t *db, struct list_head *list, int level) - printf("\t\t**********\n"); - printf("\t\tInterface:\n"); - printf("\t\t**********\n"); -- if (iface_is_bound(&curr->iface)) { -- if (iface_get_by_bind_info(db, &curr->iface, &iface)) -- printf("\t\tIface Name: %s\n", UNKNOWN_VALUE); -- else -- printf("\t\tIface Name: %s\n", iface.name); -- } else -- printf("\t\tIface Name: %s\n", DEFAULT_IFACENAME); -+ if (strlen(curr->iface.name)) -+ printf("\t\tIface Name: %s\n", curr->iface.name); -+ else -+ printf("\t\tIface Name: %s\n", UNKNOWN_VALUE); - printf("\t\tIface Transport: %s\n", - t ? t->name : UNKNOWN_VALUE); -- printf("\t\tIface IPaddress: %s\n", curr->iface.ipaddress); -+ printf("\t\tIface Initiatorname: %s\n", -+ strlen(curr->iface.iname) ? curr->iface.iname : -+ UNKNOWN_VALUE); -+ if (strchr(curr->address, '.')) -+ printf("\t\tIface IPaddress: %s\n", -+ curr->iface.ipaddress); -+ else -+ printf("\t\tIface IPaddress: [%s]\n", -+ curr->iface.ipaddress); - printf("\t\tIface HWaddress: %s\n", curr->iface.hwaddress); - printf("\t\tIface Netdev: %s\n", curr->iface.netdev); - printf("\t\tSID: %d\n", curr->sid); -@@ -888,37 +1063,7 @@ next: - } - } - --static int print_session(idbm_t *db, int info_level, struct session_info *info) --{ -- struct list_head list; -- int err; -- -- switch (info_level) { -- case 0: -- case -1: -- err = print_session_flat(NULL, info); -- break; -- case 1: -- case 2: -- case 3: -- INIT_LIST_HEAD(&list); -- -- err = link_sessions(&list, info); -- if (err) -- break; -- print_sessions_tree(db, &list, info_level); -- break; -- default: -- log_error("Invalid info level %d. Try 0 - 3.", info_level); -- return EINVAL; -- } -- -- if (err) -- log_error("Can not get session info (%d)", err); -- return 0; --} -- --static int print_sessions(idbm_t *db, int info_level) -+static int print_sessions(int info_level, struct session_info *match_info) - { - struct list_head list; - int num_found = 0, err = 0; -@@ -927,7 +1072,7 @@ static int print_sessions(idbm_t *db, int info_level) - switch (info_level) { - case 0: - case -1: -- err = sysfs_for_each_session(NULL, &num_found, -+ err = sysfs_for_each_session(match_info, &num_found, - print_session_flat); - break; - case 2: -@@ -941,13 +1086,18 @@ static int print_sessions(idbm_t *db, int info_level) - /* fall through */ - case 1: - INIT_LIST_HEAD(&list); -- -- err = sysfs_for_each_session(&list, &num_found, -+ struct session_link_info link_info; -+ -+ memset(&link_info, 0, sizeof(link_info)); -+ link_info.list = &list; -+ link_info.match_info = match_info; -+ -+ err = sysfs_for_each_session(&link_info, &num_found, - link_sessions); - if (err || !num_found) - break; - -- print_sessions_tree(db, &list, info_level); -+ print_sessions_tree(&list, info_level); - break; - default: - log_error("Invalid info level %d. Try 0 - 3.", info_level); -@@ -969,17 +1119,18 @@ static int rescan_portal(void *data, struct session_info *info) - if (!iscsi_match_session(data, info)) - return -1; - -- printf("Rescanning session [sid: %d, iface: %s, target: %s, portal: " -- "%s,%d]\n", info->sid, info->iface.name, -- info->targetname, info->persistent_address, -- info->port); -+ printf("Rescanning session [sid: %d, target: %s, portal: " -+ "%s,%d]\n", info->sid, info->targetname, -+ info->persistent_address, info->port); - - host_no = get_host_no_from_sid(info->sid, &err); - if (err) { - log_error("Could not rescan session sid %d.", info->sid); - return err; - } -- -+ /* rescan each device to pick up size changes */ -+ sysfs_for_each_device(host_no, info->sid, rescan_device); -+ /* now scan for new devices */ - scan_host(host_no, 0); - return 0; - } -@@ -1002,10 +1153,10 @@ session_stats(void *data, struct session_info *info) - if (rc) - return EIO; - -- printf("Stats for session [sid: %d, iface: %s, target: %s, portal: " -+ printf("Stats for session [sid: %d, target: %s, portal: " - "%s,%d]\n", -- info->sid, info->iface.name, info->targetname, -- info->persistent_address, info->port); -+ info->sid, info->targetname, info->persistent_address, -+ info->port); - - printf( "iSCSI SNMP:\n" - -@@ -1069,7 +1220,7 @@ session_stats(void *data, struct session_info *info) - return 0; - } - --static int add_static_rec(idbm_t *db, int *found, char *targetname, int tpgt, -+static int add_static_rec(int *found, char *targetname, int tpgt, - char *ip, int port, struct iface_rec *iface) - { - node_rec_t *rec; -@@ -1091,7 +1242,7 @@ static int add_static_rec(idbm_t *db, int *found, char *targetname, int tpgt, - } - drec->type = DISCOVERY_TYPE_STATIC; - -- idbm_node_setup_from_conf(db, rec); -+ idbm_node_setup_from_conf(rec); - strncpy(rec->name, targetname, TARGET_NAME_MAXLEN); - rec->tpgt = tpgt; - rec->conn[0].port = port; -@@ -1108,7 +1259,7 @@ static int add_static_rec(idbm_t *db, int *found, char *targetname, int tpgt, - iface_copy(&rec->iface, iface); - } - -- rc = idbm_add_node(db, rec, drec); -+ rc = idbm_add_node(rec, drec, 1); - if (!rc) { - (*found)++; - printf("New iSCSI node [%s:" iface_fmt " %s,%d,%d %s] added\n", -@@ -1122,7 +1273,7 @@ done: - return rc; - } - --static int add_static_portal(idbm_t *db, int *found, void *data, -+static int add_static_portal(int *found, void *data, - char *targetname, int tpgt, char *ip, int port) - { - node_rec_t *rec = data; -@@ -1134,11 +1285,11 @@ static int add_static_portal(idbm_t *db, int *found, void *data, - if (rec->conn[0].port != -1 && rec->conn[0].port != port) - return 0; - -- return add_static_rec(db, found, targetname, tpgt, ip, port, -+ return add_static_rec(found, targetname, tpgt, ip, port, - &rec->iface); - } - --static int add_static_node(idbm_t *db, int *found, void *data, -+static int add_static_node(int *found, void *data, - char *targetname) - { - node_rec_t *rec = data; -@@ -1152,19 +1303,19 @@ static int add_static_node(idbm_t *db, int *found, void *data, - if (!strlen(rec->conn[0].address)) - goto search; - -- return add_static_rec(db, found, targetname, rec->tpgt, -+ return add_static_rec(found, targetname, rec->tpgt, - rec->conn[0].address, - rec->conn[0].port, &rec->iface); - search: -- return idbm_for_each_portal(db, found, data, add_static_portal, -+ return idbm_for_each_portal(found, data, add_static_portal, - targetname); - } - --static int add_static_recs(idbm_t *db, struct node_rec *rec) -+static int add_static_recs(struct node_rec *rec) - { - int rc, nr_found = 0; - -- rc = idbm_for_each_node(db, &nr_found, rec, add_static_node); -+ rc = idbm_for_each_node(&nr_found, rec, add_static_node); - if (rc) { - log_error("Error while adding records. DB may be in an " - "inconsistent state. Err %d", rc); -@@ -1176,7 +1327,7 @@ static int add_static_recs(idbm_t *db, struct node_rec *rec) - - /* brand new target */ - if (strlen(rec->name) && strlen(rec->conn[0].address)) { -- rc = add_static_rec(db, &nr_found, rec->name, rec->tpgt, -+ rc = add_static_rec(&nr_found, rec->name, rec->tpgt, - rec->conn[0].address, rec->conn[0].port, - &rec->iface); - if (rc) -@@ -1193,29 +1344,193 @@ done: - * particular config - */ - static int --do_offload_sendtargets(idbm_t *db, discovery_rec_t *drec, -- int host_no, int do_login) -+do_offload_sendtargets(discovery_rec_t *drec, int host_no, int do_login) - { - drec->type = DISCOVERY_TYPE_OFFLOAD_SENDTARGETS; -- return discovery_offload_sendtargets(db, host_no, do_login, drec); -+ return discovery_offload_sendtargets(host_no, do_login, drec); -+} -+ -+static int login_discovered_portal(void *data, struct list_head *list, -+ node_rec_t *rec) -+{ -+ discovery_rec_t *drec = data; -+ -+ if (rec->disc_type != drec->type || -+ rec->disc_port != drec->port || -+ strcmp(rec->disc_address, drec->address)) -+ return -1; -+ -+ login_portal(NULL, list, rec); -+ /* -+ * This is used during the initial setup, so we want to see -+ * what portals we can or cannot log into and we will just continue -+ */ -+ return 0; -+} -+ -+/* TODO merge with initiator.c implementation */ -+/* And add locking */ -+static int check_for_session_through_iface(struct node_rec *rec) -+{ -+ int nr_found = 0; -+ if (sysfs_for_each_session(rec, &nr_found, iscsi_match_session)) -+ return 1; -+ return 0; -+} -+ -+static int delete_node(void *data, struct node_rec *rec) -+{ -+ if (check_for_session_through_iface(rec)) { -+ /* -+ * We could log out the session for the user, but if -+ * the session is being used the user may get something -+ * they were not expecting (FS errors and a read only -+ * remount). -+ */ -+ log_error("This command will remove the record [iface: %s, " -+ "target: %s, portal: %s,%d], but a session is " -+ "using it. Logout session then rerun command to " -+ "remove record.", rec->iface.name, rec->name, -+ rec->conn[0].address, rec->conn[0].port); -+ return EINVAL; -+ } -+ -+ return idbm_delete_node(rec); -+} -+ -+static int delete_stale_recs(void *data, struct node_rec *rec) -+{ -+ struct list_head *new_rec_list = data; -+ struct node_rec *new_rec; -+ -+ list_for_each_entry(new_rec, new_rec_list, list) { -+ /* -+ * We could also move this to idbm.c and instead of looping -+ * over every node just loop over disc to node links. -+ */ -+ if (rec->disc_type != new_rec->disc_type || -+ rec->disc_port != new_rec->disc_port || -+ strcmp(rec->disc_address, new_rec->disc_address)) -+ /* -+ * if we are not from the same discovery source -+ * ignore it -+ */ -+ return 0; -+ -+ if (__iscsi_match_session(rec, -+ new_rec->name, -+ new_rec->conn[0].address, -+ new_rec->conn[0].port, -+ &new_rec->iface)) -+ return 0; -+ } -+ /* if there is a error we can continue on */ -+ delete_node(NULL, rec); -+ return 0; - } - - static int --do_sofware_sendtargets(idbm_t *db, discovery_rec_t *drec, -- struct list_head *ifaces, int info_level, int do_login) -+update_discovery_recs(discovery_rec_t *drec, -+ struct list_head *new_rec_list, struct list_head *ifaces, -+ int info_level, int do_login, int op) - { -+ int rc, err, found = 0; -+ struct list_head bound_rec_list; -+ struct node_rec *new_rec, *tmp; -+ -+ INIT_LIST_HEAD(&bound_rec_list); -+ -+ /* bind ifaces to node recs so we know what we have */ -+ list_for_each_entry(new_rec, new_rec_list, list) { -+ rc = idbm_bind_ifaces_to_node(new_rec, ifaces, -+ &bound_rec_list); -+ if (rc) -+ goto free_bound_recs; -+ } -+ -+ -+ /* clean up node db */ -+ if (op & OP_DELETE) -+ idbm_for_each_rec(&found, &bound_rec_list, -+ delete_stale_recs); -+ -+ if (op & OP_NEW || op & OP_UPDATE) { -+ /* now add/update records */ -+ list_for_each_entry(new_rec, &bound_rec_list, list) { -+ rc = idbm_add_node(new_rec, drec, op & OP_UPDATE); -+ if (rc) -+ log_error("Could not add/update " -+ "[%s:" iface_fmt " %s,%d,%d %s]", -+ new_rec->iface.transport_name, -+ iface_str(&new_rec->iface), -+ new_rec->conn[0].address, -+ new_rec->conn[0].port, -+ new_rec->tpgt, new_rec->name); -+ } -+ } -+ -+ idbm_print_discovered(drec, info_level); -+ -+ if (!do_login) { -+ rc = 0; -+ goto free_bound_recs; -+ } -+ -+ err = __login_portals(drec, &found, &bound_rec_list, -+ login_discovered_portal); -+ if (err && !rc) -+ rc = err; -+ -+free_bound_recs: -+ list_for_each_entry_safe(new_rec, tmp, &bound_rec_list, list) { -+ list_del(&new_rec->list); -+ free(new_rec); -+ } -+ return rc; -+} -+ -+static int -+do_sofware_sendtargets(discovery_rec_t *drec, struct list_head *ifaces, -+ int info_level, int do_login, -+ int op) -+{ -+ struct list_head new_rec_list; -+ struct node_rec *new_rec, *tmp; - int rc; - -+ INIT_LIST_HEAD(&new_rec_list); -+ /* -+ * compat: if the user did not pass any op then we do all -+ * ops for them -+ */ -+ if (!op) -+ op = OP_NEW | OP_DELETE | OP_UPDATE; -+ - drec->type = DISCOVERY_TYPE_SENDTARGETS; -- rc = discovery_sendtargets(db, drec, ifaces); -- if (!rc) -- idbm_print_discovered(db, drec, info_level); -+ rc = discovery_sendtargets(drec, &new_rec_list); -+ if (rc) -+ return rc; -+ -+ rc = idbm_add_discovery(drec, op & OP_UPDATE); -+ if (rc) { -+ log_error("Could not add new discovery record."); -+ goto free_new_recs; -+ } -+ -+ rc = update_discovery_recs(drec, &new_rec_list, ifaces, -+ info_level, do_login, op); -+ -+free_new_recs: -+ list_for_each_entry_safe(new_rec, tmp, &new_rec_list, list) { -+ list_del(&new_rec->list); -+ free(new_rec); -+ } - return rc; - } - - static int --do_sendtargets(idbm_t *db, discovery_rec_t *drec, struct list_head *ifaces, -- int info_level, int do_login) -+do_sendtargets(discovery_rec_t *drec, struct list_head *ifaces, -+ int info_level, int do_login, int op) - { - struct iface_rec *tmp, *iface; - int rc, host_no; -@@ -1239,10 +1554,6 @@ do_sendtargets(idbm_t *db, discovery_rec_t *drec, struct list_head *ifaces, - continue; - } - -- /* if no binding it must be software */ -- if (!iface_is_bound(iface)) -- continue; -- - host_no = get_host_no_from_iface(iface, &rc); - if (rc || host_no == -1) { - log_debug(1, "Could not match iface" iface_fmt " to " -@@ -1264,8 +1575,7 @@ do_sendtargets(idbm_t *db, discovery_rec_t *drec, struct list_head *ifaces, - } - - if (t->caps & CAP_SENDTARGETS_OFFLOAD) { -- do_offload_sendtargets(db, drec, host_no, -- do_login); -+ do_offload_sendtargets(drec, host_no, do_login); - list_del(&iface->list); - free(iface); - } -@@ -1275,10 +1585,27 @@ do_sendtargets(idbm_t *db, discovery_rec_t *drec, struct list_head *ifaces, - return ENODEV; - - sw_st: -- return do_sofware_sendtargets(db, drec, ifaces, info_level, do_login); -+ return do_sofware_sendtargets(drec, ifaces, info_level, do_login, -+ op); - } - --static int isns_dev_attr_query(idbm_t *db, discovery_rec_t *drec, -+/* TODO: merge this with the idbm code */ -+static void print_fw_nodes(struct node_rec *rec, int info_level) -+{ -+ switch (info_level) { -+ case -1: -+ case 0: -+ idbm_print_node_flat(NULL, rec); -+ break; -+ case 1: -+ idbm_print_node_tree(NULL, rec); -+ break; -+ default: -+ log_error("Invalid print level %d. Try 0 or 1.", info_level); -+ } -+} -+ -+static int isns_dev_attr_query(discovery_rec_t *drec, - int info_level) - { - iscsiadm_req_t req; -@@ -1293,7 +1620,7 @@ static int isns_dev_attr_query(idbm_t *db, discovery_rec_t *drec, - iscsid_handle_error(err); - return EIO; - } else { -- idbm_print_discovered(db, drec, info_level); -+ idbm_print_discovered(drec, info_level); - return 0; - } - } -@@ -1324,36 +1651,8 @@ static void catch_sigint( int signo ) { - exit(1); - } - --static int check_for_session_through_iface(struct node_rec *rec) --{ -- int nr_found = 0; -- if (sysfs_for_each_session(rec, &nr_found, iscsi_match_session)) -- return 1; -- return 0; --} -- --static struct node_rec *setup_rec_from_iface(struct iface_rec *iface) --{ -- struct node_rec *rec; -- -- rec = calloc(1, sizeof(*rec)); -- if (!rec) { -- log_error("Could not not allocate memory to create node " -- "record."); -- return NULL; -- } -- -- rec->tpgt = -1; -- rec->conn[0].port = -1; -- iface_copy(&rec->iface, iface); -- if (iface_conf_read(&rec->iface)) { -- free(rec); -- rec = NULL; -- } -- return rec; --} -- --static int exec_iface_op(idbm_t *db, int op, int do_show, int info_level, -+/* TODO: merge iter helpers and clean them up, so we can use them here */ -+static int exec_iface_op(int op, int do_show, int info_level, - struct iface_rec *iface, char *name, char *value) - { - struct db_set_param set_param; -@@ -1368,16 +1667,13 @@ static int exec_iface_op(idbm_t *db, int op, int do_show, int info_level, - return EINVAL; - } - -- rec = setup_rec_from_iface(iface); -- if (rec) { -- if (check_for_session_through_iface(rec)) { -- rc = EBUSY; -- goto new_fail; -- } -- log_warning("Overwriting existing %s.", iface->name); -+ rec = create_node_record(NULL, -1, NULL, -1, iface, 0); -+ if (rec && check_for_session_through_iface(rec)) { -+ rc = EBUSY; -+ goto new_fail; - } - -- iface_init(iface); -+ iface_setup_defaults(iface); - rc = iface_conf_write(iface); - if (rc) - goto new_fail; -@@ -1393,19 +1689,14 @@ new_fail: - return EINVAL; - } - -- rec = setup_rec_from_iface(iface); -+ rec = create_node_record(NULL, -1, NULL, -1, iface, 1); - if (!rec) { - rc = EINVAL; - goto delete_fail; - } - -- if (check_for_session_through_iface(rec)) { -- rc = EBUSY; -- goto delete_fail; -- } -- -- /* delete node records using it first */ -- rc = __for_each_rec(db, 0, rec, NULL, idbm_delete_node); -+ /* logout and delete records using it first */ -+ rc = __for_each_rec(0, rec, NULL, delete_node); - if (rc && rc != ENODEV) - goto delete_fail; - -@@ -1427,7 +1718,7 @@ delete_fail: - break; - } - -- rec = setup_rec_from_iface(iface); -+ rec = create_node_record(NULL, -1, NULL, -1, iface, 1); - if (!rec) { - rc = EINVAL; - goto update_fail; -@@ -1439,46 +1730,44 @@ delete_fail: - } - - if (!strcmp(name, "iface.iscsi_ifacename")) { -- log_error("Can not update iface.iscsi_ifacename. " -- "Delete it, and then create a new one."); -+ log_error("Can not update " -+ "iface.iscsi_ifacename. Delete it, " -+ "and then create a new one."); - rc = EINVAL; - break; - } - - if (iface_is_bound_by_hwaddr(&rec->iface) && - !strcmp(name, "iface.net_ifacename")) { -- log_error("Can not update interface binding from " -- "hwaddress to net_ifacename. "); -- log_error("You must delete the interface and create " -- "a new one"); -+ log_error("Can not update interface binding " -+ "from hwaddress to net_ifacename. "); -+ log_error("You must delete the interface and " -+ "create a new one"); - rc = EINVAL; - break; - } - - if (iface_is_bound_by_netdev(&rec->iface) && - !strcmp(name, "iface.hwaddress")) { -- log_error("Can not update interface binding from " -- "net_ifacename to hwaddress. "); -- log_error("You must delete the interface and create " -- "a new one"); -+ log_error("Can not update interface binding " -+ "from net_ifacename to hwaddress. "); -+ log_error("You must delete the interface and " -+ "create a new one"); - rc = EINVAL; - break; - } -- -- set_param.db = db; - set_param.name = name; - set_param.value = value; - -- rc = __for_each_rec(db, 0, rec, &set_param, -- idbm_node_set_param); -- if (rc && rc != ENODEV) -- goto update_fail; -- - /* pass rec's iface because it has the db values */ - rc = iface_conf_update(&set_param, &rec->iface); - if (rc) - goto update_fail; - -+ rc = __for_each_rec(0, rec, &set_param, idbm_node_set_param); -+ if (rc && rc != ENODEV) -+ goto update_fail; -+ - printf("%s updated.\n", iface->name); - break; - update_fail: -@@ -1487,10 +1776,19 @@ update_fail: - iface->name); - break; - default: -- if (op < 0 || op == OP_SHOW) -- rc = print_ifaces(db, info_level); -- else -- rc = EINVAL; -+ if (!iface) { -+ if (op == OP_NOOP || op == OP_SHOW) -+ rc = print_ifaces(info_level); -+ else -+ rc = EINVAL; -+ } else { -+ rc = iface_conf_read(iface); -+ if (!rc) -+ idbm_print_iface_info(&do_show, iface); -+ else -+ log_error("Could not read iface %s (%d).", -+ iface->name, rc); -+ } - } - - if (rec) -@@ -1499,7 +1797,7 @@ update_fail: - } - - /* TODO cleanup arguments */ --static int exec_node_op(idbm_t *db, int op, int do_login, int do_logout, -+static int exec_node_op(int op, int do_login, int do_logout, - int do_show, int do_rescan, int do_stats, - int info_level, struct node_rec *rec, - char *name, char *value) -@@ -1513,7 +1811,7 @@ static int exec_node_op(idbm_t *db, int op, int do_login, int do_logout, - rec->name, rec->conn[0].address, rec->conn[0].port); - - if (op == OP_NEW) { -- if (add_static_recs(db, rec)) -+ if (add_static_recs(rec)) - rc = -1; - goto out; - } -@@ -1536,34 +1834,34 @@ static int exec_node_op(idbm_t *db, int op, int do_login, int do_logout, - goto out; - } - -- if ((do_login || do_logout) && op >= 0) { -+ if ((do_login || do_logout) && op > OP_NOOP) { - log_error("either operation or login/logout " - "at the time allowed!"); - rc = -1; - goto out; - } - -- if ((!do_login && !do_logout && op < 0) && -+ if ((!do_login && !do_logout && op == OP_NOOP) && - (!strlen(rec->name) && !strlen(rec->conn[0].address) && - !strlen(rec->iface.name))) { -- rc = print_nodes(db, info_level, rec); -+ rc = print_nodes(info_level, rec); - goto out; - } - - if (do_login) { -- if (for_each_rec(db, rec, NULL, login_portal)) -+ if (login_portals(rec)) - rc = -1; - goto out; - } - - if (do_logout) { -- if (for_each_session(rec, logout_portal)) -+ if (logout_portals(rec)) - rc = -1; - goto out; - } - -- if (op < 0 || (!do_login && !do_logout && op == OP_SHOW)) { -- if (for_each_rec(db, rec, &do_show, idbm_print_node_info)) -+ if (op == OP_NOOP || (!do_login && !do_logout && op == OP_SHOW)) { -+ if (for_each_rec(rec, &do_show, idbm_print_node_info)) - rc = -1; - goto out; - } -@@ -1575,15 +1873,21 @@ static int exec_node_op(idbm_t *db, int op, int do_login, int do_logout, - goto out; - } - -- set_param.db = db; -+ if (!strncmp(name, "iface.", 6)) { -+ log_error("Cannot modify %s. Use iface mode to update " -+ "this value.", name); -+ rc = -1; -+ goto out; -+ } -+ - set_param.name = name; - set_param.value = value; - -- if (for_each_rec(db, rec, &set_param, idbm_node_set_param)) -+ if (for_each_rec(rec, &set_param, idbm_node_set_param)) - rc = -1; - goto out; - } else if (op == OP_DELETE) { -- if (for_each_rec(db, rec, NULL, idbm_delete_node)) -+ if (for_each_rec(rec, NULL, delete_node)) - rc = -1; - goto out; - } else { -@@ -1595,6 +1899,71 @@ out: - return rc; - } - -+static struct node_rec * -+fw_create_rec_by_entry(struct boot_context *context) -+{ -+ struct node_rec *rec; -+ -+ /* tpgt hard coded to 1 ??? */ -+ rec = create_node_record(context->targetname, 1, -+ context->target_ipaddr, context->target_port, -+ NULL, 1); -+ if (!rec) { -+ log_error("Could not setup rec for fw discovery login."); -+ return NULL; -+ } -+ -+ /* todo - grab mac and set that here */ -+ iface_setup_defaults(&rec->iface); -+ strncpy(rec->iface.iname, context->initiatorname, -+ sizeof(context->initiatorname)); -+ strncpy(rec->session.auth.username, context->chap_name, -+ sizeof(context->chap_name)); -+ strncpy((char *)rec->session.auth.password, context->chap_password, -+ sizeof(context->chap_password)); -+ strncpy(rec->session.auth.username_in, context->chap_name_in, -+ sizeof(context->chap_name_in)); -+ strncpy((char *)rec->session.auth.password_in, -+ context->chap_password_in, -+ sizeof(context->chap_password_in)); -+ rec->session.auth.password_length = -+ strlen((char *)context->chap_password); -+ rec->session.auth.password_in_length = -+ strlen((char *)context->chap_password_in); -+ return rec; -+} -+ -+static int exec_fw_op(discovery_rec_t *drec, int do_login, int info_level) -+{ -+ struct boot_context context; -+ struct node_rec *rec; -+ int ret = 0; -+ -+ memset(&context, 0, sizeof(struct boot_context)); -+ ret = fw_get_entry(&context, NULL); -+ if (ret) { -+ log_error("Could not read fw values."); -+ return ret; -+ } -+ -+ rec = fw_create_rec_by_entry(&context); -+ if (!rec) -+ return ENODEV; -+ -+ /* if discovery, print nodes that were found. */ -+ if (drec) -+ print_fw_nodes(rec, info_level); -+ -+ if (do_login) -+ ret = login_portal(NULL, NULL, rec); -+ free(rec); -+ -+ /* print the fw node info if called in fw mode with no params */ -+ if (!do_login && !drec) -+ fw_print_entry(&context); -+ return ret; -+} -+ - static int parse_sid(char *session) - { - struct stat statb; -@@ -1651,10 +2020,9 @@ main(int argc, char **argv) - char *ip = NULL, *name = NULL, *value = NULL; - char *targetname = NULL, *group_session_mgmt_mode = NULL; - int ch, longindex, mode=-1, port=-1, do_login=0, do_rescan=0; -- int rc=0, sid=-1, op=-1, type=-1, do_logout=0, do_stats=0, do_show=0; -+ int rc=0, sid=-1, op=OP_NOOP, type=-1, do_logout=0, do_stats=0; - int do_login_all=0, do_logout_all=0, info_level=-1, num_ifaces = 0; -- int tpgt = PORTAL_GROUP_TAG_UNKNOWN; -- idbm_t *db; -+ int tpgt = PORTAL_GROUP_TAG_UNKNOWN, killiscsid=-1, do_show=0; - struct sigaction sa_old; - struct sigaction sa_new; - discovery_rec_t drec; -@@ -1664,6 +2032,9 @@ main(int argc, char **argv) - - INIT_LIST_HEAD(&ifaces); - /* do not allow ctrl-c for now... */ -+ memset(&sa_old, 0, sizeof(struct sigaction)); -+ memset(&sa_new, 0, sizeof(struct sigaction)); -+ - sa_new.sa_handler = catch_sigint; - sigemptyset(&sa_new.sa_mask); - sa_new.sa_flags = 0; -@@ -1679,12 +2050,21 @@ main(int argc, char **argv) - while ((ch = getopt_long(argc, argv, short_options, - long_options, &longindex)) >= 0) { - switch (ch) { -+ case 'k': -+ killiscsid = atoi(optarg); -+ if (killiscsid < 0) { -+ log_error("Invalid killiscsid priority %d " -+ "Priority must be greater than or " -+ "equal to zero.", killiscsid); -+ exit(-1); -+ } -+ break; - case 't': - type = str_to_type(optarg); - break; - case 'o': -- op = str_to_op(optarg); -- if (op < 0) { -+ op |= str_to_op(optarg); -+ if (op == OP_NOOP) { - log_error("can not recognize operation: '%s'", - optarg); - return -1; -@@ -1773,22 +2153,33 @@ main(int argc, char **argv) - return -1; - } - -+ if (killiscsid >= 0) { -+ kill_iscsid(killiscsid); -+ goto free_ifaces; -+ } -+ - if (mode < 0) - usage(0); - -- config_init(); -- if (initiator_name[0] == '\0') { -- log_warning("exiting due to configuration error"); -- return -1; -+ if (mode == MODE_FW) { -+ if ((rc = verify_mode_params(argc, argv, "ml", 0))) { -+ log_error("fw mode: option '-%c' is not " -+ "allowed/supported", rc); -+ rc = -1; -+ goto out; -+ } -+ -+ rc = exec_fw_op(NULL, do_login, info_level); -+ goto out; - } - -- db = idbm_init(config_file); -- if (!db) { -+ increase_max_files(); -+ if (idbm_init(get_config_file)) { - log_warning("exiting due to idbm configuration error"); - return -1; - } - -- iface_setup_host_bindings(db); -+ iface_setup_host_bindings(); - - switch (mode) { - case MODE_IFACE: -@@ -1807,7 +2198,7 @@ main(int argc, char **argv) - "interface. Using the first one " - "%s.", iface->name); - } -- rc = exec_iface_op(db, op, do_show, info_level, iface, -+ rc = exec_iface_op(op, do_show, info_level, iface, - name, value); - break; - case MODE_DISCOVERY: -@@ -1827,12 +2218,12 @@ main(int argc, char **argv) - } - - memset(&drec, 0, sizeof(discovery_rec_t)); -- idbm_sendtargets_defaults(db, &drec.u.sendtargets); -+ idbm_sendtargets_defaults(&drec.u.sendtargets); - strncpy(drec.address, ip, sizeof(drec.address)); - drec.port = port; - -- if (do_sendtargets(db, &drec, &ifaces, info_level, -- do_login)) { -+ if (do_sendtargets(&drec, &ifaces, info_level, -+ do_login, op)) { - rc = -1; - goto out; - } -@@ -1845,12 +2236,17 @@ main(int argc, char **argv) - case DISCOVERY_TYPE_ISNS: - drec.type = DISCOVERY_TYPE_ISNS; - -- if (isns_dev_attr_query(db, &drec, info_level)) -+ if (isns_dev_attr_query(&drec, info_level)) -+ rc = -1; -+ break; -+ case DISCOVERY_TYPE_FWBOOT: -+ drec.type = DISCOVERY_TYPE_FWBOOT; -+ if (exec_fw_op(&drec, do_login, info_level)) - rc = -1; - break; - default: - if (ip) { -- if (idbm_discovery_read(db, &drec, ip, port)) { -+ if (idbm_discovery_read(&drec, ip, port)) { - log_error("discovery record [%s,%d] " - "not found!", ip, port); - rc = -1; -@@ -1858,8 +2254,9 @@ main(int argc, char **argv) - } - if (do_login && - drec.type == DISCOVERY_TYPE_SENDTARGETS) { -- do_sendtargets(db, &drec, &ifaces, -- info_level, do_login); -+ do_sendtargets(&drec, &ifaces, -+ info_level, do_login, -+ op); - } else if (do_login && - drec.type == DISCOVERY_TYPE_SLP) { - log_error("SLP discovery is not fully " -@@ -1872,14 +2269,14 @@ main(int argc, char **argv) - "implemented yet."); - rc = -1; - goto out; -- } else if (op < 0 || op == OP_SHOW) { -- if (!idbm_print_discovery_info(db, -- &drec, do_show)) { -+ } else if (op == OP_NOOP || op == OP_SHOW) { -+ if (!idbm_print_discovery_info(&drec, -+ do_show)) { - log_error("no records found!"); - rc = -1; - } - } else if (op == OP_DELETE) { -- if (idbm_delete_discovery(db, &drec)) { -+ if (idbm_delete_discovery(&drec)) { - log_error("unable to delete " - "record!"); - rc = -1; -@@ -1890,8 +2287,8 @@ main(int argc, char **argv) - goto out; - } - -- } else if (op < 0 || op == OP_SHOW) { -- if (!idbm_print_all_discovery(db, info_level)) -+ } else if (op == OP_NOOP || op == OP_SHOW) { -+ if (!idbm_print_all_discovery(info_level)) - rc = -1; - goto out; - } else if (op == OP_DELETE) { -@@ -1918,12 +2315,12 @@ main(int argc, char **argv) - } - - if (do_login_all) { -- rc = login_by_startup(db, group_session_mgmt_mode); -+ rc = login_by_startup(group_session_mgmt_mode); - goto out; - } - - if (do_logout_all) { -- rc = logout_by_startup(db, group_session_mgmt_mode); -+ rc = logout_by_startup(group_session_mgmt_mode); - goto out; - } - -@@ -1938,13 +2335,13 @@ main(int argc, char **argv) - iface->hwaddress, iface->ipaddress); - } - -- rec = create_node_record(db, targetname, tpgt, ip, port, iface); -+ rec = create_node_record(targetname, tpgt, ip, port, iface, 1); - if (!rec) { - rc = -1; - goto out; - } - -- rc = exec_node_op(db, op, do_login, do_logout, do_show, -+ rc = exec_node_op(op, do_login, do_logout, do_show, - do_rescan, do_stats, info_level, rec, - name, value); - break; -@@ -1958,7 +2355,6 @@ main(int argc, char **argv) - } - if (sid >= 0) { - char session[64]; -- struct iscsi_transport *t; - struct session_info *info; - - snprintf(session, 63, "session%d", sid); -@@ -1977,30 +2373,34 @@ main(int argc, char **argv) - goto free_info; - } - -- t = get_transport_by_sid(sid); -- if (!t) -+ /* -+ * We should be able to go on, but for now -+ * we only support session mode ops if the module -+ * is loaded and we support that module. -+ */ -+ if (!get_transport_by_sid(sid)) - goto free_info; - -- if (!do_logout && !do_rescan && !do_stats && op < 0 && -- info_level > 0) { -- rc = print_session(db, info_level, info); -+ if (!do_logout && !do_rescan && !do_stats && -+ op == OP_NOOP && info_level > 0) { -+ rc = print_sessions(info_level, info); - if (rc) - rc = -1; - goto free_info; - } - -- rec = create_node_record(db, info->targetname, -+ rec = create_node_record(info->targetname, - info->tpgt, - info->persistent_address, - info->persistent_port, -- &info->iface); -+ &info->iface, 1); - if (!rec) { - rc = -1; - goto free_info; - } - - /* drop down to node ops */ -- rc = exec_node_op(db, op, do_login, do_logout, do_show, -+ rc = exec_node_op(op, do_login, do_logout, do_show, - do_rescan, do_stats, info_level, - rec, name, value); - free_info: -@@ -2008,13 +2408,13 @@ free_info: - goto out; - } else { - if (do_logout || do_rescan || do_stats) { -- rc = exec_node_op(db, op, do_login, do_logout, -+ rc = exec_node_op(op, do_login, do_logout, - do_show, do_rescan, do_stats, - info_level, NULL, name, value); - goto out; - } - -- rc = print_sessions(db, info_level); -+ rc = print_sessions(info_level, NULL); - } - break; - default: -@@ -2025,7 +2425,7 @@ free_info: - out: - if (rec) - free(rec); -- idbm_terminate(db); -+ idbm_terminate(); - free_ifaces: - list_for_each_entry_safe(iface, tmp, &ifaces, list) { - list_del(&iface->list); -diff --git a/usr/iscsiadm.h b/usr/iscsiadm.h -index 7a7278a..b9302ab 100644 ---- a/usr/iscsiadm.h -+++ b/usr/iscsiadm.h -@@ -23,17 +23,12 @@ - #include "strings.h" - #include "config.h" - --extern char initiator_name[]; --extern char initiator_alias[]; -- - /* discovery.c */ --struct idbm; - struct discovery_rec; - struct list_head; - --extern int discovery_sendtargets(struct idbm *db, struct discovery_rec *drec, -- struct list_head *ifaces); --extern int discovery_offload_sendtargets(struct idbm *db, int host_no, -- int do_login, discovery_rec_t *drec); -- -+extern int discovery_sendtargets(struct discovery_rec *drec, -+ struct list_head *rec_list); -+extern int discovery_offload_sendtargets(int host_no, int do_login, -+ discovery_rec_t *drec); - #endif /* ISCSIADM_H */ -diff --git a/usr/iscsid.c b/usr/iscsid.c -index 35ceaef..68d78cc 100644 ---- a/usr/iscsid.c -+++ b/usr/iscsid.c -@@ -19,7 +19,6 @@ - * - * See the file COPYING included with this distribution for more details. - */ --#include - #include - #include - #include -@@ -28,13 +27,8 @@ - #include - #include - #include --#include --#include --#include - #include --#include - #include --#include - - #include "iscsid.h" - #include "mgmt_ipc.h" -@@ -46,7 +40,7 @@ - #include "idbm.h" - #include "version.h" - #include "iscsi_sysfs.h" --#include "iscsi_settings.h" -+#include "iface.h" - - /* global config info */ - struct iscsi_daemon_config daemon_config; -@@ -95,14 +89,13 @@ Open-iSCSI initiator daemon.\n\ - } - - static void --setup_rec_from_negotiated_values(idbm_t *db, node_rec_t *rec, -- struct session_info *info) -+setup_rec_from_negotiated_values(node_rec_t *rec, struct session_info *info) - { - struct iscsi_session_operational_config session_conf; - struct iscsi_conn_operational_config conn_conf; - struct iscsi_auth_config auth_conf; - -- idbm_node_setup_from_conf(db, rec); -+ idbm_node_setup_from_conf(rec); - strncpy(rec->name, info->targetname, TARGET_NAME_MAXLEN); - rec->conn[0].port = info->persistent_port; - strncpy(rec->conn[0].address, info->persistent_address, NI_MAXHOST); -@@ -183,8 +176,7 @@ setup_rec_from_negotiated_values(idbm_t *db, node_rec_t *rec, - - static int sync_session(void *data, struct session_info *info) - { -- idbm_t *db = data; -- node_rec_t rec; -+ node_rec_t rec, sysfsrec; - iscsiadm_req_t req; - iscsiadm_rsp_t rsp; - struct iscsi_transport *t; -@@ -216,21 +208,44 @@ static int sync_session(void *data, struct session_info *info) - } - - memset(&rec, 0, sizeof(node_rec_t)); -- if (iface_get_by_bind_info(db, &info->iface, &rec.iface)) { -- log_warning("Could not read data from db. Using default and " -- "currently negotiated values\n"); -- setup_rec_from_negotiated_values(db, &rec, info); -- } else if (idbm_rec_read(db, &rec, info->targetname, info->tpgt, -+ if (idbm_rec_read(&rec, info->targetname, info->tpgt, - info->persistent_address, info->persistent_port, -- &rec.iface)) { -+ &info->iface)) { - log_warning("Could not read data from db. Using default and " - "currently negotiated values\n"); -- setup_rec_from_negotiated_values(db, &rec, info); -+ setup_rec_from_negotiated_values(&rec, info); -+ } else { -+ /* -+ * we have a valid record and iface so lets merge -+ * the values from them and sysfs to try and get -+ * the most uptodate values. -+ * -+ * Currenlty that means we will use the CHAP, target and -+ * and portal values from sysfs and use timer, queue depth, -+ * and segment length values from the record. In the future -+ * when boot supports iface binding we will want to use -+ * those values from sysfs. -+ */ -+ memset(&sysfsrec, 0, sizeof(node_rec_t)); -+ setup_rec_from_negotiated_values(&sysfsrec, info); -+ /* -+ * target and portal values have to be the same or -+ * we would not have found the record, so just copy -+ * CHAP. -+ */ -+ memcpy(&rec.session.auth, &sysfsrec.session.auth, -+ sizeof(struct iscsi_auth_config)); - } - - /* multiple drivers could be connected to the same portal */ - if (!iscsi_match_session(&rec, info)) - return -1; -+ /* -+ * We use the initiator name from sysfs because -+ * the session could have come from our db or ibft or some other -+ * app. -+ */ -+ strcpy(rec.iface.iname, info->iface.iname); - - memset(&req, 0, sizeof(req)); - req.command = MGMT_IPC_SESSION_SYNC; -@@ -241,33 +256,68 @@ static int sync_session(void *data, struct session_info *info) - return 0; - } - -+static char *iscsid_get_config_file(void) -+{ -+ return daemon_config.config_file; -+} -+ - static void sync_sessions(void) - { -- idbm_t *db; - int nr_found = 0; - -- db = idbm_init(daemon_config.config_file); -- if (!db) -+ if (idbm_init(iscsid_get_config_file)) - return; -- sysfs_for_each_session(db, &nr_found, sync_session); -- idbm_terminate(db); --} -- --static void catch_signal(int signo) --{ -- log_warning("caught signal -%d, ignoring...", signo); -+ sysfs_for_each_session(NULL, &nr_found, sync_session); -+ idbm_terminate(); - } - - static void iscsid_exit(void) - { -- log_debug(1, "iscsid_exit"); -+ isns_exit(); -+ ipc->ctldev_close(); -+ mgmt_ipc_close(mgmt_ipc_fd); - if (daemon_config.initiator_name) - free(daemon_config.initiator_name); - if (daemon_config.initiator_alias) - free(daemon_config.initiator_alias); - free_initiator(); -- mgmt_ipc_close(mgmt_ipc_fd); -- ipc->ctldev_close(); -+} -+ -+static void iscsid_shutdown(void) -+{ -+ log_warning("iscsid shutting down."); -+ if (log_daemon && log_pid >= 0) { -+ log_debug(1, "daemon stopping"); -+ log_close(log_pid); -+ fprintf(stderr, "done done\n"); -+ } -+ exit(0); -+} -+ -+static void catch_signal(int signo) -+{ -+ log_debug(1, "%d caught signal -%d...", signo, getpid()); -+ -+ switch (signo) { -+ case SIGTERM: -+ iscsid_shutdown(); -+ break; -+ default: -+ break; -+ } -+} -+ -+static void missing_iname_warn(char *initiatorname_file) -+{ -+ fprintf(stderr, "Warning: initiatorname file %s doesn't " -+ "exist. If using software iscsi (iscsi_tcp or ib_iser) or " -+ "partial offload (bnx iscsi), you may not be able to log " -+ "into or discovery targets. Please create a file %s that " -+ "contains a sting with the format: InitiatorName=" -+ "iqn.yyyy-mm.[:identifier].\n\n" -+ "Example: InitiatorName=iqn.2001-04.com.redhat:fc6.\n" -+ "If using hardware iscsi like qla4xxx this message can be " -+ "ignored.\n", initiatorname_file, initiatorname_file); - } - - int main(int argc, char *argv[]) -@@ -329,24 +379,14 @@ int main(int argc, char *argv[]) - } - } - -- /* make sure we have initiatorname config file first */ -- if (access(initiatorname_file, R_OK)) { -- fprintf(stderr, "Error: initiatorname file %s doesn't exist.\n", -- initiatorname_file); -- fprintf(stderr, "Please create a file %s that contains\n", -- initiatorname_file); -- fprintf(stderr, "a sting with the format: InitiatorName=" -- "iqn.yyyy-mm.[:identifier].\n"); -- fprintf(stderr, "Example: InitiatorName=iqn.2001-04.com.redhat:" -- "fc6\n"); -- usage(0); -- } -- - /* initialize logger */ - log_pid = log_init(program_name, DEFAULT_AREA_SIZE); - if (log_pid < 0) - exit(1); -- check_class_version(); -+ if (check_class_version()) { -+ log_close(log_pid); -+ exit(1); -+ } - - umask(0177); - -@@ -356,11 +396,14 @@ int main(int argc, char *argv[]) - daemon_config.initiator_alias = NULL; - if (atexit(iscsid_exit)) { - log_error("failed to set exit function\n"); -+ log_close(log_pid); - exit(1); - } - -- if ((mgmt_ipc_fd = mgmt_ipc_listen()) < 0) -- exit(-1); -+ if ((mgmt_ipc_fd = mgmt_ipc_listen()) < 0) { -+ log_close(log_pid); -+ exit(1); -+ } - - if (log_daemon) { - char buf[64]; -@@ -369,23 +412,28 @@ int main(int argc, char *argv[]) - fd = open(pid_file, O_WRONLY|O_CREAT, 0644); - if (fd < 0) { - log_error("Unable to create pid file"); -+ log_close(log_pid); - exit(1); - } - pid = fork(); - if (pid < 0) { - log_error("Starting daemon failed"); -+ log_close(log_pid); - exit(1); - } else if (pid) { - log_error("iSCSI daemon with pid=%d started!", pid); - exit(0); - } - -- if ((control_fd = ipc->ctldev_open()) < 0) -- exit(-1); -+ if ((control_fd = ipc->ctldev_open()) < 0) { -+ log_close(log_pid); -+ exit(1); -+ } - - chdir("/"); - if (lockf(fd, F_TLOCK, 0) < 0) { - log_error("Unable to lock pid file"); -+ log_close(log_pid); - exit(1); - } - ftruncate(fd, 0); -@@ -394,8 +442,10 @@ int main(int argc, char *argv[]) - - daemon_init(); - } else { -- if ((control_fd = ipc->ctldev_open()) < 0) -- exit(-1); -+ if ((control_fd = ipc->ctldev_open()) < 0) { -+ log_close(log_pid); -+ exit(1); -+ } - } - - if (uid && setuid(uid) < 0) -@@ -407,17 +457,14 @@ int main(int argc, char *argv[]) - memset(&daemon_config, 0, sizeof (daemon_config)); - daemon_config.pid_file = pid_file; - daemon_config.config_file = config_file; -- daemon_config.initiator_name_file = initiatorname_file; - daemon_config.initiator_name = -- get_iscsi_initiatorname(daemon_config.initiator_name_file); -- if (daemon_config.initiator_name == NULL) { -- log_warning("exiting due to configuration error"); -- exit(1); -- } -+ get_iscsi_initiatorname(initiatorname_file); -+ if (daemon_config.initiator_name == NULL) -+ missing_iname_warn(initiatorname_file); - - /* optional InitiatorAlias */ - daemon_config.initiator_alias = -- get_iscsi_initiatoralias(daemon_config.initiator_name_file); -+ get_iscsi_initiatoralias(initiatorname_file); - if (!daemon_config.initiator_alias) { - memset(&host_info, 0, sizeof (host_info)); - if (uname(&host_info) >= 0) { -@@ -426,7 +473,8 @@ int main(int argc, char *argv[]) - } - } - -- log_debug(1, "InitiatorName=%s", daemon_config.initiator_name); -+ log_debug(1, "InitiatorName=%s", daemon_config.initiator_name ? -+ daemon_config.initiator_name : "NOT SET"); - log_debug(1, "InitiatorAlias=%s", daemon_config.initiator_alias); - - pid = fork(); -@@ -447,14 +495,14 @@ int main(int argc, char *argv[]) - /* we don't want our active sessions to be paged out... */ - if (mlockall(MCL_CURRENT | MCL_FUTURE)) { - log_error("failed to mlockall, exiting..."); -+ log_close(log_pid); - exit(1); - } - -+ increase_max_files(); - actor_init(); - isns_fd = isns_init(); - event_loop(ipc, control_fd, mgmt_ipc_fd, isns_fd); -- isns_exit(); -- -- log_debug(1, "daemon stopping"); -+ iscsid_shutdown(); - return 0; - } -diff --git a/usr/iscsid.h b/usr/iscsid.h -index 7437d9e..b646f32 100644 ---- a/usr/iscsid.h -+++ b/usr/iscsid.h -@@ -27,7 +27,6 @@ extern struct iscsi_ipc *ipc; - struct iscsi_daemon_config { - char *config_file; - char *pid_file; -- char *initiator_name_file; - char *initiator_name; - char *initiator_alias; - }; -diff --git a/usr/iscsistart.c b/usr/iscsistart.c -index 5ef05bb..870f3b0 100644 ---- a/usr/iscsistart.c -+++ b/usr/iscsistart.c -@@ -42,6 +42,7 @@ - #include "version.h" - #include "iscsi_sysfs.h" - #include "iscsi_settings.h" -+#include "fw_context.h" - - /* global config info */ - /* initiator needs initiator name/alias */ -@@ -68,6 +69,8 @@ static struct option const long_options[] = { - {"username_in", required_argument, NULL, 'U'}, - {"password_in", required_argument, NULL, 'W'}, - {"debug", required_argument, NULL, 'd'}, -+ {"fwparam_connect", no_argument, NULL, 'b'}, -+ {"fwparam_print", no_argument, NULL, 'f'}, - {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, - {NULL, 0, NULL, 0}, -@@ -92,6 +95,8 @@ Open-iSCSI initiator.\n\ - -U, --username_in=N set incoming username to N (optional)\n\ - -W, --password_in=N set incoming password to N (optional\n\ - -d, --debug debuglevel print debugging information \n\ -+ -b, --fwparam_connect create a session to the target\n\ -+ -f, --fwparam_print print the iBFT to STDOUT \n\ - -h, --help display this help and exit\n\ - -v, --version display version and exit\n\ - "); -@@ -108,11 +113,10 @@ static int stop_event_loop(void) - memset(&req, 0, sizeof(req)); - req.command = MGMT_IPC_IMMEDIATE_STOP; - rc = do_iscsid(&req, &rsp); -- if (rc) -+ if (rc) { - iscsid_handle_error(rc); -- if (rc) - log_error("Could not stop event_loop\n"); -- -+ } - return rc; - } - -@@ -145,22 +149,22 @@ static int check_params(char *initiatorname) - { - if (!initiatorname) { - log_error("InitiatorName not set. Exiting %s\n", program_name); -- return -EINVAL; -+ return EINVAL; - } - - if (config_rec.tpgt == PORTAL_GROUP_TAG_UNKNOWN) { - log_error("Portal Group not set. Exiting %s\n", program_name); -- return -EINVAL; -+ return EINVAL; - } - - if (!strlen(config_rec.name)) { - log_error("TargetName not set. Exiting %s\n", program_name); -- return -EINVAL; -+ return EINVAL; - } - - if (!strlen(config_rec.conn[0].address)) { - log_error("IP Address not set. Exiting %s\n", program_name); -- return -EINVAL; -+ return EINVAL; - } - - return 0; -@@ -175,15 +179,15 @@ do { \ - } \ - } while (0); - --/* TODO fix leaks */ - int main(int argc, char *argv[]) - { - struct utsname host_info; /* will use to compound initiator alias */ - struct iscsi_auth_config *auth; - char *initiatorname = NULL; -- int ch, longindex; -+ int ch, longindex, ret; - struct sigaction sa_old; - struct sigaction sa_new; -+ struct boot_context context; - pid_t pid; - - idbm_node_setup_defaults(&config_rec); -@@ -197,7 +201,7 @@ int main(int argc, char *argv[]) - sa_new.sa_flags = 0; - sigaction(SIGINT, &sa_new, &sa_old ); - -- while ((ch = getopt_long(argc, argv, "i:t:g:a:p:d:u:w:U:W:vh", -+ while ((ch = getopt_long(argc, argv, "i:t:g:a:p:d:u:w:U:W:bfvh", - long_options, &longindex)) >= 0) { - switch (ch) { - case 'i': -@@ -246,6 +250,44 @@ int main(int argc, char *argv[]) - case 'd': - log_level = atoi(optarg); - break; -+ case 'b': -+ ret = fw_get_entry(&context, NULL); -+ if (ret) { -+ printf("Could not setup fw entries."); -+ exit(-1); -+ } -+ -+ initiatorname = context.initiatorname; -+ strncpy(config_rec.name, context.targetname, -+ sizeof(context.targetname)); -+ strncpy(config_rec.conn[0].address, -+ context.target_ipaddr, -+ sizeof(context.target_ipaddr)); -+ config_rec.conn[0].port = context.target_port; -+ /* this seems broken ??? */ -+ config_rec.tpgt = 1; -+ strncpy(auth->username, context.chap_name, -+ sizeof(context.chap_name)); -+ strncpy((char *)auth->password, context.chap_password, -+ sizeof(context.chap_password)); -+ auth->password_length = strlen((char *)auth->password); -+ strncpy(auth->username_in, context.chap_name_in, -+ sizeof(context.chap_name_in)); -+ strncpy((char *)auth->password_in, -+ context.chap_password_in, -+ sizeof(context.chap_password_in)); -+ auth->password_in_length = -+ strlen((char *)auth->password_in); -+ break; -+ case 'f': -+ ret = fw_get_entry(&context, NULL); -+ if (ret) { -+ printf("Could not read fw values.\n"); -+ exit(-1); -+ } -+ -+ fw_print_entry(&context); -+ exit(0); - case 'v': - printf("%s version %s\n", program_name, - ISCSI_VERSION_STR); -@@ -263,7 +305,9 @@ int main(int argc, char *argv[]) - log_daemon = 0; - log_init(program_name, DEFAULT_AREA_SIZE); - -- check_class_version(); -+ if (check_class_version()) -+ exit(1); -+ - if (check_params(initiatorname)) - exit(1); - -diff --git a/usr/isns.c b/usr/isns.c -index b823bf4..e6a4a65 100644 ---- a/usr/isns.c -+++ b/usr/isns.c -@@ -50,16 +50,17 @@ struct isns_task { - int done; - int retry; - queue_task_t *qtask; -+ struct actor actor; - }; - --static actor_t isns_actor; --static queue_t *isns_queue = NULL; - static struct sockaddr_storage ss; - static uint16_t transaction; - - static char isns_address[NI_MAXHOST]; - static int isns_port = 3205, isns_listen_port, max_retry = 10000; - -+static void isns_poll(void *data); -+ - #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - - #define get_hdr_param(hdr, function, length, flags, transaction, sequence) \ -@@ -270,17 +271,24 @@ static int isns_recv_pdu(struct isns_task *task) - return 0; - } - -+static char *isns_get_config_file(void) -+{ -+ return dconfig->config_file; -+} -+ - static void add_new_target_node(char *targetname, uint8_t *ip, int port, - int tag) - { - int err; - node_rec_t rec; - discovery_rec_t drec; -- idbm_t *db; - char dst[INET6_ADDRSTRLEN]; - - memset(dst, 0, sizeof(dst)); -- if (!memcmp(ip, dst, 10) && ip[10] == 0xff && ip[11] == 0xff) -+ /* -+ * some servers are sending compat instead of mapped -+ */ -+ if (IN6_IS_ADDR_V4MAPPED(ip) || IN6_IS_ADDR_V4COMPAT(ip)) - inet_ntop(AF_INET, ip + 12, dst, sizeof(dst)); - else - inet_ntop(AF_INET6, ip, dst, sizeof(dst)); -@@ -288,26 +296,26 @@ static void add_new_target_node(char *targetname, uint8_t *ip, int port, - log_debug(1, "add a new target node:%s %s,%d %d", - targetname, dst, port, tag); - -- db = idbm_init(dconfig->config_file); -- if (!db) { -+ if (idbm_init(isns_get_config_file)) { - log_error("Could not add new target node:%s %s,%d", - targetname, dst, port); - return; - } -- idbm_node_setup_from_conf(db, &rec); -+ idbm_node_setup_from_conf(&rec); - strncpy(rec.name, targetname, TARGET_NAME_MAXLEN); - rec.conn[0].port = port; - rec.tpgt = tag; - strncpy(rec.conn[0].address, dst, NI_MAXHOST); - - /* TODO?: shoudl we set the address and port of the server ? */ -+ memset(&drec, 0, sizeof(discovery_rec_t)); - drec.type = DISCOVERY_TYPE_ISNS; -- err = idbm_add_nodes(db, &rec, &drec, NULL); -+ err = idbm_add_nodes(&rec, &drec, NULL, 0); - if (err) - log_error("Could not add new target node:%s %s,%d", - targetname, dst, port); - -- idbm_terminate(db); -+ idbm_terminate(); - } - - static int qry_rsp_handle(struct isns_hdr *hdr) -@@ -432,8 +440,8 @@ static int isns_task_done(struct isns_task *task) - task->len = length + sizeof(*hdr); - task->done = 0; - -- queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, task); -+ actor_schedule(&task->actor); - finished = 0; - break; - default: -@@ -502,8 +510,8 @@ int isns_dev_attr_query_task(queue_task_t *qtask) - - qtask->rsp.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY; - -- queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, task); -+ actor_schedule(&task->actor); - - return MGMT_IPC_OK; - } -@@ -532,15 +540,15 @@ void isns_handle(int listen_fd) - task->state = ISNS_TASK_RECV_PDU; - task->fd = fd; - -- queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, task); -+ actor_schedule(&task->actor); - } - --static void isns_poll(queue_item_t *item) -+static void isns_poll(void *data) - { - int err, finished; - struct pollfd pfd; -- struct isns_task *task = item->context; -+ struct isns_task *task = data; - struct isns_hdr *hdr = (struct isns_hdr *) task->data; - uint16_t function = ntohs(hdr->function); - -@@ -573,9 +581,8 @@ static void isns_poll(queue_item_t *item) - goto free_task; - } - -- queue_produce(isns_queue, EV_CONN_POLL, task, 0, -- NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, task); -+ actor_schedule(&task->actor); - } - break; - case ISNS_TASK_RECV_PDU: -@@ -590,9 +597,9 @@ static void isns_poll(queue_item_t *item) - goto free_task; - } else { - /* need to read more */ -- queue_produce(isns_queue, EV_CONN_POLL, -- task, 0, NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, -+ task); -+ actor_schedule(&task->actor); - } - } - } -@@ -602,8 +609,8 @@ static void isns_poll(queue_item_t *item) - log_error("abort task"); - goto abort_task; - } else { -- queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, task); -+ actor_schedule(&task->actor); - } - } - -@@ -615,32 +622,6 @@ free_task: - isns_free_task(task); - } - --static void isns_control(void *data) --{ -- int count = isns_queue->count, i; -- int err; -- unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX]; -- queue_item_t *item = (queue_item_t *)(void *)item_buf; -- -- for (i = 0; i < count; i++) { -- err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item); -- if (err == QUEUE_IS_EMPTY) { -- log_debug(4, "%d items flushed while mainloop " -- "was processing", count - i); -- break; -- } -- -- switch (item->event_type) { -- case EV_CONN_POLL: -- isns_poll(item); -- break; -- default: -- log_error("%d unknown event type", item->event_type); -- break; -- } -- } --} -- - static int isns_dev_register(void) - { - struct isns_task *task; -@@ -658,10 +639,9 @@ static int isns_dev_register(void) - - task->state = ISNS_TASK_WAIT_CONN; - build_dev_reg_req(task); -- queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); - -- actor_new(&isns_actor, isns_control, NULL); -- actor_schedule(&isns_actor); -+ actor_new(&task->actor, isns_poll, task); -+ actor_schedule(&task->actor); - - return 0; - } -@@ -722,7 +702,7 @@ int isns_init(void) - int fd = -1, err; - FILE *f; - -- f = fopen(dconfig->config_file, "r"); -+ f = fopen(isns_get_config_file(), "r"); - if (!f) - return -EIO; - -@@ -741,62 +721,23 @@ int isns_init(void) - if (!strlen(isns_address)) - return -1; - -- isns_queue = queue_create(4, 4, NULL, NULL); -- if (!isns_queue) { -- log_error("can't create queue %m"); -- return -ENOMEM; -- } -- - snprintf(port, sizeof(port), "%d", isns_port); - err = resolve_address(isns_address, port, &ss); - if (err) { - log_error("can't resolve address %m, %s", isns_address); -- goto free_queue; -+ return err; - } - - err = isns_listen_init(&fd); - if (err) -- goto free_queue; -+ return err; - - isns_dev_register(); - return fd; -- --free_queue: -- queue_destroy(isns_queue); -- return err; - } - - void isns_exit(void) - { -- int err, count, i; -- unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX]; -- queue_item_t *item = (queue_item_t *)(void *)item_buf; -- -- if (!isns_queue) -- return; -- -- count = isns_queue->count; -- /* -- * TODO: Add some code to gracefully shutdown. -- */ -- for (i = 0; i < count; i++) { -- err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item); -- if (err == QUEUE_IS_EMPTY) { -- log_debug(4, "%d items flushed while mainloop " -- "was processing", count - i); -- break; -- } -- -- log_debug(4, "Dropping event type %d\n", item->event_type); -- switch (item->event_type) { -- case EV_CONN_POLL: -- isns_free_task(item->context); -- continue; -- default: -- log_error("%d unknown event type", item->event_type); -- continue; -- } -- } -- -- queue_destroy(isns_queue); -+ /* do nothing for now */ -+ ; - } -diff --git a/usr/list.h b/usr/list.h -index 3e85c9b..bbf3425 100644 ---- a/usr/list.h -+++ b/usr/list.h -@@ -1,6 +1,7 @@ - #ifndef __LIST_H__ - #define __LIST_H__ - -+#include - /* taken from linux kernel */ - - #undef offsetof -@@ -83,4 +84,10 @@ static inline void list_del(struct list_head *entry) - entry->next = entry->prev = NULL; - } - -+static inline void list_del_init(struct list_head *entry) -+{ -+ __list_del(entry->prev, entry->next); -+ INIT_LIST_HEAD(entry); -+} -+ - #endif -diff --git a/usr/log.c b/usr/log.c -index 6746bb3..013caed 100644 ---- a/usr/log.c -+++ b/usr/log.c -@@ -12,6 +12,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -36,6 +37,33 @@ int log_level = 0; - - static int log_stop_daemon = 0; - -+static void free_logarea (void) -+{ -+ int shmid; -+ -+ if (!la) -+ return; -+ -+ if (la->semid != -1) -+ semctl(la->semid, 0, IPC_RMID, la->semarg); -+ if (la->buff) { -+ shmdt(la->buff); -+ shmctl(la->shmid_buff, IPC_RMID, NULL); -+ la->buff = NULL; -+ la->shmid_buff = -1; -+ } -+ if (la->start) { -+ shmdt(la->start); -+ shmctl(la->shmid_msg, IPC_RMID, NULL); -+ la->start = NULL; -+ la->shmid_msg = -1; -+ } -+ shmid = la->shmid; -+ shmdt(la); -+ shmctl(shmid, IPC_RMID, NULL); -+ la = NULL; -+} -+ - static int logarea_init (int size) - { - int shmid; -@@ -47,21 +75,28 @@ static int logarea_init (int size) - return 1; - - la = shmat(shmid, NULL, 0); -- if (!la) -+ if (!la) { -+ shmctl(shmid, IPC_RMID, NULL); - return 1; -+ } -+ la->shmid = shmid; -+ la->start = NULL; -+ la->buff = NULL; -+ la->semid = -1; - - if (size < MAX_MSG_SIZE) - size = DEFAULT_AREA_SIZE; - - if ((shmid = shmget(IPC_PRIVATE, size, - 0644 | IPC_CREAT | IPC_EXCL)) == -1) { -- shmdt(la); -+ free_logarea(); - return 1; - } -+ la->shmid_msg = shmid; - -- la->start = shmat(shmid, NULL, 0); -+ la->start = shmat(la->shmid_msg, NULL, 0); - if (!la->start) { -- shmdt(la); -+ free_logarea(); - return 1; - } - memset(la->start, 0, size); -@@ -73,32 +108,27 @@ static int logarea_init (int size) - - if ((shmid = shmget(IPC_PRIVATE, MAX_MSG_SIZE + sizeof(struct logmsg), - 0644 | IPC_CREAT | IPC_EXCL)) == -1) { -- shmdt(la->start); -- shmdt(la); -+ free_logarea(); - return 1; - } - la->buff = shmat(shmid, NULL, 0); - if (!la->buff) { -- shmdt(la->start); -- shmdt(la); -+ free_logarea(); - return 1; - } - - if ((la->semid = semget(SEMKEY, 1, 0600 | IPC_CREAT)) < 0) { -- shmdt(la->buff); -- shmdt(la->start); -- shmdt(la); -+ free_logarea(); - return 1; - } - - la->semarg.val=1; - if (semctl(la->semid, 0, SETVAL, la->semarg) < 0) { -- shmdt(la->buff); -- shmdt(la->start); -- shmdt(la); -+ free_logarea(); - return 1; - } - -+ la->shmid_buff = shmid; - la->ops[0].sem_num = 0; - la->ops[0].sem_flg = 0; - -@@ -106,15 +136,6 @@ static int logarea_init (int size) - - } - --static void free_logarea (void) --{ -- semctl(la->semid, 0, IPC_RMID, la->semarg); -- shmdt(la->buff); -- shmdt(la->start); -- shmdt(la); -- return; --} -- - #if LOGDBG - static void dump_logarea (void) - { -@@ -196,7 +217,7 @@ int log_dequeue (void * buff) - int len; - - if (la->empty) -- return 1; -+ return 0; - - len = strlen((char *)&src->str) * sizeof(char) + - sizeof(struct logmsg) + 1; -@@ -215,7 +236,7 @@ int log_dequeue (void * buff) - - memset((void *)src, 0, len); - -- return la->empty; -+ return len; - } - - /* -@@ -233,7 +254,7 @@ static void dolog(int prio, const char *fmt, va_list ap) - if (log_daemon) { - la->ops[0].sem_op = -1; - if (semop(la->semid, la->ops, 1) < 0) { -- syslog(LOG_ERR, "semop up failed"); -+ syslog(LOG_ERR, "semop up failed %d", errno); - return; - } - -@@ -314,19 +335,22 @@ static void __dump_char(int level, unsigned char *buf, int *cp, int ch) - - static void log_flush(void) - { -+ int msglen; -+ - while (!la->empty) { - la->ops[0].sem_op = -1; - if (semop(la->semid, la->ops, 1) < 0) { -- syslog(LOG_ERR, "semop up failed"); -+ syslog(LOG_ERR, "semop up failed %d", errno); - exit(1); - } -- log_dequeue(la->buff); -+ msglen = log_dequeue(la->buff); - la->ops[0].sem_op = 1; - if (semop(la->semid, la->ops, 1) < 0) { - syslog(LOG_ERR, "semop down failed"); - exit(1); - } -- log_syslog(la->buff); -+ if (msglen) -+ log_syslog(la->buff); - } - } - -@@ -347,7 +371,6 @@ static void catch_signal(int signo) - static void __log_close(void) - { - if (log_daemon) { -- fprintf(stderr, "close log\n"); - log_flush(); - closelog(); - free_logarea(); -@@ -403,7 +426,6 @@ int log_init(char *program_name, int size) - - return 0; - } -- - void log_close(pid_t pid) - { - int status; -diff --git a/usr/log.h b/usr/log.h -index 4816a1b..4d2a265 100644 ---- a/usr/log.h -+++ b/usr/log.h -@@ -51,6 +51,9 @@ struct logmsg { - }; - - struct logarea { -+ int shmid; -+ int shmid_msg; -+ int shmid_buff; - int empty; - void *head; - void *tail; -diff --git a/usr/login.c b/usr/login.c -index 614fd42..7c004cd 100644 ---- a/usr/login.c -+++ b/usr/login.c -@@ -264,24 +264,20 @@ get_security_text_keys(iscsi_session_t *session, int cid, char **data, - &value, &value_end)) { - /* - * We should have already obtained this -- * via discovery. -- * We've already picked an isid, so the -- * most we can do is confirm we reached -- * the portal group we were expecting to -+ * via discovery, but the value could be stale. -+ * If the target was reconfigured it will send us -+ * the updated tpgt. - */ - tag = strtoul(value, NULL, 0); - if (session->portal_group_tag >= 0) { -- if (tag != session->portal_group_tag) { -- log_error("Portal group tag " -+ if (tag != session->portal_group_tag) -+ log_debug(2, "Portal group tag " - "mismatch, expected %u, " -- "received %u", -+ "received %u. Updating", - session->portal_group_tag, tag); -- return LOGIN_WRONG_PORTAL_GROUP; -- } -- } else -- /* we now know the tag */ -- session->portal_group_tag = tag; -- -+ } -+ /* we now know the tag */ -+ session->portal_group_tag = tag; - text = value_end; - } else { - /* -@@ -339,21 +335,22 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, - } - } else if (iscsi_find_key_value("TargetPortalGroupTag", text, end, - &value, &value_end)) { -+ int tag = strtoul(value, NULL, 0); - /* -- * confirm we reached the portal group we were expecting to -+ * We should have already obtained this -+ * via discovery, but the value could be stale. -+ * If the target was reconfigured it will send us -+ * the updated tpgt. - */ -- int tag = strtoul(value, NULL, 0); - if (session->portal_group_tag >= 0) { -- if (tag != session->portal_group_tag) { -- log_error("Portal group tag mismatch, " -- "expected %u, received %u", -+ if (tag != session->portal_group_tag) -+ log_debug(2, "Portal group tag " -+ "mismatch, expected %u, " -+ "received %u. Updating", - session->portal_group_tag, tag); -- return LOGIN_WRONG_PORTAL_GROUP; -- } -- } else -- /* we now know the tag */ -- session->portal_group_tag = tag; -- -+ } -+ /* we now know the tag */ -+ session->portal_group_tag = tag; - text = value_end; - } else if (iscsi_find_key_value("InitialR2T", text, end, &value, - &value_end)) { -@@ -1399,8 +1396,6 @@ iscsi_login_req(iscsi_session_t *session, iscsi_login_context_t *c) - c->ret = LOGIN_IO_ERROR; - goto done; - } -- -- conn->state = STATE_IN_LOGIN; - return 0; - - done: -@@ -1464,8 +1459,6 @@ iscsi_login_rsp(iscsi_session_t *session, iscsi_login_context_t *c) - &c->final); - if (c->final) - goto done; -- -- conn->state = STATE_FREE; - return 0; - - done: -@@ -1501,7 +1494,6 @@ iscsi_login(iscsi_session_t *session, int cid, char *buffer, size_t bufsize, - iscsi_login_context_t *c = &conn->login_context; - int ret; - -- conn->kernel_io = 0; - /* - * assume iscsi_login is only called from discovery, so it is - * safe to always set to zero -diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c -index fb2be80..7520c80 100644 ---- a/usr/mgmt_ipc.c -+++ b/usr/mgmt_ipc.c -@@ -21,20 +21,13 @@ - * - * See the file COPYING included with this distribution for more details. - */ -+#include - #include - #include -- --#include --#include --#include --#include --#include -+#include - #include --#include --#include - #include - #include --#include - - #include "iscsid.h" - #include "idbm.h" -@@ -42,9 +35,11 @@ - #include "iscsi_ipc.h" - #include "log.h" - #include "transport.h" --#include "iscsi_sysfs.h" -+ -+static int leave_event_loop = 0; - - #define PEERUSER_MAX 64 -+#define EXTMSG_MAX (64 * 1024) - - int - mgmt_ipc_listen(void) -@@ -81,118 +76,129 @@ mgmt_ipc_listen(void) - void - mgmt_ipc_close(int fd) - { -+ leave_event_loop = 1; -+ if (fd >= 0) -+ close(fd); - } - - static mgmt_ipc_err_e --mgmt_ipc_session_login(queue_task_t *qtask, node_rec_t *rec) -+mgmt_ipc_session_login(queue_task_t *qtask) - { -- if (session_is_running(rec)) { -- log_error("session [%s,%s,%d] already running.", rec->name, -- rec->conn[0].address, rec->conn[0].port); -- return MGMT_IPC_ERR_EXISTS; -- } -- -- return session_login_task(rec, qtask); -+ return session_login_task(&qtask->req.u.session.rec, qtask); - } - - static mgmt_ipc_err_e --mgmt_ipc_session_getstats(queue_task_t *qtask, int sid, -- iscsiadm_rsp_t *rsp) -+mgmt_ipc_session_getstats(queue_task_t *qtask) - { -- struct iscsi_transport *t; -+ int sid = qtask->req.u.session.sid; - iscsi_session_t *session; -+ int rc; - -- list_for_each_entry(t, &transports, list) { -- list_for_each_entry(session, &t->sessions, list) { -- if (session->id == sid) { -- int rc; -- -- rc = ipc->get_stats(session->t->handle, -- session->id, session->conn[0].id, -- (void*)&rsp->u.getstats, -- MGMT_IPC_GETSTATS_BUF_MAX); -- if (rc) { -- log_error("get_stats(): IPC error %d " -- "session [%02d]", rc, sid); -- return MGMT_IPC_ERR_INTERNAL; -- } -- return MGMT_IPC_OK; -- } -- } -+ if (!(session = session_find_by_sid(sid))) -+ return MGMT_IPC_ERR_NOT_FOUND; -+ -+ rc = ipc->get_stats(session->t->handle, -+ session->id, session->conn[0].id, -+ (void *)&qtask->rsp.u.getstats, -+ MGMT_IPC_GETSTATS_BUF_MAX); -+ if (rc) { -+ log_error("get_stats(): IPC error %d " -+ "session [%02d]", rc, sid); -+ return MGMT_IPC_ERR_INTERNAL; - } - -- return MGMT_IPC_ERR_NOT_FOUND; -+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); -+ return MGMT_IPC_OK; - } - - static mgmt_ipc_err_e --mgmt_ipc_session_logout(queue_task_t *qtask, node_rec_t *rec) -+mgmt_ipc_send_targets(queue_task_t *qtask) - { -- iscsi_session_t *session; -- -- if (!(session = session_find_by_rec(rec))) { -- log_error("session [%s,%s,%d] not found!", rec->name, -- rec->conn[0].address, rec->conn[0].port); -- return MGMT_IPC_ERR_NOT_FOUND; -- } -+ iscsiadm_req_t *req = &qtask->req; -+ mgmt_ipc_err_e err; - -- return session_logout_task(session, qtask); -+ err = iscsi_host_send_targets(qtask, req->u.st.host_no, -+ req->u.st.do_login, -+ &req->u.st.ss); -+ mgmt_ipc_write_rsp(qtask, err); -+ return MGMT_IPC_OK; - } - - static mgmt_ipc_err_e --mgmt_ipc_session_sync(queue_task_t *qtask, node_rec_t *rec, int sid) -+mgmt_ipc_session_logout(queue_task_t *qtask) - { -- return iscsi_sync_session(rec, qtask, sid); -+ return session_logout_task(qtask->req.u.session.sid, qtask); - } - - static mgmt_ipc_err_e --mgmt_ipc_cfg_initiatorname(queue_task_t *qtask, iscsiadm_rsp_t *rsp) -+mgmt_ipc_session_sync(queue_task_t *qtask) - { -- strcpy(rsp->u.config.var, dconfig->initiator_name); -+ struct ipc_msg_session *session= &qtask->req.u.session; - -+ return iscsi_sync_session(&session->rec, qtask, session->sid); -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_cfg_initiatorname(queue_task_t *qtask) -+{ -+ if (dconfig->initiator_name) -+ strcpy(qtask->rsp.u.config.var, dconfig->initiator_name); -+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); - return MGMT_IPC_OK; - } - - static mgmt_ipc_err_e --mgmt_ipc_session_info(queue_task_t *qtask, int sid, iscsiadm_rsp_t *rsp) -+mgmt_ipc_session_info(queue_task_t *qtask) - { -+ int sid = qtask->req.u.session.sid; - iscsi_session_t *session; - struct ipc_msg_session_state *info; - - if (!(session = session_find_by_sid(sid))) { -- log_error("session with sid %d not found!", sid); -+ log_debug(1, "session with sid %d not found!", sid); - return MGMT_IPC_ERR_NOT_FOUND; - } - -- info = &rsp->u.session_state; -+ info = &qtask->rsp.u.session_state; - info->conn_state = session->conn[0].state; - info->session_state = session->r_stage; -+ -+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); - return MGMT_IPC_OK; - } - - static mgmt_ipc_err_e --mgmt_ipc_cfg_initiatoralias(queue_task_t *qtask, iscsiadm_rsp_t *rsp) -+mgmt_ipc_cfg_initiatoralias(queue_task_t *qtask) - { -- strcpy(rsp->u.config.var, dconfig->initiator_alias); -- -+ strcpy(qtask->rsp.u.config.var, dconfig->initiator_alias); -+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); - return MGMT_IPC_OK; - } - - static mgmt_ipc_err_e --mgmt_ipc_cfg_filename(queue_task_t *qtask, iscsiadm_rsp_t *rsp) -+mgmt_ipc_cfg_filename(queue_task_t *qtask) - { -- strcpy(rsp->u.config.var, dconfig->config_file); -- -+ strcpy(qtask->rsp.u.config.var, dconfig->config_file); -+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); - return MGMT_IPC_OK; - } - - static mgmt_ipc_err_e --mgmt_ipc_conn_add(queue_task_t *qtask, int cid) -+mgmt_ipc_conn_add(queue_task_t *qtask) - { - return MGMT_IPC_ERR; - } - - static mgmt_ipc_err_e --mgmt_ipc_conn_remove(queue_task_t *qtask, int cid) -+mgmt_ipc_immediate_stop(queue_task_t *qtask) -+{ -+ leave_event_loop = 1; -+ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); -+ return MGMT_IPC_OK; -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_conn_remove(queue_task_t *qtask) - { - return MGMT_IPC_ERR; - } -@@ -203,6 +209,137 @@ mgmt_ipc_isns_dev_attr_query(queue_task_t *qtask) - return isns_dev_attr_query_task(qtask); - } - -+ -+static mgmt_ipc_err_e -+mgmt_ipc_host_set_param(queue_task_t *qtask) -+{ -+ struct ipc_msg_set_host_param *hp = &qtask->req.u.set_host_param; -+ int err; -+ -+ err = iscsi_host_set_param(hp->host_no, hp->param, hp->value); -+ mgmt_ipc_write_rsp(qtask, err); -+ return MGMT_IPC_OK; -+} -+ -+/* -+ * Parse a list of strings, encoded as a 32bit -+ * length followed by the string itself (not necessarily -+ * NUL-terminated). -+ */ -+static int -+mgmt_ipc_parse_strings(queue_task_t *qtask, char ***result) -+{ -+ char *data, *endp, **argv = NULL; -+ unsigned int left, argc; -+ -+again: -+ data = qtask->payload; -+ left = qtask->req.payload_len; -+ endp = NULL; -+ argc = 0; -+ -+ while (left) { -+ uint32_t len; -+ -+ if (left < 4) -+ return -1; -+ memcpy(&len, data, 4); -+ data += 4; -+ -+ if (endp) -+ *endp = '\0'; -+ -+ if (len > left) -+ return -1; -+ -+ if (argv) { -+ argv[argc] = (char *) data; -+ endp = data + len; -+ } -+ data += len; -+ argc++; -+ } -+ -+ if (endp) -+ *endp = '\0'; -+ -+ if (argv == NULL) { -+ argv = malloc((argc + 1) * sizeof(char *)); -+ *result = argv; -+ goto again; -+ } -+ -+ argv[argc] = NULL; -+ return argc; -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_notify_common(queue_task_t *qtask, -+ mgmt_ipc_err_e (*handler)(int, char **)) -+{ -+ char **argv = NULL; -+ int argc, err = MGMT_IPC_ERR; -+ -+ argc = mgmt_ipc_parse_strings(qtask, &argv); -+ if (argc > 0) -+ err = handler(argc, argv); -+ -+ if (argv) -+ free(argv); -+ mgmt_ipc_write_rsp(qtask, err); -+ return MGMT_IPC_OK; -+} -+ -+/* Replace these dummies as you implement them -+ elsewhere */ -+static mgmt_ipc_err_e -+iscsi_discovery_add_node(int argc, char **argv) -+{ -+ return MGMT_IPC_OK; -+} -+ -+static mgmt_ipc_err_e -+iscsi_discovery_del_node(int argc, char **argv) -+{ -+ return MGMT_IPC_OK; -+} -+ -+static mgmt_ipc_err_e -+iscsi_discovery_add_portal(int argc, char **argv) -+{ -+ return MGMT_IPC_OK; -+} -+ -+static mgmt_ipc_err_e -+iscsi_discovery_del_portal(int argc, char **argv) -+{ -+ return MGMT_IPC_OK; -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_notify_add_node(queue_task_t *qtask) -+{ -+ return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_node); -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_notify_del_node(queue_task_t *qtask) -+{ -+ return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_node); -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_notify_add_portal(queue_task_t *qtask) -+{ -+ return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_portal); -+} -+ -+static mgmt_ipc_err_e -+mgmt_ipc_notify_del_portal(queue_task_t *qtask) -+{ -+ return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_portal); -+} -+ - static int - mgmt_peeruser(int sock, char *user) - { -@@ -287,6 +424,24 @@ mgmt_peeruser(int sock, char *user) - #endif - } - -+static void -+mgmt_ipc_destroy_queue_task(queue_task_t *qtask) -+{ -+ if (qtask->mgmt_ipc_fd >= 0) -+ close(qtask->mgmt_ipc_fd); -+ if (qtask->payload) -+ free(qtask->payload); -+ if (qtask->allocated) -+ free(qtask); -+} -+ -+/* -+ * Send the IPC response and destroy the queue_task. -+ * The recovery code uses a qtask which is allocated as -+ * part of a larger structure, and we don't want it to -+ * get freed when we come here. This is what qtask->allocated -+ * is for. -+ */ - void - mgmt_ipc_write_rsp(queue_task_t *qtask, mgmt_ipc_err_e err) - { -@@ -295,135 +450,136 @@ mgmt_ipc_write_rsp(queue_task_t *qtask, mgmt_ipc_err_e err) - log_debug(4, "%s: rsp to fd %d", __FUNCTION__, - qtask->mgmt_ipc_fd); - -- if (qtask->mgmt_ipc_fd < 0) -+ if (qtask->mgmt_ipc_fd < 0) { -+ mgmt_ipc_destroy_queue_task(qtask); - return; -+ } - - qtask->rsp.err = err; - write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp)); - close(qtask->mgmt_ipc_fd); -- free(qtask); -+ mgmt_ipc_destroy_queue_task(qtask); - } - - static int -+mgmt_ipc_read_data(int fd, void *ptr, size_t len) -+{ -+ int n; -+ -+ while (len) { -+ n = read(fd, ptr, len); -+ if (n < 0) { -+ if (errno == EINTR) -+ continue; -+ return -EIO; -+ } -+ if (n == 0) { -+ /* Client closed connection */ -+ return -EIO; -+ } -+ ptr += n; -+ len -= n; -+ } -+ return 0; -+} -+ -+static int -+mgmt_ipc_read_req(queue_task_t *qtask) -+{ -+ iscsiadm_req_t *req = &qtask->req; -+ int rc; -+ -+ rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd, req, sizeof(*req)); -+ if (rc >= 0 && req->payload_len > 0) { -+ /* Limit what we accept */ -+ if (req->payload_len > EXTMSG_MAX) -+ return -EIO; -+ -+ /* Remember the allocated pointer in the -+ * qtask - it will be freed by write_rsp. -+ * Note: we allocate one byte in excess -+ * so we can append a NUL byte. */ -+ qtask->payload = malloc(req->payload_len + 1); -+ rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd, -+ qtask->payload, -+ req->payload_len); -+ } -+ return rc; -+} -+ -+static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = { -+[MGMT_IPC_SESSION_LOGIN] = mgmt_ipc_session_login, -+[MGMT_IPC_SESSION_LOGOUT] = mgmt_ipc_session_logout, -+[MGMT_IPC_SESSION_SYNC] = mgmt_ipc_session_sync, -+[MGMT_IPC_SESSION_STATS] = mgmt_ipc_session_getstats, -+[MGMT_IPC_SEND_TARGETS] = mgmt_ipc_send_targets, -+[MGMT_IPC_SESSION_INFO] = mgmt_ipc_session_info, -+[MGMT_IPC_CONN_ADD] = mgmt_ipc_conn_add, -+[MGMT_IPC_CONN_REMOVE] = mgmt_ipc_conn_remove, -+[MGMT_IPC_CONFIG_INAME] = mgmt_ipc_cfg_initiatorname, -+[MGMT_IPC_CONFIG_IALIAS] = mgmt_ipc_cfg_initiatoralias, -+[MGMT_IPC_CONFIG_FILE] = mgmt_ipc_cfg_filename, -+[MGMT_IPC_IMMEDIATE_STOP] = mgmt_ipc_immediate_stop, -+[MGMT_IPC_ISNS_DEV_ATTR_QUERY] = mgmt_ipc_isns_dev_attr_query, -+[MGMT_IPC_SET_HOST_PARAM] = mgmt_ipc_host_set_param, -+[MGMT_IPC_NOTIFY_ADD_NODE] = mgmt_ipc_notify_add_node, -+[MGMT_IPC_NOTIFY_DEL_NODE] = mgmt_ipc_notify_del_node, -+[MGMT_IPC_NOTIFY_ADD_PORTAL] = mgmt_ipc_notify_add_portal, -+[MGMT_IPC_NOTIFY_DEL_PORTAL] = mgmt_ipc_notify_del_portal, -+}; -+ -+static void - mgmt_ipc_handle(int accept_fd) - { -- struct sockaddr addr; -- int fd, rc = 0, immrsp = 0; -- iscsiadm_req_t req; -- iscsiadm_rsp_t rsp; -+ unsigned int command; -+ int fd, err; - queue_task_t *qtask = NULL; -+ mgmt_ipc_fn_t *handler = NULL; - char user[PEERUSER_MAX]; -- socklen_t len; -- -- memset(&rsp, 0, sizeof(rsp)); -- len = sizeof(addr); -- if ((fd = accept(accept_fd, (struct sockaddr *) &addr, &len)) < 0) { -- if (errno == EINTR) -- rc = -EINTR; -- else -- rc = -EIO; -- return rc; -+ -+ qtask = calloc(1, sizeof(queue_task_t)); -+ if (!qtask) -+ return; -+ -+ if ((fd = accept(accept_fd, NULL, NULL)) < 0) { -+ free(qtask); -+ return; - } - -+ qtask->allocated = 1; -+ qtask->mgmt_ipc_fd = fd; -+ - if (!mgmt_peeruser(fd, user) || strncmp(user, "root", PEERUSER_MAX)) { -- rsp.err = MGMT_IPC_ERR_ACCESS; -- rc = EINVAL; -+ err = MGMT_IPC_ERR_ACCESS; - goto err; - } - -- if (read(fd, &req, sizeof(req)) != sizeof(req)) { -- rc = -EIO; -- close(fd); -- return rc; -- } -- rsp.command = req.command; -- -- qtask = calloc(1, sizeof(queue_task_t)); -- if (!qtask) { -- rsp.err = MGMT_IPC_ERR_NOMEM; -- rc = -ENOMEM; -- goto err; -+ if (mgmt_ipc_read_req(qtask) < 0) { -+ mgmt_ipc_destroy_queue_task(qtask); -+ return; - } -- memcpy(&qtask->req, &req, sizeof(iscsiadm_req_t)); -- qtask->mgmt_ipc_fd = fd; - -- switch(req.command) { -- case MGMT_IPC_SESSION_LOGIN: -- rsp.err = mgmt_ipc_session_login(qtask, &req.u.session.rec); -- break; -- case MGMT_IPC_SESSION_LOGOUT: -- rsp.err = mgmt_ipc_session_logout(qtask, &req.u.session.rec); -- break; -- case MGMT_IPC_SESSION_SYNC: -- rsp.err = mgmt_ipc_session_sync(qtask, &req.u.session.rec, -- req.u.session.sid); -- break; -- case MGMT_IPC_SESSION_STATS: -- rsp.err = mgmt_ipc_session_getstats(qtask, req.u.session.sid, -- &rsp); -- immrsp = 1; -- break; -- case MGMT_IPC_SEND_TARGETS: -- rsp.err = iscsi_host_send_targets(qtask, req.u.st.host_no, -- req.u.st.do_login, -- &req.u.st.ss); -- immrsp = 1; -- break; -- case MGMT_IPC_SESSION_INFO: -- rsp.err = mgmt_ipc_session_info(qtask, req.u.session.sid, -- &rsp); -- immrsp = 1; -- break; -- case MGMT_IPC_CONN_ADD: -- rsp.err = mgmt_ipc_conn_add(qtask, req.u.conn.cid); -- break; -- case MGMT_IPC_CONN_REMOVE: -- rsp.err = mgmt_ipc_conn_remove(qtask, req.u.conn.cid); -- break; -- case MGMT_IPC_CONFIG_INAME: -- rsp.err = mgmt_ipc_cfg_initiatorname(qtask, &rsp); -- immrsp = 1; -- break; -- case MGMT_IPC_CONFIG_IALIAS: -- rsp.err = mgmt_ipc_cfg_initiatoralias(qtask, &rsp); -- immrsp = 1; -- break; -- case MGMT_IPC_CONFIG_FILE: -- rsp.err = mgmt_ipc_cfg_filename(qtask, &rsp); -- immrsp = 1; -- break; -- case MGMT_IPC_IMMEDIATE_STOP: -- rsp.err = MGMT_IPC_OK; -- immrsp = 1; -- rc = 1; -- break; -- case MGMT_IPC_ISNS_DEV_ATTR_QUERY: -- rsp.err = mgmt_ipc_isns_dev_attr_query(qtask); -- break; -- case MGMT_IPC_SET_HOST_PARAM: -- rsp.err = iscsi_host_set_param(req.u.set_host_param.host_no, -- req.u.set_host_param.param, -- req.u.set_host_param.value); -- immrsp = 1; -- break; -- default: -+ command = qtask->req.command; -+ qtask->rsp.command = command; -+ -+ if (0 <= command && command < __MGMT_IPC_MAX_COMMAND) -+ handler = mgmt_ipc_functions[command]; -+ if (handler != NULL) { -+ /* If the handler returns OK, this means it -+ * already sent the reply. */ -+ err = handler(qtask); -+ if (err == MGMT_IPC_OK) -+ return; -+ } else { - log_error("unknown request: %s(%d) %u", -- __FUNCTION__, __LINE__, req.command); -- rsp.err = MGMT_IPC_ERR_INVALID_REQ; -- immrsp = 1; -- break; -+ __FUNCTION__, __LINE__, command); -+ err = MGMT_IPC_ERR_INVALID_REQ; - } - -- if (rsp.err == MGMT_IPC_OK && !immrsp) -- return 0; -- - err: -- if (write(fd, &rsp, sizeof(rsp)) != sizeof(rsp)) -- rc = -EIO; -- close(fd); -- if (qtask) -- free(qtask); -- return rc; -+ /* This will send the response, close the -+ * connection and free the qtask */ -+ mgmt_ipc_write_rsp(qtask, err); - } - - static int reap_count; -@@ -477,7 +633,8 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd, - poll_array[POLL_ISNS].events = POLLIN; - } - -- while (1) { -+ leave_event_loop = 0; -+ while (!leave_event_loop) { - res = poll(poll_array, POLL_MAX, ACTOR_RESOLUTION); - if (res > 0) { - log_debug(6, "poll result %d", res); -@@ -485,8 +642,7 @@ void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd, - ipc->ctldev_handle(); - - if (poll_array[POLL_IPC].revents) -- if (mgmt_ipc_handle(mgmt_ipc_fd) == 1) -- break; -+ mgmt_ipc_handle(mgmt_ipc_fd); - if (poll_array[POLL_ISNS].revents) - isns_handle(isns_fd); - -diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h -index 3fa7521..e2eaae2 100644 ---- a/usr/mgmt_ipc.h -+++ b/usr/mgmt_ipc.h -@@ -65,28 +65,18 @@ typedef enum iscsiadm_cmd { - MGMT_IPC_ISNS_DEV_ATTR_QUERY = 14, - MGMT_IPC_SEND_TARGETS = 15, - MGMT_IPC_SET_HOST_PARAM = 16, --} iscsiadm_cmd_e; -- --typedef enum iscsi_conn_state_e { -- STATE_FREE, -- STATE_XPT_WAIT, -- STATE_IN_LOGIN, -- STATE_LOGGED_IN, -- STATE_IN_LOGOUT, -- STATE_LOGOUT_REQUESTED, -- STATE_CLEANUP_WAIT, --} iscsi_conn_state_e; -+ MGMT_IPC_NOTIFY_ADD_NODE = 17, -+ MGMT_IPC_NOTIFY_DEL_NODE = 18, -+ MGMT_IPC_NOTIFY_ADD_PORTAL = 19, -+ MGMT_IPC_NOTIFY_DEL_PORTAL = 20, - --typedef enum iscsi_session_r_stage_e { -- R_STAGE_NO_CHANGE, -- R_STAGE_SESSION_CLEANUP, -- R_STAGE_SESSION_REOPEN, -- R_STAGE_SESSION_REDIRECT, --} iscsi_session_r_stage_e; -+ __MGMT_IPC_MAX_COMMAND -+} iscsiadm_cmd_e; - - /* IPC Request */ - typedef struct iscsiadm_req { - iscsiadm_cmd_e command; -+ uint32_t payload_len; - - union { - /* messages */ -@@ -133,12 +123,15 @@ typedef struct iscsiadm_rsp { - char var[VALUE_MAXLEN]; - } config; - struct ipc_msg_session_state { -- iscsi_session_r_stage_e session_state; -- iscsi_conn_state_e conn_state; -+ int session_state; -+ int conn_state; - } session_state; - } u; - } iscsiadm_rsp_t; - -+struct queue_task; -+typedef mgmt_ipc_err_e mgmt_ipc_fn_t(struct queue_task *); -+ - struct iscsi_ipc *ipc; - - void need_reap(void); -diff --git a/usr/netlink.c b/usr/netlink.c -index 12cb7f7..f039aff 100644 ---- a/usr/netlink.c -+++ b/usr/netlink.c -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -388,6 +389,27 @@ kdestroy_session(uint64_t transport_handle, uint32_t sid) - } - - static int -+kunbind_session(uint64_t transport_handle, uint32_t sid) -+{ -+ int rc; -+ struct iscsi_uevent ev; -+ -+ log_debug(7, "in %s", __FUNCTION__); -+ -+ memset(&ev, 0, sizeof(struct iscsi_uevent)); -+ -+ ev.type = ISCSI_UEVENT_UNBIND_SESSION; -+ ev.transport_handle = transport_handle; -+ ev.u.d_session.sid = sid; -+ -+ if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) { -+ return rc; -+ } -+ -+ return 0; -+} -+ -+static int - kcreate_conn(uint64_t transport_handle, uint32_t sid, - uint32_t cid, uint32_t *out_cid) - { -@@ -668,8 +690,7 @@ kstart_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid, - } - - static int --krecv_pdu_begin(uint64_t transport_handle, uintptr_t recv_handle, -- uintptr_t *pdu_handle, int *pdu_size) -+krecv_pdu_begin(struct iscsi_conn *conn) - { - log_debug(7, "in %s", __FUNCTION__); - -@@ -677,19 +698,17 @@ krecv_pdu_begin(uint64_t transport_handle, uintptr_t recv_handle, - log_error("recv's begin state machine bug?"); - return -EIO; - } -- recvbuf = (void*)recv_handle + sizeof(struct iscsi_uevent); -+ recvbuf = conn->recv_context->data + sizeof(struct iscsi_uevent); - recvlen = 0; -- *pdu_handle = recv_handle; - - log_debug(3, "recv PDU began, pdu handle 0x%p", -- (void*)*pdu_handle); -+ recvbuf); - - return 0; - } - - static int --krecv_pdu_end(uint64_t transport_handle, uintptr_t conn_handle, -- uintptr_t pdu_handle) -+krecv_pdu_end(struct iscsi_conn *conn) - { - log_debug(7, "in %s", __FUNCTION__); - -@@ -699,9 +718,10 @@ krecv_pdu_end(uint64_t transport_handle, uintptr_t conn_handle, - } - - log_debug(3, "recv PDU finished for pdu handle 0x%p", -- (void*)pdu_handle); -+ recvbuf); - -- recvpool_put((void*)conn_handle, (void*)pdu_handle); -+ iscsi_conn_context_put(conn->recv_context); -+ conn->recv_context = NULL; - recvbuf = NULL; - return 0; - } -@@ -774,7 +794,7 @@ ktransport_ep_disconnect(iscsi_conn_t *conn) - - log_debug(7, "in %s", __FUNCTION__); - -- if (conn->transport_ep_handle < 0) -+ if (conn->transport_ep_handle == -1) - return; - - memset(&ev, 0, sizeof(struct iscsi_uevent)); -@@ -784,8 +804,9 @@ ktransport_ep_disconnect(iscsi_conn_t *conn) - ev.u.ep_disconnect.ep_handle = conn->transport_ep_handle; - - if ((rc = __kipc_call(&ev, sizeof(ev))) < 0) { -- log_error("conn %p session %p transport disconnect failed %d\n", -- conn, conn->session, rc); -+ log_error("connnection %d:%d transport disconnect failed for " -+ "ep %" PRIu64 " with error %d.", conn->session->id, -+ conn->id, conn->transport_ep_handle, rc); - } else - conn->transport_ep_handle = -1; - } -@@ -847,15 +868,14 @@ static void drop_data(struct nlmsghdr *nlh) - - static int ctldev_handle(void) - { -- int rc; -+ int rc, ev_size; - struct iscsi_uevent *ev; -- struct iscsi_transport *t; - iscsi_session_t *session = NULL; - iscsi_conn_t *conn = NULL; -- uintptr_t recv_handle; - char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; - struct nlmsghdr *nlh; -- int ev_size; -+ struct iscsi_conn_context *conn_context; -+ uint32_t sid = 0, cid = 0; - - log_debug(7, "in %s", __FUNCTION__); - -@@ -870,6 +890,8 @@ static int ctldev_handle(void) - log_debug(7, "%s got event type %u\n", __FUNCTION__, ev->type); - /* drivers like qla4xxx can be inserted after iscsid is started */ - switch (ev->type) { -+ case ISCSI_KEVENT_CREATE_SESSION: -+ /* old kernels sent ISCSI_UEVENT_CREATE_SESSION on creation */ - case ISCSI_UEVENT_CREATE_SESSION: - drop_data(nlh); - iscsi_async_session_creation(ev->r.c_session_ret.host_no, -@@ -880,76 +902,82 @@ static int ctldev_handle(void) - iscsi_async_session_destruction(ev->r.d_session.host_no, - ev->r.d_session.sid); - return 0; -+ case ISCSI_KEVENT_RECV_PDU: -+ sid = ev->r.recv_req.sid; -+ cid = ev->r.recv_req.cid; -+ break; -+ case ISCSI_KEVENT_CONN_ERROR: -+ sid = ev->r.connerror.sid; -+ cid = ev->r.connerror.cid; -+ case ISCSI_KEVENT_UNBIND_SESSION: -+ sid = ev->r.unbind_session.sid; -+ /* session wide event so cid is 0 */ -+ cid = 0; -+ break; - default: - ; /* fall through */ - } - -- /* verify connection */ -- list_for_each_entry(t, &transports, list) { -- list_for_each_entry(session, &t->sessions, list) { -- int i; -- -- for (i=0; itype == ISCSI_KEVENT_RECV_PDU && -- session->id == ev->r.recv_req.sid && -- session->conn[i].id == ev->r.recv_req.cid) { -- conn = &session->conn[i]; -- goto verify_conn; -- } -- if (ev->type == ISCSI_KEVENT_CONN_ERROR && -- session->id == ev->r.connerror.sid && -- session->conn[i].id == ev->r.connerror.cid) { -- conn = &session->conn[i]; -- goto verify_conn; -- } -- } -+ /* Handle transport error which is not connection related */ -+ if (ev->type == ISCSI_KEVENT_TRANS_ERROR) { -+ rc = free_transport_by_handle(ev->transport_handle); -+ if (rc) { -+ log_error("Could not release transport 0x%lx\n", ev->transport_handle); - } -+ drop_data(nlh); -+ return rc; - } - --verify_conn: -- if (conn == NULL) { -+ /* verify connection */ -+ session = session_find_by_sid(sid); -+ if (!session) { - log_error("Could not verify connection %d:%d. Dropping " -- "event.\n", ev->r.recv_req.sid, ev->r.recv_req.cid); -+ "event.\n", sid, cid); - drop_data(nlh); - return -ENXIO; - } -+ conn = &session->conn[0]; - - ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); -- recv_handle = (uintptr_t)recvpool_get(conn, ev_size); -- if (!recv_handle) { -+ conn_context = iscsi_conn_context_get(conn, ev_size); -+ if (!conn_context) { - /* retry later */ -- log_error("Can not allocate memory for receive handle."); -+ log_error("Can not allocate memory for receive context."); - return -ENOMEM; - } - - log_debug(6, "message real length is %d bytes, recv_handle %p", -- nlh->nlmsg_len, (void*)recv_handle); -+ nlh->nlmsg_len, conn_context->data); - -- if ((rc = nlpayload_read(ctrl_fd, (void*)recv_handle, -+ if ((rc = nlpayload_read(ctrl_fd, conn_context->data, - ev_size, 0)) < 0) { -- recvpool_put(conn, (void*)recv_handle); -+ iscsi_conn_context_put(conn_context); - log_error("can not read from NL socket, error %d", rc); - /* retry later */ - return rc; - } - -+ /* -+ * we sched these events because the handlers could call back -+ * into ctldev_handle -+ */ - switch (ev->type) { - case ISCSI_KEVENT_RECV_PDU: -- /* produce an event, so session manager will handle */ -- queue_produce(session->queue, EV_CONN_RECV_PDU, conn, -- sizeof(uintptr_t), &recv_handle); -- actor_schedule(&session->mainloop); -+ iscsi_sched_conn_context(conn_context, conn, 0, -+ EV_CONN_RECV_PDU); - break; - case ISCSI_KEVENT_CONN_ERROR: -- /* produce an event, so session manager will handle */ -- memcpy((void *)recv_handle, &ev->r.connerror.error, -+ memcpy(conn_context->data, &ev->r.connerror.error, - sizeof(ev->r.connerror.error)); -- queue_produce(session->queue, EV_CONN_ERROR, conn, -- sizeof(uintptr_t), &recv_handle); -- actor_schedule(&session->mainloop); -+ iscsi_sched_conn_context(conn_context, conn, 0, -+ EV_CONN_ERROR); -+ break; -+ case ISCSI_KEVENT_UNBIND_SESSION: -+ iscsi_sched_conn_context(conn_context, conn, 0, -+ EV_CONN_STOP); - break; - default: -- recvpool_put(conn, (void*)recv_handle); -+ iscsi_conn_context_put(conn_context); - log_error("unknown kernel event %d", ev->type); - return -EEXIST; - } -@@ -1028,12 +1056,12 @@ ctldev_close(void) - { - log_debug(7, "in %s", __FUNCTION__); - -+ if (ctrl_fd >= 0) -+ close(ctrl_fd); - free(setparam_buf); - free(pdu_sendbuf); - free(nlm_recvbuf); - free(nlm_sendbuf); -- if (ctrl_fd >= 0) -- close(ctrl_fd); - } - - struct iscsi_ipc nl_ipc = { -@@ -1045,6 +1073,7 @@ struct iscsi_ipc nl_ipc = { - .sendtargets = ksendtargets, - .create_session = kcreate_session, - .destroy_session = kdestroy_session, -+ .unbind_session = kunbind_session, - .create_conn = kcreate_conn, - .destroy_conn = kdestroy_conn, - .bind_conn = kbind_conn, -diff --git a/usr/queue.c b/usr/queue.c -deleted file mode 100644 -index 323bc6f..0000000 ---- a/usr/queue.c -+++ /dev/null -@@ -1,251 +0,0 @@ --/* -- * iSCSI event queue -- * -- * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman -- * maintained by open-iscsi@googlegroups.com -- * -- * 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. -- * -- * See the file COPYING included with this distribution for more details. -- */ -- --#include --#include --#include --#include "queue.h" --#include "log.h" --#include "actor.h" -- --queue_t* --queue_create(int pages_initial, int pages_max, queued_f queued, -- void *queued_data) --{ -- queue_t *queue; -- -- if ((queue = malloc(sizeof(queue_t))) == NULL) { -- log_error("out of memory when allocating queue_t"); -- return NULL; -- } -- -- queue->queued_func = queued; -- queue->queued_data = queued_data; -- queue->pages_current = pages_initial; -- queue->start_ptr = malloc(queue->pages_current * QUEUE_BUF_SIZE); -- if (queue->start_ptr == NULL) { -- log_error("out of memory when allocating queue's pages"); -- free(queue); -- return NULL; -- } -- memset(queue->start_ptr, 0, queue->pages_current * QUEUE_BUF_SIZE); -- queue->head_ptr = queue->tail_ptr = queue->start_ptr; -- queue->end_ptr = (char *)queue->start_ptr + -- queue->pages_current * QUEUE_BUF_SIZE; -- queue->pages_initial = pages_initial; -- queue->pages_max = pages_max; -- queue->list_head.q_forw = &queue->list_head; -- queue->list_head.q_back = &queue->list_head; -- queue->count = 0; -- -- return queue; --} -- --void --queue_destroy(queue_t* queue) --{ -- if (queue->list_head.q_forw != &queue->list_head) { -- log_error("destroying non-empty queue 0x%p", queue); -- } -- free(queue->start_ptr); -- free(queue); --} -- --static queue_status_e --__io_queue_grow(queue_t *queue) --{ -- void *newbuf, *oldbuf; -- struct qelem *item; -- queue_item_t *elem; -- -- log_debug(7, "queue 0x%p:%d is growing", queue, queue->pages_current); -- -- newbuf = malloc((queue->pages_current + 1) * QUEUE_BUF_SIZE); -- if (newbuf == NULL) { -- return QUEUE_OUT_OF_MEMORY; -- } -- memcpy(newbuf, queue->start_ptr, queue->pages_current * QUEUE_BUF_SIZE); -- oldbuf = queue->start_ptr; -- -- /* adjust queue sizes */ -- queue->start_ptr = newbuf; -- queue->end_ptr = (char *)newbuf + -- (queue->pages_current + 1) * QUEUE_BUF_SIZE; -- queue->tail_ptr = (char *)newbuf + ((char *)queue->tail_ptr - -- (char *)oldbuf); -- queue->head_ptr = (char *)newbuf + ((char *)queue->head_ptr - -- (char *)oldbuf); -- queue->list_head.q_forw = (struct qelem *) (void *)((char *)newbuf + -- ((char *)queue->list_head.q_forw - (char *)oldbuf)); -- queue->list_head.q_back = (struct qelem *) (void *)((char *)newbuf + -- ((char *)queue->list_head.q_back - (char *)oldbuf)); -- /* adjust queue list */ -- for (item = queue->list_head.q_forw; -- item != queue->list_head.q_forw; item = item->q_forw) { -- elem = (queue_item_t *)item; -- if (elem->item.q_forw != &queue->list_head) { -- elem->item.q_forw = -- (struct qelem *)(void *)((char *)newbuf + -- ((char *)elem->item.q_forw - (char *)oldbuf)); -- } -- if (elem->item.q_back != &queue->list_head) { -- elem->item.q_back = -- (struct qelem *) (void *)((char *)newbuf + -- ((char *)elem->item.q_back - (char *)oldbuf)); -- } -- } -- free(oldbuf); -- queue->pages_current++; -- -- return QUEUE_OK; --} -- --queue_status_e --queue_consume(queue_t *queue, int data_max_size, queue_item_t *item) --{ -- int real_size; -- queue_item_t *elem; -- -- if (queue->list_head.q_forw == &queue->list_head) { -- if (queue->count) -- log_error("queue integrety lost! Bug?"); -- return QUEUE_IS_EMPTY; -- } -- elem = (queue_item_t *)queue->list_head.q_forw; -- if (elem->data_size > data_max_size) { -- return QUEUE_NOT_ENOUGH_SPACE; -- } -- remque(&elem->item); -- real_size = elem->data_size + sizeof(queue_item_t); -- if (queue->head_ptr == elem) { -- queue->head_ptr = (char *)queue->head_ptr + real_size; -- log_debug(7, -- "event_type: %d removing from the head: " -- "0x%p:0x%p:0x%p:0x%p elem 0x%p length %d", -- elem->event_type, -- queue->start_ptr, -- queue->head_ptr, -- queue->tail_ptr, -- queue->end_ptr, -- elem, -- real_size); -- } else if ((char *)queue->tail_ptr - real_size == (char*)elem) { -- queue->tail_ptr = (char *)queue->tail_ptr - real_size; -- log_debug(7, -- "event_type: %d removing from the tail: " -- "0x%p:0x%p:0x%p:0x%p elem 0x%p length %d", -- elem->event_type, -- queue->start_ptr, -- queue->head_ptr, -- queue->tail_ptr, -- queue->end_ptr, -- elem, -- real_size); -- } else { -- log_debug(7, -- "event_type: %d removing from the list: " -- "0x%p:0x%p:0x%p:0x%p elem 0x%p length %d", -- elem->event_type, -- queue->start_ptr, -- queue->head_ptr, -- queue->tail_ptr, -- queue->end_ptr, -- elem, -- real_size); -- } -- memcpy(item, elem, sizeof(queue_item_t)); -- memcpy(queue_item_data(item), queue_item_data(elem), elem->data_size); -- -- if (queue->list_head.q_forw == &queue->list_head) { -- /* reset buffer pointers just to be clean */ -- queue->head_ptr = queue->tail_ptr = queue->start_ptr; -- } -- -- queue->count--; -- -- return QUEUE_OK; --} -- --void* --queue_item_data (queue_item_t *item) --{ -- return (char *)item + sizeof(queue_item_t); --} -- --queue_status_e --queue_produce(queue_t *queue, int event_type, void *context, -- const int data_size, void *data) --{ -- int real_size = data_size + sizeof(queue_item_t); -- queue_item_t *elem; -- --try_again: -- if ((char *)queue->tail_ptr + real_size <= (char *)queue->end_ptr) { -- elem = queue->tail_ptr; -- queue->tail_ptr = (void *)((char *)queue->tail_ptr + real_size); -- log_debug(7, "event_type: %d adding to the tail: " -- "0x%p:0x%p:0x%p:0x%p elem 0x%p length %d", -- event_type, -- queue->start_ptr, -- queue->head_ptr, -- queue->tail_ptr, -- queue->end_ptr, -- elem, -- real_size); -- } else if ((char *)queue->head_ptr - real_size >= -- (char *)queue->start_ptr) { -- elem = (void *)((char *)queue->head_ptr - real_size); -- queue->head_ptr = elem; -- log_debug(7, "event_type: %d adding to the head: " -- "0x%p:0x%p:0x%p:0x%p length %d", -- event_type, -- queue->start_ptr, -- queue->head_ptr, -- queue->tail_ptr, -- queue->end_ptr, -- real_size); -- } else { -- queue_status_e status; -- -- if (queue->pages_current >= queue->pages_max) { -- return QUEUE_IS_FULL; -- } -- -- /* grow */ -- status = __io_queue_grow(queue); -- if (status != QUEUE_OK) { -- return status; -- } -- -- goto try_again; -- } -- elem->data_size = data_size; -- elem->event_type = event_type; -- elem->context = context; -- memcpy(queue_item_data(elem), data, data_size); -- insque(&elem->item, queue->list_head.q_back); -- -- if (queue->queued_func) -- queue->queued_func(queue->queued_data, event_type); -- -- queue->count++; -- -- return QUEUE_OK; --} -- -diff --git a/usr/queue.h b/usr/queue.h -deleted file mode 100644 -index 6abe613..0000000 ---- a/usr/queue.h -+++ /dev/null -@@ -1,68 +0,0 @@ --/* -- * iSCSI event queue -- * -- * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman -- * maintained by open-iscsi@googlegroups.com -- * -- * 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. -- * -- * See the file COPYING included with this distribution for more details. -- */ -- --#ifndef QUEUE_H --#define QUEUE_H -- --#include "types.h" -- --#define QUEUE_BUF_SIZE 4096 --#define EVENT_PAYLOAD_MAX (DATASEG_MAX+HDRSEG_MAX) -- --typedef enum queue_status_e { -- QUEUE_OK = 0, -- QUEUE_IS_FULL = 1, -- QUEUE_IS_EMPTY = 2, -- QUEUE_OUT_OF_MEMORY = 3, -- QUEUE_NOT_ENOUGH_SPACE = 4 --} queue_status_e; -- --typedef struct queue_item_t { -- struct qelem item; -- int event_type; -- int data_size; -- void *context; --} queue_item_t; -- --typedef void (*queued_f) (void *data, int event_type); -- --typedef struct queue_t { -- void *start_ptr; -- void *end_ptr; -- void *head_ptr; -- void *tail_ptr; -- unsigned int pages_initial; -- unsigned int pages_max; -- unsigned int pages_current; -- struct qelem list_head; -- queued_f queued_func; -- void *queued_data; -- int count; --} queue_t; -- --extern queue_t* queue_create(int pages_initial, int pages_max, -- queued_f queued_func, void *queued_data); --extern void queue_destroy(queue_t *queue); --extern void* queue_item_data(queue_item_t *item); --extern queue_status_e queue_produce(queue_t* queue, int event_type, -- void *context, const int data_size, void *data); --extern queue_status_e queue_consume(queue_t *queue, int data_max_size, -- queue_item_t *item); -- --#endif /* QUEUE_H */ -diff --git a/usr/scsi.c b/usr/scsi.c -new file mode 100644 -index 0000000..14767e2 ---- /dev/null -+++ b/usr/scsi.c -@@ -0,0 +1,83 @@ -+/* -+ * The following is from the linux kernel scsi_error.c -+ * -+ * scsi_error.c Copyright (C) 1997 Eric Youngdale -+ * -+ * SCSI error/timeout handling -+ * Initial versions: Eric Youngdale. Based upon conversations with -+ * Leonard Zubkoff and David Miller at Linux Expo, -+ * ideas originating from all over the place. -+ * -+ * Restructured scsi_unjam_host and associated functions. -+ * September 04, 2002 Mike Anderson (andmike@us.ibm.com) -+ * -+ * Forward port of Russell King's (rmk@arm.linux.org.uk) changes and -+ * minor cleanups. -+ * September 30, 2002 Mike Anderson (andmike@us.ibm.com) -+ */ -+ -+#include -+#include "scsi.h" -+ -+/** -+ * scsi_normalize_sense - normalize main elements from either fixed or -+ * descriptor sense data format into a common format. -+ * -+ * @sense_buffer: byte array containing sense data returned by device -+ * @sb_len: number of valid bytes in sense_buffer -+ * @sshdr: pointer to instance of structure that common -+ * elements are written to. -+ * -+ * Notes: -+ * The "main elements" from sense data are: response_code, sense_key, -+ * asc, ascq and additional_length (only for descriptor format). -+ * -+ * Typically this function can be called after a device has -+ * responded to a SCSI command with the CHECK_CONDITION status. -+ * -+ * Return value: -+ * 1 if valid sense data information found, else 0; -+ **/ -+int scsi_normalize_sense(const uint8_t *sense_buffer, int sb_len, -+ struct scsi_sense_hdr *sshdr) -+{ -+ if (!sense_buffer || !sb_len) -+ return 0; -+ -+ memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); -+ -+ sshdr->response_code = (sense_buffer[0] & 0x7f); -+ -+ if (!scsi_sense_valid(sshdr)) -+ return 0; -+ -+ if (sshdr->response_code >= 0x72) { -+ /* -+ * descriptor format -+ */ -+ if (sb_len > 1) -+ sshdr->sense_key = (sense_buffer[1] & 0xf); -+ if (sb_len > 2) -+ sshdr->asc = sense_buffer[2]; -+ if (sb_len > 3) -+ sshdr->ascq = sense_buffer[3]; -+ if (sb_len > 7) -+ sshdr->additional_length = sense_buffer[7]; -+ } else { -+ /* -+ * fixed format -+ */ -+ if (sb_len > 2) -+ sshdr->sense_key = (sense_buffer[2] & 0xf); -+ if (sb_len > 7) { -+ sb_len = (sb_len < (sense_buffer[7] + 8)) ? -+ sb_len : (sense_buffer[7] + 8); -+ if (sb_len > 12) -+ sshdr->asc = sense_buffer[12]; -+ if (sb_len > 13) -+ sshdr->ascq = sense_buffer[13]; -+ } -+ } -+ -+ return 1; -+} -diff --git a/usr/scsi.h b/usr/scsi.h -new file mode 100644 -index 0000000..d8ef951 ---- /dev/null -+++ b/usr/scsi.h -@@ -0,0 +1,40 @@ -+/* -+ * this is from the linux kernel scsi_eh.h -+ */ -+#ifndef _SCSI_SCSI_H -+#define _SCSI_SCSI_H -+ -+#include -+ -+/* -+ * This is a slightly modified SCSI sense "descriptor" format header. -+ * The addition is to allow the 0x70 and 0x71 response codes. The idea -+ * is to place the salient data from either "fixed" or "descriptor" sense -+ * format into one structure to ease application processing. -+ * -+ * The original sense buffer should be kept around for those cases -+ * in which more information is required (e.g. the LBA of a MEDIUM ERROR). -+ */ -+struct scsi_sense_hdr { /* See SPC-3 section 4.5 */ -+ uint8_t response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */ -+ uint8_t sense_key; -+ uint8_t asc; -+ uint8_t ascq; -+ uint8_t byte4; -+ uint8_t byte5; -+ uint8_t byte6; -+ uint8_t additional_length; /* always 0 for fixed sense format */ -+}; -+ -+static inline int scsi_sense_valid(struct scsi_sense_hdr *sshdr) -+{ -+ if (!sshdr) -+ return 0; -+ -+ return (sshdr->response_code & 0x70) == 0x70; -+} -+ -+extern int scsi_normalize_sense(const uint8_t *sense_buffer, int sb_len, -+ struct scsi_sense_hdr *sshdr); -+ -+#endif -diff --git a/usr/strings.c b/usr/strings.c -index e885685..aee1de3 100644 ---- a/usr/strings.c -+++ b/usr/strings.c -@@ -74,14 +74,26 @@ free_string_buffer(struct string_buffer *s) - void - enlarge_data(struct string_buffer *s, int length) - { -+ void *new_buf; -+ - if (s) { - s->data_length += length; -- if (s->data_length >= s->allocated_length) { -- /* too big */ -- log_error("enlarged buffer %p to %d data bytes, " -- "with only %d bytes of buffer space", -- s, (int)s->data_length, -- (int)s->allocated_length); -+ if (s->data_length > s->allocated_length) { -+ log_debug(7, "enlarge buffer from %lu to %lu\n", -+ s->allocated_length, s->data_length); -+ new_buf = realloc(s->buffer, s->data_length); -+ if (!new_buf) { -+ /* too big */ -+ log_error("enlarged buffer %p to %d data " -+ "bytes, with only %d bytes of buffer " -+ "space", s, (int)s->data_length, -+ (int)s->allocated_length); -+ exit(1); -+ } -+ s->buffer = new_buf; -+ memset(s->buffer + s->allocated_length, 0, -+ s->data_length - s->allocated_length); -+ s->allocated_length = s->data_length; - } - } - } -@@ -99,29 +111,13 @@ remove_initial(struct string_buffer *s, int length) - } - } - --static int --realloc_buffer(struct string_buffer *s, size_t min_length) --{ -- size_t length = MAX(min_length + 1, s->allocated_length + 1024); -- char *buf = realloc(s->buffer, length); -- -- if (buf) { -- s->buffer = buf; -- s->buffer[length - 1] = '\0'; -- s->allocated_length = length; -- return 1; -- } else { -- log_error( -- "failed to allocate more space for string buffer %p", s); -- return 0; -- } --} -- - /* truncate the data length down */ - void - truncate_buffer(struct string_buffer *s, size_t length) - { - if (s) { -+ if (!s->data_length) -+ return; - if (length <= s->data_length) { - s->data_length = length; - s->buffer[s->data_length] = '\0'; -@@ -141,100 +137,6 @@ truncate_buffer(struct string_buffer *s, size_t length) - } - } - --/* append a string onto the buffer */ --int --append_string(struct string_buffer *s, const char *str) --{ -- size_t length = strlen(str); -- size_t needed = s->data_length + length + 1; /* existing + new + -- * trailing NUL -- */ -- -- if (needed >= s->allocated_length) { -- /* need more space */ -- if (!realloc_buffer(s, needed)) -- return 0; -- } -- -- strcpy(s->buffer + s->data_length, str); -- s->data_length += length; -- return 1; --} -- --int --append_sprintf(struct string_buffer *s, const char *format, ...) --{ -- va_list args; -- size_t appended; -- size_t available = s->allocated_length - s->data_length - 1; -- int ret = 0; -- -- va_start(args, format); -- -- for (;;) { -- appended = vsnprintf(s->buffer + s->data_length, available, -- format, args); -- -- if (appended < 0) { -- /* error, need more space, but don't know how much */ -- if (!realloc_buffer(s, s->data_length + 1024)) -- goto done; -- } else if (appended >= available) { -- /* what would have been output overflows the buffer, -- * need more space -- */ -- if (!realloc_buffer(s, s->data_length + appended)) -- goto done; -- } else { -- /* it fit */ -- s->data_length += appended; -- ret = 1; -- break; -- } -- } -- -- done: -- va_end(args); -- -- return ret; --} -- --/* append a string after the NUL at the end of any current data. This -- * maintains NUL termination of all strings -- */ --int --adjoin_string(struct string_buffer *s, const char *str) --{ -- size_t length = strlen(str) + 1; -- size_t needed; -- -- if (s->buffer[s->data_length - 1] == '\0') -- needed = s->data_length + length; /* lengths include NULs -- */ -- else -- needed = s->data_length + 1 + length; /* existing + NUL + -- * new + NUL -- */ -- if (needed >= s->allocated_length) { -- /* need more space */ -- if (!realloc_buffer(s, needed)) -- return 0; -- } -- -- if (s->buffer[s->data_length - 1] == '\0') { -- memcpy(s->buffer + s->data_length, str, length); -- /* lengths already include NULs -- */ -- s->data_length += length; /* new string + NUL */ -- } else { -- memcpy(s->buffer + s->data_length + 1, str, length); -- /* NUL + new + NUL */ -- s->data_length += 1 + length; /* NUL + new string + NUL */ -- } -- -- return 1; --} -- - char * - buffer_data(struct string_buffer *s) - { -@@ -261,27 +163,3 @@ unused_length(struct string_buffer * s) - else - return 0; - } -- --/* write the entire buffer to the fd, or exit */ --void --write_buffer(struct string_buffer *s, int fd) --{ -- const char *data = buffer_data(s); -- const char *end = data + data_length(s); -- int result; -- -- /* write the target info to the pipe */ -- log_debug(7, "writing to pipe %d, data %p, size %d, text '%s'", -- fd, data, (int)(end - data), data); -- while (data < end) { -- result = write(fd, data, end - data); -- if (result < 0) { -- if (errno != EINTR) { -- log_error("can't write to pipe %d", fd); -- exit(1); -- } -- } else { -- data += result; -- } -- } --} -diff --git a/usr/strings.h b/usr/strings.h -index a209a44..0e9c2ac 100644 ---- a/usr/strings.h -+++ b/usr/strings.h -@@ -35,12 +35,8 @@ extern void free_string_buffer(struct string_buffer *s); - extern void enlarge_data(struct string_buffer *s, int length); - extern void remove_initial(struct string_buffer *s, int length); - extern void truncate_buffer(struct string_buffer *s, size_t length); --extern int append_string(struct string_buffer *s, const char *str); --extern int append_sprintf(struct string_buffer *s, const char *format, ...); --extern int adjoin_string(struct string_buffer *s, const char *str); - extern char *buffer_data(struct string_buffer *s); - extern size_t data_length(struct string_buffer *s); - extern size_t unused_length(struct string_buffer *s); --extern void write_buffer(struct string_buffer *s, int fd); - - #endif /* STRINGS_H */ -diff --git a/usr/transport.c b/usr/transport.c -index fd47b8f..8fcc8c7 100644 ---- a/usr/transport.c -+++ b/usr/transport.c -@@ -70,5 +70,5 @@ int set_transport_template(struct iscsi_transport *t) - } - - log_error("Could not fund uspace transport for %s\n", t->name); -- return -ENOSYS; -+ return ENOSYS; - } -diff --git a/usr/transport.h b/usr/transport.h -index 2df8d8b..402747f 100644 ---- a/usr/transport.h -+++ b/usr/transport.h -@@ -26,9 +26,9 @@ struct iscsi_conn; - struct iscsi_transport_template { - const char *name; - uint8_t rdma; -- int (*ep_connect) (iscsi_conn_t *conn, int non_blocking); -- int (*ep_poll) (iscsi_conn_t *conn, int timeout_ms); -- void (*ep_disconnect) (iscsi_conn_t *conn); -+ int (*ep_connect) (struct iscsi_conn *conn, int non_blocking); -+ int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms); -+ void (*ep_disconnect) (struct iscsi_conn *conn); - }; - - /* represents data path provider */ -diff --git a/usr/types.h b/usr/types.h -index 63dd5cc..77e3f97 100644 ---- a/usr/types.h -+++ b/usr/types.h -@@ -10,10 +10,6 @@ - #include - #include - #include --#include -- --#define DATASEG_MAX 8192 --#define HDRSEG_MAX 48+4 - - /* - * using the __be types allows stricter static -diff --git a/usr/util.c b/usr/util.c -index ce8333b..f3b9afd 100644 ---- a/usr/util.c -+++ b/usr/util.c -@@ -2,15 +2,11 @@ - #include - #include - #include --#include - #include - #include --#include --#include --#include - #include --#include --#include -+#include -+#include - - #include "log.h" - #include "actor.h" -@@ -24,6 +20,7 @@ - #include "iscsi_proto.h" - #include "transport.h" - #include "idbm.h" -+#include "iface.h" - - void daemon_init(void) - { -@@ -92,6 +89,35 @@ str_to_ipport(char *str, int *port, int *tpgt) - return ip; - } - -+#define ISCSI_MAX_FILES 16384 -+ -+int increase_max_files(void) -+{ -+ struct rlimit rl; -+ int err; -+ -+ err = getrlimit(RLIMIT_NOFILE, &rl); -+ if (err) { -+ log_debug(1, "Could not get file limit (err %d)\n", errno); -+ return errno; -+ } -+ log_debug(1, "Max file limits %lu %lu\n", rl.rlim_cur, rl.rlim_max); -+ -+ if (rl.rlim_cur < ISCSI_MAX_FILES) -+ rl.rlim_cur = ISCSI_MAX_FILES; -+ if (rl.rlim_max < ISCSI_MAX_FILES) -+ rl.rlim_max = ISCSI_MAX_FILES; -+ -+ err = setrlimit(RLIMIT_NOFILE, &rl); -+ if (err) { -+ log_debug(1, "Could not set file limit to %lu/%lu (err %d)\n", -+ rl.rlim_cur, rl.rlim_max, errno); -+ return errno; -+ } -+ -+ return 0; -+} -+ - #define MAXSLEEP 128 - - static mgmt_ipc_err_e iscsid_connect(int *fd) -@@ -117,6 +143,11 @@ static mgmt_ipc_err_e iscsid_connect(int *fd) - /* Connection established */ - return MGMT_IPC_OK; - -+ /* If iscsid isn't there, there's no sense -+ * in retrying. */ -+ if (errno == ECONNREFUSED) -+ break; -+ - /* - * Delay before trying again - */ -@@ -127,27 +158,38 @@ static mgmt_ipc_err_e iscsid_connect(int *fd) - return MGMT_IPC_ERR_ISCSID_COMM_ERR; - } - --static mgmt_ipc_err_e iscsid_request(int fd, iscsiadm_req_t *req) -+mgmt_ipc_err_e iscsid_request(int *fd, iscsiadm_req_t *req) - { - int err; - -- if ((err = write(fd, req, sizeof(*req))) != sizeof(*req)) { -+ err = iscsid_connect(fd); -+ if (err) -+ return err; -+ -+ if ((err = write(*fd, req, sizeof(*req))) != sizeof(*req)) { - log_error("got write error (%d/%d) on cmd %d, daemon died?", - err, errno, req->command); -+ close(*fd); - return MGMT_IPC_ERR_ISCSID_COMM_ERR; - } - return MGMT_IPC_OK; - } - --static mgmt_ipc_err_e iscsid_response(int fd, iscsiadm_rsp_t *rsp) -+mgmt_ipc_err_e iscsid_response(int fd, iscsiadm_cmd_e cmd, iscsiadm_rsp_t *rsp) - { -+ mgmt_ipc_err_e iscsi_err; - int err; - - if ((err = recv(fd, rsp, sizeof(*rsp), MSG_WAITALL)) != sizeof(*rsp)) { - log_error("got read error (%d/%d), daemon died?", err, errno); -- return MGMT_IPC_ERR_ISCSID_COMM_ERR; -+ iscsi_err = MGMT_IPC_ERR_ISCSID_COMM_ERR; - } else -- return rsp->err; -+ iscsi_err = rsp->err; -+ close(fd); -+ -+ if (!iscsi_err && cmd != rsp->command) -+ iscsi_err = MGMT_IPC_ERR_ISCSID_COMM_ERR; -+ return iscsi_err; - } - - mgmt_ipc_err_e do_iscsid(iscsiadm_req_t *req, iscsiadm_rsp_t *rsp) -@@ -155,24 +197,61 @@ mgmt_ipc_err_e do_iscsid(iscsiadm_req_t *req, iscsiadm_rsp_t *rsp) - int fd; - mgmt_ipc_err_e err; - -- err = iscsid_connect(&fd); -+ err = iscsid_request(&fd, req); - if (err) -- goto out; -+ return err; -+ -+ return iscsid_response(fd, req->command, rsp); -+} -+ -+int iscsid_req_wait(iscsiadm_cmd_e cmd, int fd) -+{ -+ iscsiadm_rsp_t rsp; - -- err = iscsid_request(fd, req); -+ memset(&rsp, 0, sizeof(iscsiadm_rsp_t)); -+ return iscsid_response(fd, cmd, &rsp); -+} -+ -+int iscsid_req_by_rec_async(iscsiadm_cmd_e cmd, node_rec_t *rec, int *fd) -+{ -+ iscsiadm_req_t req; -+ -+ memset(&req, 0, sizeof(iscsiadm_req_t)); -+ req.command = cmd; -+ memcpy(&req.u.session.rec, rec, sizeof(node_rec_t)); -+ -+ return iscsid_request(fd, &req); -+} -+ -+int iscsid_req_by_rec(iscsiadm_cmd_e cmd, node_rec_t *rec) -+{ -+ int err, fd; -+ -+ err = iscsid_req_by_rec_async(cmd, rec, &fd); - if (err) -- goto out; -+ return err; -+ return iscsid_req_wait(cmd, fd); -+} - -- err = iscsid_response(fd, rsp); -+int iscsid_req_by_sid_async(iscsiadm_cmd_e cmd, int sid, int *fd) -+{ -+ iscsiadm_req_t req; -+ -+ memset(&req, 0, sizeof(iscsiadm_req_t)); -+ req.command = cmd; -+ req.u.session.sid = sid; -+ -+ return iscsid_request(fd, &req); -+} -+ -+int iscsid_req_by_sid(iscsiadm_cmd_e cmd, int sid) -+{ -+ int err, fd; -+ -+ err = iscsid_req_by_sid_async(cmd, sid, &fd); - if (err) -- goto out; -- -- if (!err && req->command != rsp->command) -- err = MGMT_IPC_ERR_ISCSID_COMM_ERR; --out: -- if (fd >= 0) -- close(fd); -- return err; -+ return err; -+ return iscsid_req_wait(cmd, fd); - } - - void idbm_node_setup_defaults(node_rec_t *rec) -@@ -191,43 +270,42 @@ void idbm_node_setup_defaults(node_rec_t *rec) - rec->session.auth.authmethod = 0; - rec->session.auth.password_length = 0; - rec->session.auth.password_in_length = 0; -- rec->session.err_timeo.abort_timeout = 10; -- rec->session.err_timeo.reset_timeout = 30; -+ rec->session.err_timeo.abort_timeout = DEF_ABORT_TIMEO; -+ rec->session.err_timeo.lu_reset_timeout = DEF_LU_RESET_TIMEO; -+ rec->session.err_timeo.host_reset_timeout = DEF_HOST_RESET_TIMEO; - rec->session.timeo.replacement_timeout = DEF_REPLACEMENT_TIMEO; - rec->session.iscsi.InitialR2T = 0; - rec->session.iscsi.ImmediateData = 1; - rec->session.iscsi.FirstBurstLength = DEF_INI_FIRST_BURST_LEN; - rec->session.iscsi.MaxBurstLength = DEF_INI_MAX_BURST_LEN; -- rec->session.iscsi.DefaultTime2Wait = 0; -+ rec->session.iscsi.DefaultTime2Wait = ISCSI_DEF_TIME2WAIT; - rec->session.iscsi.DefaultTime2Retain = 0; - rec->session.iscsi.MaxConnections = 1; - rec->session.iscsi.MaxOutstandingR2T = 1; - rec->session.iscsi.ERL = 0; -+ rec->session.iscsi.FastAbort = 1; - - for (i=0; iconn[i].startup = 0; -+ rec->conn[i].startup = ISCSI_STARTUP_MANUAL; - rec->conn[i].port = ISCSI_LISTEN_PORT; - rec->conn[i].tcp.window_size = TCP_WINDOW_SIZE; - rec->conn[i].tcp.type_of_service = 0; - rec->conn[i].timeo.login_timeout= DEF_LOGIN_TIMEO; - rec->conn[i].timeo.logout_timeout= DEF_LOGOUT_TIMEO; - rec->conn[i].timeo.auth_timeout = 45; -- rec->conn[i].timeo.active_timeout=5; -- rec->conn[i].timeo.idle_timeout = 60; -- rec->conn[i].timeo.ping_timeout = 5; - - rec->conn[i].timeo.noop_out_interval = DEF_NOOP_OUT_INTERVAL; - rec->conn[i].timeo.noop_out_timeout = DEF_NOOP_OUT_TIMEO; - - rec->conn[i].iscsi.MaxRecvDataSegmentLength = - DEF_INI_MAX_RECV_SEG_LEN; -- rec->conn[i].iscsi.HeaderDigest = CONFIG_DIGEST_PREFER_OFF; -+ rec->conn[i].iscsi.HeaderDigest = CONFIG_DIGEST_NEVER; - rec->conn[i].iscsi.DataDigest = CONFIG_DIGEST_NEVER; - rec->conn[i].iscsi.IFMarker = 0; - rec->conn[i].iscsi.OFMarker = 0; - } - -- iface_init(&rec->iface); -+ iface_setup_defaults(&rec->iface); - } - - void iscsid_handle_error(mgmt_ipc_err_e err) -@@ -286,11 +364,7 @@ int __iscsi_match_session(node_rec_t *rec, char *targetname, - if (rec->conn[0].port != -1 && port != rec->conn[0].port) - return 0; - -- if (strlen(rec->iface.transport_name) && -- strcmp(rec->iface.transport_name, iface->transport_name)) -- return 0; -- -- if (!iface_match_bind_info(&rec->iface, iface)) -+ if (!iface_match(&rec->iface, iface)) - return 0; - - return 1; -diff --git a/usr/util.h b/usr/util.h -index 29ea0bf..6c4b5e3 100644 ---- a/usr/util.h -+++ b/usr/util.h -@@ -11,9 +11,17 @@ struct session_info; - - extern int oom_adjust(void); - extern void daemon_init(void); -+extern int increase_max_files(void); - - extern int do_iscsid(struct iscsiadm_req *req, struct iscsiadm_rsp *rsp); - extern void iscsid_handle_error(int err); -+extern int iscsid_request(int *fd, struct iscsiadm_req *req); -+extern int iscsid_response(int fd, int cmd, struct iscsiadm_rsp *rsp); -+extern int iscsid_req_wait(int cmd, int fd); -+extern int iscsid_req_by_rec_async(int cmd, struct node_rec *rec, int *fd); -+extern int iscsid_req_by_rec(int cmd, struct node_rec *rec); -+extern int iscsid_req_by_sid_async(int cmd, int sid, int *fd); -+extern int iscsid_req_by_sid(int cmd, int sid); - - extern char *str_to_ipport(char *str, int *port, int *tgpt); - extern void idbm_node_setup_defaults(struct node_rec *rec); -diff --git a/usr/version.h b/usr/version.h -index 3c540d0..ba23457 100644 ---- a/usr/version.h -+++ b/usr/version.h -@@ -6,7 +6,7 @@ - * This may not be the same value as the kernel versions because - * some other maintainer could merge a patch without going through us - */ --#define ISCSI_VERSION_STR "2.0-865" -+#define ISCSI_VERSION_STR "2.0-869" - #define ISCSI_VERSION_FILE "/sys/module/scsi_transport_iscsi/version" - - #endif -diff --git a/utils/Makefile b/utils/Makefile -index b376129..2c7e891 100644 ---- a/utils/Makefile -+++ b/utils/Makefile -@@ -9,4 +9,9 @@ iscsi-iname: md5.o iscsi-iname.o - $(CC) $(CFLAGS) $^ $(DBM_LIB) -o $@ - - clean: -- rm -f *.o $(PROGRAMS) -+ rm -f *.o $(PROGRAMS) .depend -+ -+depend: -+ gcc $(CFLAGS) -M `ls *.c` > .depend -+ -+-include .depend -diff --git a/utils/fwparam_ibft/Makefile b/utils/fwparam_ibft/Makefile -index ee090c1..6d7d00a 100644 ---- a/utils/fwparam_ibft/Makefile -+++ b/utils/fwparam_ibft/Makefile -@@ -1,11 +1,42 @@ --OPTFLAGS ?= -O2 -g -+# -+# Copyright (C) IBM Corporation. 2007 -+# Author: Doug Maxey -+# -+# 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, see . -+# -+# Authors: Patrick Mansfield -+# Mike Anderson -+# Doug Maxey -+# "Prasanna Mumbai" -+# -+ -+OBJS := fwparam_ibft.o fw_entry.o -+OBJS += prom_lex.o prom_parse.tab.o fwparam_ppc.o -+CLEANFILES = $(OBJS) *.output *~ -+ -+OPTFLAGS ?= -O2 -g -fPIC - WARNFLAGS ?= -Wall -Wstrict-prototypes --CFLAGS += $(OPTFLAGS) $(WARNFLAGS) --PROGRAMS = fwparam_ibft -+CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../../include - --all: $(PROGRAMS) -+all: $(OBJS) - --fwparam_ibft: fwparam_ibft.c fwparam_ibft.h -- $(CC) $(CFLAGS) $^ -o $@ - clean: -- rm -f *.o $(PROGRAMS) -+ rm -f *.o $(CLEANFILES) .depend -+ -+$(OBJS): prom_parse.tab.h prom_parse.h fwparam_ibft.h -+ -+depend: -+ gcc $(CFLAGS) -M `ls *.c` > .depend -+ -+-include .depend -diff --git a/utils/fwparam_ibft/fw_entry.c b/utils/fwparam_ibft/fw_entry.c -new file mode 100644 -index 0000000..915bbb7 ---- /dev/null -+++ b/utils/fwparam_ibft/fw_entry.c -@@ -0,0 +1,84 @@ -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * based on code written by "Prasanna Mumbai" -+ * -+ * 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, see . -+ * -+ */ -+#include -+#include -+#include -+#include "fw_context.h" -+#include "fwparam_ibft.h" -+ -+int fw_get_entry(struct boot_context *context, const char *filepath) -+{ -+ int ret; -+ -+ ret = fwparam_ppc(context, filepath); -+ if (ret) -+ ret = fwparam_ibft(context, filepath); -+ return ret; -+} -+ -+/* -+ * Dump the 8 byte mac address -+ */ -+static void dump_mac(struct boot_context *context) -+{ -+ int i; -+ -+ if (!strlen(context->mac)) -+ return; -+ -+ printf("iface.hwaddress = %s\n", context->mac); -+} -+ -+static void dump_initiator(struct boot_context *context) -+{ -+ if (!strlen(context->initiatorname)) -+ return; -+ printf("iface.initiatorname = %s\n", context->initiatorname); -+} -+ -+static void dump_target(struct boot_context *context) -+{ -+ -+ if (strlen(context->targetname)) -+ printf("node.name = %s\n", context->targetname); -+ -+ if (strlen(context->target_ipaddr)) -+ printf("node.conn[0].address = %s\n", context->target_ipaddr); -+ printf("node.conn[0].port = %d\n", context->target_port); -+ -+ if (strlen(context->chap_name)) -+ printf("node.session.auth.username = %s\n", context->chap_name); -+ if (strlen(context->chap_password)) -+ printf("node.session.auth.password = %s\n", -+ context->chap_password); -+ if (strlen(context->chap_name_in)) -+ printf("node.session.auth.username_in = %s\n", -+ context->chap_name_in); -+ if (strlen(context->chap_password_in)) -+ printf("node.session.auth.password_in = %s\n", -+ context->chap_password_in); -+} -+ -+void fw_print_entry(struct boot_context *context) -+{ -+ dump_initiator(context); -+ dump_mac(context); -+ dump_target(context); -+} -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index 1b33a98..02f8ac8 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -32,24 +32,16 @@ - #include - - #include "fwparam_ibft.h" -+#include "fw_context.h" - --char *progname; -+char *progname = "fwparam_ibft"; - int debug; --char default_file_name[] = "/dev/mem"; --char *filename = default_file_name; --int boot_selected_only; -+int dev_count; -+char filename[FILENAMESZ]; - - const char nulls[16]; /* defaults to zero */ - --/* -- * Prefix strings, for the "prefixN:NAME=value". -- */ --#define NETWORK "network" --#define INITIATOR "iscsi-initiator" --#define TGT "target" -- -- --void -+int - verify_hdr(char *name, struct ibft_hdr *hdr, int id, int version, int length) - { - #define VERIFY_HDR_FIELD(val) \ -@@ -58,7 +50,7 @@ verify_hdr(char *name, struct ibft_hdr *hdr, int id, int version, int length) - "%s: error, %s structure expected %s %d but" \ - " got %d\n", \ - progname, name, #val, hdr->val, val); \ -- exit(1); \ -+ return -1; \ - } - - if (debug > 1) -@@ -70,6 +62,7 @@ verify_hdr(char *name, struct ibft_hdr *hdr, int id, int version, int length) - VERIFY_HDR_FIELD(length); - - #undef VERIFY_HDR_FIELD -+ return 0; - } - - #define CHECK_HDR(ident, name) \ -@@ -314,20 +307,14 @@ dump_tgt_prefix(void *ibft_loc, struct ibft_tgt *tgt, char *prefix) - * Read in and dump ASCII output for ibft starting at ibft_loc. - */ - int --dump_ibft(void *ibft_loc) -+dump_ibft(void *ibft_loc, struct boot_context *context) - { - struct ibft_table_hdr *ibft_hdr = ibft_loc; - struct ibft_control *control; - struct ibft_initiator *initiator = NULL; - struct ibft_nic *nic0 = NULL, *nic1 = NULL; - struct ibft_tgt *tgt0 = NULL, *tgt1 = NULL; -- char sum = 0, *buf = ibft_loc; -- char prefix[32]; -- -- for (; buf <= (char *) (ibft_loc + ibft_hdr->length);) -- sum += *buf++; -- if (sum) -- fprintf(stderr, "Checksum not zero 0x%x \n", sum); -+ char ipbuf[32]; - - control = ibft_loc + sizeof(*ibft_hdr); - CHECK_HDR(control, control); -@@ -367,128 +354,122 @@ dump_ibft(void *ibft_loc) - CHECK_HDR(tgt1, target); - } - -- if (boot_selected_only) { -- -- snprintf(prefix, sizeof(prefix), "iSCSI_INITIATOR_"); -- -- if (initiator && (initiator->hdr.flags & -- INIT_FLAG_FW_SEL_BOOT)) -- dump_initiator_prefix(ibft_loc, initiator, prefix); -- -- if (nic0 && (nic0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -- dump_nic_prefix(ibft_loc, nic0, prefix); -- else if (nic1 && (nic1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -- dump_nic_prefix(ibft_loc, nic1, prefix); -- -- snprintf(prefix, sizeof(prefix), "iSCSI_TARGET_"); -- if (tgt0 && (tgt0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -- dump_tgt_prefix(ibft_loc, tgt0, prefix); -- else if (tgt1 && (tgt1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) -- dump_tgt_prefix(ibft_loc, tgt1, prefix); -- -- } else { -- -- snprintf(prefix, sizeof(prefix), "%s%d:", INITIATOR, 0); -- dump_initiator_prefix(ibft_loc, initiator, prefix); -- -- snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, 0); -- dump_nic_prefix(ibft_loc, nic0, prefix); -- snprintf(prefix, sizeof(prefix), "%s%d:", TGT, 0); -- dump_tgt_prefix(ibft_loc, tgt0, prefix); -- -- snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, 1); -- dump_nic_prefix(ibft_loc, nic1, prefix); -- snprintf(prefix, sizeof(prefix), "%s%d:", TGT, 1); -- dump_tgt_prefix(ibft_loc, tgt1, prefix); -+ strncpy(context->initiatorname, -+ (char *)ibft_loc+initiator->initiator_name_off, -+ initiator->initiator_name_len + 1); -+ -+ if (tgt0 && (tgt0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) { -+ strncpy((char *)context->targetname, -+ (char *)(ibft_loc+tgt0->tgt_name_off), -+ tgt0->tgt_name_len); -+ format_ipaddr(ipbuf, sizeof(ipbuf), -+ tgt0->ip_addr); -+ strncpy((char *)context->target_ipaddr, ipbuf, -+ sizeof(ipbuf)); -+ context->target_port = tgt0->port; -+ strncpy(context->chap_name, -+ (char *)(ibft_loc + tgt0->chap_name_off), -+ tgt0->chap_name_len); -+ strncpy(context->chap_password, -+ (char*)(ibft_loc + tgt0->chap_secret_off), -+ tgt0->chap_secret_len); -+ strncpy(context->chap_name_in, -+ (char *)(ibft_loc + tgt0->rev_chap_name_off), -+ tgt0->rev_chap_name_len); -+ strncpy(context->chap_password_in, -+ (char *)(ibft_loc + tgt0->rev_chap_secret_off), -+ tgt0->rev_chap_secret_len); -+ } else if (tgt1 && -+ (tgt1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) { -+ strncpy((char *)context->targetname, -+ (char *)(ibft_loc+tgt1->tgt_name_off), -+ tgt1->tgt_name_len); -+ format_ipaddr(ipbuf, sizeof(ipbuf), -+ tgt1->ip_addr); -+ strncpy((char *)context->target_ipaddr,ipbuf, -+ sizeof(ipbuf)); -+ context->target_port = tgt1->port; -+ strncpy(context->chap_name, -+ (char *)(ibft_loc + tgt1->chap_name_off), -+ tgt1->chap_name_len); -+ strncpy(context->chap_password, -+ (char*)(ibft_loc + tgt1->chap_secret_off), -+ tgt1->chap_secret_len); -+ strncpy(context->chap_name_in, -+ (char *)(ibft_loc + tgt1->rev_chap_name_off), -+ tgt1->rev_chap_name_len); -+ strncpy(context->chap_password_in, -+ (char *)(ibft_loc + tgt1->rev_chap_secret_off), -+ tgt1->rev_chap_secret_len); - } - - return 0; - } - --/* -- * return the address of the location of string in filebuf, search up to -- * max bytes of *filebuf, if not found returns NULL. -- */ --char * --search_file(char *filebuf, char *string, int len, int max) -+char *search_ibft(unsigned char *start, int length) - { -- char *cur = filebuf; -- char *end = filebuf + max; -- int i = 0; -- -- if (debug > 1) { -- fprintf(stderr, -- "%s: cur 0x%p, end 0x%p, string '%.4s', len %d\n", -- __FUNCTION__, cur, end, string, len); -- } -- while ((cur < end) && memcmp(cur, string, len)) { -- if (debug > 2) { -- fprintf(stderr, "i %d, cur 0x%p: 0x%x ('%c')\n", -- i, cur, cur[0], cur[0]); -- i++; -+ unsigned char *cur_ptr; -+ struct ibft_table_hdr *ibft_hdr; -+ unsigned char check_sum; -+ uint32_t i; -+ -+ cur_ptr = (unsigned char *)start; -+ for (cur_ptr = (unsigned char *)start; -+ cur_ptr < (start + length); -+ cur_ptr++) { -+ if (memcmp(cur_ptr, iBFTSTR,strlen(iBFTSTR))) -+ continue; -+ -+ ibft_hdr = (struct ibft_table_hdr *)cur_ptr; -+ /* Make sure it's correct version. */ -+ if (ibft_hdr->revision != iBFT_REV) -+ continue; -+ -+ /* Make sure that length is valid. */ -+ if ((cur_ptr + ibft_hdr->length) <= (start + length)) { -+ /* Let verify the checksum */ -+ for (i = 0, check_sum = 0; i < ibft_hdr->length; i++) -+ check_sum += cur_ptr[i]; -+ -+ if (check_sum == 0) -+ return (char *)cur_ptr; - } -- cur++; - } -- if (cur < end) -- return cur; -- else -- return NULL; -+ return NULL; - } - - int --main (int argc, char **argv) -+fwparam_ibft(struct boot_context *context, const char *filepath) - { -- int fd, option, ret; -+ int fd, ret; - char *filebuf, *ibft_loc; - int start = 512 * 1024; /* 512k */ - int end_search = (1024 * 1024) - start; /* 512k */ -+ struct stat buf; - -- progname = argv[0]; -- -- while (1) { -- option = getopt(argc, argv, "f:m:s:e:vhb"); -- if (option == -1) -- break; -- switch (option) { -- case 'b': -- boot_selected_only = 1; -- break; -- case 'e': -- end_search = strtoul(optarg, NULL, 0); -- break; -- case 'f': -- filename = optarg; -- break; -- case 's': -- start = strtoul(optarg, NULL, 0); -- break; -- case 'v': -- debug++; -- break; -- default: -- fprintf(stderr, "Unknown or bad option '%c'\n", option); -- case 'h': -- printf("Usage: %s OPTIONS\n" -- "-b print only fw boot selected sections\n" -- "-f file_to_search (default /dev/mem)\n" -- "-s offset to start search\n" -- "-e length of search\n" -- "-v verbose\n", -- progname); -- exit(1); -- } -- } -+ if (filepath) -+ strncpy(filename, filepath, FILENAMESZ); -+ else -+ strncpy(filename, X86_DEFAULT_FILENAME, FILENAMESZ); - -- if (debug) -- fprintf(stderr, "file: %s; start %d, end_search %d, debug %d\n", -- filename, start, end_search, debug); - fd = open(filename, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Could not open %s: %s (%d)\n", - filename, strerror(errno), errno); -- exit(1); -+ return -1; - } - -+ /* Find the size. */ -+ if (stat(filename, &buf)!=0) { -+ fprintf(stderr, "Could not stat file %s: %s (%d)\n", -+ filename, strerror(errno), errno); -+ return -1; -+ } -+ /* And if not zero use that size */ -+ if (buf.st_size > 0) { -+ start = 0; -+ end_search=buf.st_size; -+ } - /* - * XXX Possibly warn and exit if start > filesize(fd), or if start + - * end_search > filesize(fd). Else, we will get a bus error for -@@ -499,18 +480,19 @@ main (int argc, char **argv) - if (filebuf == MAP_FAILED) { - fprintf(stderr, "Could not mmap %s: %s (%d)\n", - filename, strerror(errno), errno); -- exit(1); -+ ret = -1; -+ goto done; - } - -- ibft_loc = search_file(filebuf, iBFTSTR, strlen(iBFTSTR), end_search); -- if (ibft_loc) { -- if (dump_ibft(ibft_loc)) -- ret = 0; -- else -- ret = 1; -- } else -- ret = 1; -+ ibft_loc = search_ibft((unsigned char *)filebuf, end_search); -+ if (ibft_loc) -+ ret = dump_ibft(ibft_loc, context); -+ else { -+ printf("Could not find iBFT.\n"); -+ ret = -1; -+ } - munmap(filebuf, end_search); -+done: - close(fd); -- exit(ret); -+ return ret; - } -diff --git a/utils/fwparam_ibft/fwparam_ibft.h b/utils/fwparam_ibft/fwparam_ibft.h -index 67b7084..90ecb17 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.h -+++ b/utils/fwparam_ibft/fwparam_ibft.h -@@ -14,7 +14,7 @@ - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - * -- * Copyright (C) IBM Corporation, 2006 -+ * Copyright (C) IBM Corporation, 2006,2007 - * - * Authors: Doug Maxey - * Patrick Mansfield -@@ -26,6 +26,7 @@ - - /* #include */ - #include -+#include "fw_context.h" - - /* - * Structures here are is based on Doug's original code, and Patrick's -@@ -35,6 +36,7 @@ - #define iBFTSTR "iBFT" - #define iBFT_SIG { 'i','B','F','T' } - -+#define iBFT_REV 1 - - /* - * These macros are lower case to make the verify_hdr macro easier. -@@ -136,4 +138,21 @@ struct ibft_tgt { - uint16_t rev_chap_secret_off; - } __attribute__((__packed__)); - -+/* Common variables */ -+#define FILENAMESZ (256) -+extern char filename[FILENAMESZ]; -+#define X86_DEFAULT_FILENAME "/dev/mem" -+extern int debug; -+extern int dev_count; -+ -+/* -+ * Prefix strings, for the "prefixN:NAME=value". -+ */ -+#define NETWORK "network" -+#define INITIATOR "iscsi-initiator" -+#define TARGET "target" -+ -+extern int fwparam_ibft(struct boot_context *context, const char *filepath); -+extern int fwparam_ppc(struct boot_context *context, const char *filepath); -+ - #endif /* FWPARAM_IBFT_H_ */ -diff --git a/utils/fwparam_ibft/fwparam_ppc.c b/utils/fwparam_ibft/fwparam_ppc.c -new file mode 100644 -index 0000000..b040837 ---- /dev/null -+++ b/utils/fwparam_ibft/fwparam_ppc.c -@@ -0,0 +1,494 @@ -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * -+ * 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, see . -+ */ -+ -+#define _XOPEN_SOURCE 500 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "fwparam_ibft.h" -+#include "fw_context.h" -+#include "iscsi_obp.h" -+#include "prom_parse.h" -+ -+void* yy_scan_string(const char *str); -+int yyparse(struct ofw_dev *ofwdev); -+ -+#define BOOTPATH "/chosen/bootpath" -+#define DT_TOP "/proc/device-tree" -+#define LOCAL_MAC_FILE "/local-mac-address" -+ -+static int devtree_offset; -+static char *bootpath_val; -+static int bytes_read; -+#define OFWDEV_MAX (10) -+static struct ofw_dev *ofwdevs[OFWDEV_MAX]; -+static char *niclist[OFWDEV_MAX]; -+static int nic_count; -+ -+static void cp_param(char *dest, const char *name, struct ofw_dev *dev, -+ enum obp_param item, int len) -+{ -+ if (dev->param[item]) -+ strncpy(dest, dev->param[item]->val, len); -+} -+ -+static void cp_int_param(int *dest, const char *name, struct ofw_dev *dev, -+ enum obp_param item) -+{ -+ if (dev->param[item]) -+ *dest = strtol(dev->param[item]->val, NULL, 10); -+} -+ -+static char *find_devtree(const char *filename) -+{ -+ char *devtree = strdup(filename); -+ char *chop_at; -+ struct stat dt_stat; -+ int error; -+ -+ /* -+ * What is the path to the device-tree? The only valid -+ * directories to locate the property are under /aliases or -+ * /chosen. -+ */ -+ -+ chop_at = strstr(devtree, "/chosen"); -+ if (!chop_at) -+ chop_at = strstr(devtree, "/aliases"); -+ -+ if (!chop_at) { -+ char *vdev = malloc(strlen(filename) + strlen("/vdevice") + 1); -+ -+ /* -+ * test to see if there is /vdevice dir -+ */ -+ if (vdev) { -+ sprintf(vdev, "%s%s", filename, "/vdevice"); -+ error = stat(vdev, &dt_stat); -+ if (error) -+ devtree = NULL; -+ free(vdev); -+ } -+ } else -+ devtree[chop_at - devtree] = 0; -+ -+ if (devtree) -+ devtree_offset = strlen(devtree); -+ -+ return devtree; -+} -+ -+/* -+ * Take the path to the property under chosen, and swizzle to make that -+ * the base for the device path discovered. -+ */ -+static int locate_mac(const char *devtree, struct ofw_dev *ofwdev) -+{ -+ int error = 0; -+ int mac_path_len = strlen(ofwdev->dev_path) + strlen(LOCAL_MAC_FILE) + -+ 2; -+ char *mac_file; -+ int mac_fd; -+ -+ mac_path_len += strlen(devtree); -+ mac_file = malloc(mac_path_len); -+ if (!mac_file) { -+ error = ENOMEM; -+ fprintf(stderr, "%s: malloc %s, %s\n", __func__, filename, -+ strerror(errno)); -+ goto lpm_bail; -+ } -+ -+ snprintf(mac_file, mac_path_len, "%s%s%s", devtree, ofwdev->dev_path, -+ LOCAL_MAC_FILE); -+ mac_fd = open(mac_file, O_RDONLY); -+ if (mac_fd < 0) { -+ error = errno; -+ fprintf(stderr, "%s: open %s, %s\n", __func__, mac_file, -+ strerror(errno)); -+ goto lpm_bail; -+ } -+ -+ bytes_read = read(mac_fd, ofwdev->mac, 6); -+ if (bytes_read != 6) { -+ error = EIO; -+ fprintf(stderr, "%s: read %s, %s\n", __func__, mac_file, -+ strerror(errno)); -+ goto lpm_bail; -+ } -+ free(mac_file); -+ close(mac_fd); -+ -+ -+lpm_bail: -+ return error; -+} -+ -+const char *obp_qual_set(struct ofw_dev *ofwdev, const char *qual) -+{ -+ if (!strcmp("bootp", qual)) -+ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_BOOTP; -+ else if (!strcmp("dhcpv6", qual)) -+ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_DHCPV6; -+ else if (!strcmp("ipv6", qual)) -+ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_IPV6; -+ else if (!strcmp("iscsi", qual)) { -+ ofwdev->type = OFW_DT_ISCSI; -+ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_ISCSI; -+ } else if (!strcmp("ping", qual)) -+ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_PING; -+ else -+ printf("%s: %s UNKNOWN\n", __func__, qual); -+ return qual; -+} -+ -+void add_obp_parm(struct ofw_dev *ofwdev, enum obp_param parm, const char *str) -+{ -+ int psz = sizeof(struct ofw_obp_param) + strlen(str); -+ -+ ofwdev->param[parm] = malloc(psz); -+ if (ofwdev->param[parm] == NULL) { -+ printf("%s: ENOMEM!\n", __func__); -+ return; -+ } -+ memset(ofwdev->param[parm], 0, psz); -+ ofwdev->param[parm]->len = psz; -+ strcpy(ofwdev->param[parm]->val, str); -+} -+ -+void obp_parm_addr(struct ofw_dev *ofwdev, const char *parm, const char *addr) -+{ -+ if (!strcmp("ciaddr", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_CIADDR, addr); -+ else if (!strcmp("dhcp", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_DHCP, addr); -+ else if (!strcmp("giaddr", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_GIADDR, addr); -+ else if (!strcmp("isns", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_ISNS, addr); -+ else if (!strcmp("siaddr", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_SIADDR, addr); -+ else if (!strcmp("slp", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_SLP, addr); -+ else if (!strcmp("subnet-mask", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_SUBNET_MASK, addr); -+ else -+ printf("%s: %s UNKNOWN\n", __func__, parm); -+} -+ -+void obp_parm_iqn(struct ofw_dev *ofwdev, const char *parm, const char *iqn) -+{ -+ if (!strcmp("itname", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_ITNAME, iqn); -+ else if (!strcmp("iname", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_INAME, iqn); -+ else -+ printf("%s: %s UNKNOWN\n", __func__, parm); -+} -+ -+void obp_parm_hexnum(struct ofw_dev *ofwdev, const char *parm, -+ const char *numstr) -+{ -+ if (!strcmp("bootp-retries", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_BOOTP_RETRIES, numstr); -+ else if (!strcmp("tftp-retries", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_TFTP_RETRIES, numstr); -+ else if (!strcmp("iport", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_IPORT, numstr); -+ else if (!strcmp("ilun", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_ILUN, numstr); -+ else if (!strcmp("isid", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_ISID, numstr); -+ else -+ printf("%s: %s UNKNOWN <%s>\n", __func__, parm, numstr); -+} -+ -+void obp_parm_str(struct ofw_dev *ofwdev, const char *parm, const char *str) -+{ -+ if (!strcmp("filename", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_FILENAME, str); -+ else if (!strcmp("ichapid", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_ICHAPID, str); -+ else if (!strcmp("ichappw", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_ICHAPPW, str); -+ else if (!strcmp("chapid", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_CHAPID, str); -+ else if (!strcmp("chappw", parm)) -+ add_obp_parm(ofwdev, OBP_PARAM_CHAPPW, str); -+ else -+ printf("%s: %s UNKNOWN <%s>\n", __func__, parm, str); -+} -+ -+void yyerror(struct ofw_dev *ofwdev, const char *msg) -+{ -+ fprintf(stderr, "%s: error in <%s> at l%d.c%d\n", "fwparam_ppc", -+ ofwdev->prop_path, yylloc.last_line, yylloc.last_column); -+} -+ -+static int parse_params(const char *buf, struct ofw_dev *ofwdev) -+{ -+ int error = 0; -+#if YYDEBUG -+ yydebug = 1; -+#endif -+ -+ -+ if (yy_scan_string(buf)) -+ error = yyparse(ofwdev); -+ -+ return error; -+} -+ -+static int find_file(const char *filename) -+{ -+ int error, fd; -+ struct stat bootpath_stat; -+ -+ error = stat(filename, &bootpath_stat); -+ if (error < 0) { -+ fprintf(stderr, "%s: stat %s, %s\n", __func__, filename, -+ strerror(errno)); -+ return error; -+ } -+ -+ bootpath_val = malloc(bootpath_stat.st_size); -+ if (!bootpath_val) { -+ error = ENOMEM; -+ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", -+ __func__, filename, strerror(error), error); -+ return -1; -+ } -+ -+ fd = open(filename, O_RDONLY); -+ if (fd < 0) { -+ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", -+ __func__, filename, strerror(errno), errno); -+ return -1; -+ } -+ -+ bytes_read = read(fd, bootpath_val, bootpath_stat.st_size); -+ close(fd); -+ if (bytes_read != bootpath_stat.st_size) { -+ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", -+ __func__, filename, strerror(EIO), EIO); -+ return -1; -+ } -+ -+ close(fd); -+ -+ return 1; -+} -+ -+static int find_nics(const char *fpath, const struct stat *sb, int tflag, -+ struct FTW *ftw) -+{ -+ if (tflag == FTW_D && -+ (strstr(fpath + ftw->base, "iscsi-toe") || -+ strstr(fpath + ftw->base, "ethernet"))) { -+ -+ if (nic_count < OFWDEV_MAX) -+ niclist[nic_count++] = strdup(fpath + devtree_offset); -+ -+ } -+ return 0; -+} -+ -+int nic_cmp(const char **a, const char **b) -+{ -+ return strcmp(*a, *b); -+} -+ -+static int find_initiator(const char *fpath, const struct stat *sb, int tflag, -+ struct FTW *ftw) -+{ -+ struct ofw_dev *dev; -+ -+ if (tflag == FTW_F && (strstr(fpath + ftw->base, -+ "/aliases/iscsi-disk"))) { -+ -+ if (dev_count < OFWDEV_MAX) { -+ ofwdevs[dev_count++] = dev = -+ calloc(sizeof(struct ofw_dev), 1); -+ dev->prop_path = strdup(fpath + devtree_offset); -+ } -+ } -+ return 0; -+} -+ -+static int loop_devs(const char *devtree) -+{ -+ int error; -+ int i; -+ char prefix[256]; -+ -+ error = nftw(devtree, find_nics, 20, 0); -+ if (error) -+ return error; -+ -+ /* -+ * Sort the nics into "natural" order. The proc fs -+ * device-tree has them in somewhat random, or reversed order. -+ */ -+ qsort(niclist, nic_count, sizeof(char *), nic_cmp); -+ -+ snprintf(prefix, sizeof(prefix), "%s/%s", devtree, "aliases"); -+ error = nftw(prefix, find_initiator, 20, 0); -+ if (error) -+ return error; -+ -+ for (i = 0; i < dev_count; i++) { -+ snprintf(prefix, sizeof(prefix), "%s%s", devtree, -+ ofwdevs[i]->prop_path); -+ if (find_file(prefix) > 0) { -+ error = parse_params(bootpath_val, ofwdevs[i]); -+ if (!error) -+ error = locate_mac(devtree, ofwdevs[i]); -+ -+ } -+ } -+ if (!error) -+ putchar('\n'); -+ return error; -+} -+ -+#define set_context(fld,abrv,item) \ -+ cp_param(context->fld, (abrv), ofwdev, (item), sizeof(context->fld)) -+#define set_int_context(fld,abrv,item) \ -+ cp_int_param(&context->fld, (abrv), ofwdev, (item)) -+ -+static void fill_context(struct boot_context *context, struct ofw_dev *ofwdev) -+{ -+ int ndx; -+ -+ memset(context, 0, sizeof(*context)); -+ -+ set_context(initiatorname, "NAME", OBP_PARAM_ITNAME); -+ -+ snprintf(context->mac, sizeof(context->mac), -+ "%02x:%02x:%02x:%02x:%02x:%02x", -+ ofwdev->mac[0], ofwdev->mac[1], ofwdev->mac[2], -+ ofwdev->mac[3], ofwdev->mac[4], ofwdev->mac[5]); -+ -+ /* -+ * nic parameters -+ */ -+ for (ndx = 0; ndx < nic_count; ndx++) { -+ if (!strcmp(niclist[ndx], ofwdev->dev_path)) { -+ snprintf(context->iface, sizeof(context->iface), -+ "eth%d", ndx); -+ break; -+ } -+ } -+ -+ set_context(ipaddr, "IPADDR", OBP_PARAM_CIADDR); -+ set_context(mask, "MASK", OBP_PARAM_SUBNET_MASK); -+ -+ /* -+ * target parameters -+ */ -+ set_context(target_ipaddr, "IPADDR", OBP_PARAM_SIADDR); -+ set_int_context(target_port, "PORT", OBP_PARAM_IPORT); -+ set_context(lun, "LUN", OBP_PARAM_ILUN); -+ set_context(targetname, "NAME", OBP_PARAM_INAME); -+ set_context(isid, "ISID", OBP_PARAM_ISID); -+ -+ /* -+ * chap stuff is always associated with the target -+ */ -+ set_context(chap_name, "CHAP_NAME", OBP_PARAM_ICHAPID); -+ set_context(chap_password, "CHAP_PASSWORD", OBP_PARAM_ICHAPPW); -+ set_context(chap_name_in, "CHAP_NAME_IN", OBP_PARAM_CHAPID); -+ set_context(chap_password_in, "CHAP_PASSWORD_IN", OBP_PARAM_CHAPPW); -+ -+} -+ -+int fwparam_ppc(struct boot_context *context, const char *filepath) -+{ -+ int error; -+ int fplen = 0; -+ char *devtree; -+ -+ /* -+ * For powerpc, our operations are fundamentally to locate -+ * either the one boot target (the singleton disk), or to find -+ * the nics that support iscsi boot. The only nics in IBM -+ * systems that can support iscsi are the ones that provide -+ * the appropriate FCODE with a load method. -+ */ -+ if (filepath) { -+ strncat(filename, filepath, FILENAMESZ); -+ fplen = strlen(filename); -+ } else -+ strncat(filename, DT_TOP, FILENAMESZ); -+ -+ strncat(filename + fplen, BOOTPATH, FILENAMESZ - fplen); -+ -+ if (debug) -+ fprintf(stderr, "%s: file:%s; debug:%d\n", __func__, filename, -+ debug); -+ -+ devtree = find_devtree(filename); -+ if (!devtree) -+ return 2; -+ -+ /* -+ * Always search the device-tree to find the capable nic devices. -+ */ -+ error = loop_devs(devtree); -+ if (error) -+ return error; -+ -+ dev_count = find_file(filename); -+ if (dev_count < 1) -+ error = 3; -+ else { -+ if (debug) -+ printf("%s:\n%s\n\n", filename, bootpath_val); -+ /* -+ * We find *almost* everything we need in the -+ * bootpath, save the mac-address. -+ */ -+ -+ if (strstr(bootpath_val, "iscsi")) { -+ ofwdevs[0] = calloc(1, sizeof(struct ofw_dev)); -+ if (!ofwdevs[0]) -+ return ENOMEM; -+ -+ error = parse_params(bootpath_val, ofwdevs[0]); -+ if (!error) -+ error = locate_mac(devtree, ofwdevs[0]); -+ -+ } else -+ /* -+ * yikes! we did not boot from iscsi. -+ * tsk, tsk. -+ */ -+ error = 1; -+ -+ if (!error) -+ fill_context(context, ofwdevs[0]); -+ -+ } -+ -+ return error; -+} -diff --git a/utils/fwparam_ibft/iscsi_obp.h b/utils/fwparam_ibft/iscsi_obp.h -new file mode 100644 -index 0000000..8580052 ---- /dev/null -+++ b/utils/fwparam_ibft/iscsi_obp.h -@@ -0,0 +1,93 @@ -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * -+ * 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, see . -+ */ -+ -+#ifndef ISCSI_OBP_H_ -+#define ISCSI_OBP_H_ -+ -+enum ofw_dev_type { -+ OFW_DT_NONE, -+ OFW_DT_BLOCK, -+ OFW_DT_NETWORK, -+ OFW_DT_ISCSI, -+}; -+ -+enum obp_tftp_qual { -+ OBP_QUAL_NONE, -+ OBP_QUAL_BOOTP, -+ OBP_QUAL_DHCPV6, -+ OBP_QUAL_IPV6, -+ OBP_QUAL_ISCSI, -+ OBP_QUAL_PING, -+ OBP_QUAL_COUNT, /* Numnber of defined OBP qualifiers */ -+}; -+ -+enum obp_param { -+ /* -+ * Defined iscsi boot parameters. -+ */ -+ OBP_PARAM_NONE, -+ OBP_PARAM_BLKSIZE, /* default is 512 */ -+ OBP_PARAM_BOOTP_RETRIES, /* default 5 */ -+ OBP_PARAM_CHAPID, /* target chap id */ -+ OBP_PARAM_CHAPPW, /* target chap password */ -+ OBP_PARAM_CIADDR, /* client (my) ip addr */ -+ OBP_PARAM_DHCP, /* dhcp server address */ -+ OBP_PARAM_FILENAME, /* boot filename */ -+ OBP_PARAM_GIADDR, /* gateway addr */ -+ OBP_PARAM_ICHAPID, /* initiator chapid */ -+ OBP_PARAM_ICHAPPW, /* initiator chap password */ -+ OBP_PARAM_ILUN, /* misnomer, really the target lun */ -+ OBP_PARAM_INAME, /* NB: target iqn */ -+ OBP_PARAM_IPORT, /* initiator port, defaults to 3260 */ -+ OBP_PARAM_ISID, /* session id */ -+ OBP_PARAM_ISNS, /* sns server address */ -+ OBP_PARAM_ITNAME, /* NB: Initiator iqn */ -+ OBP_PARAM_SIADDR, /* iscsi server ip address. */ -+ OBP_PARAM_SLP, /* slp server address */ -+ OBP_PARAM_SUBNET_MASK, -+ OBP_PARAM_TFTP_RETRIES, /* default 5 */ -+ OBP_PARAM_TIMEOUT, /* ping timeout period. */ -+ -+ OBP_PARAM_COUNT, /* number of defined OBP_PARAMs */ -+}; -+ -+struct ofw_obp_param { -+ unsigned char len; /* length of value string. */ -+ char val[1]; /* string value from the property */ -+}; -+ -+struct ofw_dev { -+ char *prop_path; /* where we found these properties. */ -+ enum ofw_dev_type type; /* known type of boot device. */ -+ int qual_count; /* count of qualifiers. */ -+ enum obp_tftp_qual quals[OBP_QUAL_COUNT]; -+ struct ofw_obp_param *param[OBP_PARAM_COUNT]; -+ int cfg_part; /* boot partition number. */ -+ char *dev_path; /* path to this ofw device. */ -+ unsigned char mac[6]; /* The binary mac address. */ -+}; -+ -+const char *obp_qual_set(struct ofw_dev *ofwdev, const char *qual); -+void add_obp_parm(struct ofw_dev *ofwdev, enum obp_param parm, const char *str); -+void obp_parm_addr(struct ofw_dev *ofwdev, const char *parm, const char *addr); -+void obp_parm_iqn(struct ofw_dev *ofwdev, const char *parm, const char *iqn); -+void obp_parm_hexnum(struct ofw_dev *ofwdev, const char *parm, -+ const char *numstr); -+void obp_parm_str(struct ofw_dev *ofwdev, const char *parm, const char *str); -+ -+#endif /* ISCSI_OBP_H_ */ -diff --git a/utils/fwparam_ibft/prom_lex.c b/utils/fwparam_ibft/prom_lex.c -new file mode 100644 -index 0000000..704d8ca ---- /dev/null -+++ b/utils/fwparam_ibft/prom_lex.c -@@ -0,0 +1,2285 @@ -+ -+#line 3 "" -+ -+#define YY_INT_ALIGNED short int -+ -+/* A lexical scanner generated by flex */ -+ -+#define FLEX_SCANNER -+#define YY_FLEX_MAJOR_VERSION 2 -+#define YY_FLEX_MINOR_VERSION 5 -+#define YY_FLEX_SUBMINOR_VERSION 33 -+#if YY_FLEX_SUBMINOR_VERSION > 0 -+#define FLEX_BETA -+#endif -+ -+/* First, we deal with platform-specific or compiler-specific issues. */ -+ -+/* begin standard C headers. */ -+#include -+#include -+#include -+#include -+ -+/* end standard C headers. */ -+ -+/* flex integer type definitions */ -+ -+#ifndef FLEXINT_H -+#define FLEXINT_H -+ -+/* C99 systems have . Non-C99 systems may or may not. */ -+ -+#if __STDC_VERSION__ >= 199901L -+ -+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, -+ * if you want the limit (max/min) macros for int types. -+ */ -+#ifndef __STDC_LIMIT_MACROS -+#define __STDC_LIMIT_MACROS 1 -+#endif -+ -+#include -+typedef int8_t flex_int8_t; -+typedef uint8_t flex_uint8_t; -+typedef int16_t flex_int16_t; -+typedef uint16_t flex_uint16_t; -+typedef int32_t flex_int32_t; -+typedef uint32_t flex_uint32_t; -+#else -+typedef signed char flex_int8_t; -+typedef short int flex_int16_t; -+typedef int flex_int32_t; -+typedef unsigned char flex_uint8_t; -+typedef unsigned short int flex_uint16_t; -+typedef unsigned int flex_uint32_t; -+#endif /* ! C99 */ -+ -+/* Limits of integral types. */ -+#ifndef INT8_MIN -+#define INT8_MIN (-128) -+#endif -+#ifndef INT16_MIN -+#define INT16_MIN (-32767-1) -+#endif -+#ifndef INT32_MIN -+#define INT32_MIN (-2147483647-1) -+#endif -+#ifndef INT8_MAX -+#define INT8_MAX (127) -+#endif -+#ifndef INT16_MAX -+#define INT16_MAX (32767) -+#endif -+#ifndef INT32_MAX -+#define INT32_MAX (2147483647) -+#endif -+#ifndef UINT8_MAX -+#define UINT8_MAX (255U) -+#endif -+#ifndef UINT16_MAX -+#define UINT16_MAX (65535U) -+#endif -+#ifndef UINT32_MAX -+#define UINT32_MAX (4294967295U) -+#endif -+ -+#endif /* ! FLEXINT_H */ -+ -+#ifdef __cplusplus -+ -+/* The "const" storage-class-modifier is valid. */ -+#define YY_USE_CONST -+ -+#else /* ! __cplusplus */ -+ -+#if __STDC__ -+ -+#define YY_USE_CONST -+ -+#endif /* __STDC__ */ -+#endif /* ! __cplusplus */ -+ -+#ifdef YY_USE_CONST -+#define yyconst const -+#else -+#define yyconst -+#endif -+ -+/* Returned upon end-of-file. */ -+#define YY_NULL 0 -+ -+/* Promotes a possibly negative, possibly signed char to an unsigned -+ * integer for use as an array index. If the signed char is negative, -+ * we want to instead treat it as an 8-bit unsigned char, hence the -+ * double cast. -+ */ -+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) -+ -+/* Enter a start condition. This macro really ought to take a parameter, -+ * but we do it the disgusting crufty way forced on us by the ()-less -+ * definition of BEGIN. -+ */ -+#define BEGIN (yy_start) = 1 + 2 * -+ -+/* Translate the current start state into a value that can be later handed -+ * to BEGIN to return to the state. The YYSTATE alias is for lex -+ * compatibility. -+ */ -+#define YY_START (((yy_start) - 1) / 2) -+#define YYSTATE YY_START -+ -+/* Action number for EOF rule of a given start state. */ -+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) -+ -+/* Special action meaning "start processing a new file". */ -+#define YY_NEW_FILE yyrestart(yyin ) -+ -+#define YY_END_OF_BUFFER_CHAR 0 -+ -+/* Size of default input buffer. */ -+#ifndef YY_BUF_SIZE -+#define YY_BUF_SIZE 16384 -+#endif -+ -+/* The state buf must be large enough to hold one state per character in the main buffer. -+ */ -+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) -+ -+#ifndef YY_TYPEDEF_YY_BUFFER_STATE -+#define YY_TYPEDEF_YY_BUFFER_STATE -+typedef struct yy_buffer_state *YY_BUFFER_STATE; -+#endif -+ -+extern int yyleng; -+ -+extern FILE *yyin, *yyout; -+ -+#define EOB_ACT_CONTINUE_SCAN 0 -+#define EOB_ACT_END_OF_FILE 1 -+#define EOB_ACT_LAST_MATCH 2 -+ -+ #define YY_LESS_LINENO(n) -+ -+/* Return all but the first "n" matched characters back to the input stream. */ -+#define yyless(n) \ -+ do \ -+ { \ -+ /* Undo effects of setting up yytext. */ \ -+ int yyless_macro_arg = (n); \ -+ YY_LESS_LINENO(yyless_macro_arg);\ -+ *yy_cp = (yy_hold_char); \ -+ YY_RESTORE_YY_MORE_OFFSET \ -+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ -+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ -+ } \ -+ while ( 0 ) -+ -+#define unput(c) yyunput( c, (yytext_ptr) ) -+ -+/* The following is because we cannot portably get our hands on size_t -+ * (without autoconf's help, which isn't available because we want -+ * flex-generated scanners to compile on their own). -+ */ -+ -+#ifndef YY_TYPEDEF_YY_SIZE_T -+#define YY_TYPEDEF_YY_SIZE_T -+typedef unsigned int yy_size_t; -+#endif -+ -+#ifndef YY_STRUCT_YY_BUFFER_STATE -+#define YY_STRUCT_YY_BUFFER_STATE -+struct yy_buffer_state -+ { -+ FILE *yy_input_file; -+ -+ char *yy_ch_buf; /* input buffer */ -+ char *yy_buf_pos; /* current position in input buffer */ -+ -+ /* Size of input buffer in bytes, not including room for EOB -+ * characters. -+ */ -+ yy_size_t yy_buf_size; -+ -+ /* Number of characters read into yy_ch_buf, not including EOB -+ * characters. -+ */ -+ int yy_n_chars; -+ -+ /* Whether we "own" the buffer - i.e., we know we created it, -+ * and can realloc() it to grow it, and should free() it to -+ * delete it. -+ */ -+ int yy_is_our_buffer; -+ -+ /* Whether this is an "interactive" input source; if so, and -+ * if we're using stdio for input, then we want to use getc() -+ * instead of fread(), to make sure we stop fetching input after -+ * each newline. -+ */ -+ int yy_is_interactive; -+ -+ /* Whether we're considered to be at the beginning of a line. -+ * If so, '^' rules will be active on the next match, otherwise -+ * not. -+ */ -+ int yy_at_bol; -+ -+ int yy_bs_lineno; /**< The line count. */ -+ int yy_bs_column; /**< The column count. */ -+ -+ /* Whether to try to fill the input buffer when we reach the -+ * end of it. -+ */ -+ int yy_fill_buffer; -+ -+ int yy_buffer_status; -+ -+#define YY_BUFFER_NEW 0 -+#define YY_BUFFER_NORMAL 1 -+ /* When an EOF's been seen but there's still some text to process -+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we -+ * shouldn't try reading from the input source any more. We might -+ * still have a bunch of tokens to match, though, because of -+ * possible backing-up. -+ * -+ * When we actually see the EOF, we change the status to "new" -+ * (via yyrestart()), so that the user can continue scanning by -+ * just pointing yyin at a new input file. -+ */ -+#define YY_BUFFER_EOF_PENDING 2 -+ -+ }; -+#endif /* !YY_STRUCT_YY_BUFFER_STATE */ -+ -+/* Stack of input buffers. */ -+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ -+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ -+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ -+ -+/* We provide macros for accessing buffer states in case in the -+ * future we want to put the buffer states in a more general -+ * "scanner state". -+ * -+ * Returns the top of the stack, or NULL. -+ */ -+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ -+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ -+ : NULL) -+ -+/* Same as previous macro, but useful when we know that the buffer stack is not -+ * NULL or when we need an lvalue. For internal use only. -+ */ -+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] -+ -+/* yy_hold_char holds the character lost when yytext is formed. */ -+static char yy_hold_char; -+static int yy_n_chars; /* number of characters read into yy_ch_buf */ -+int yyleng; -+ -+/* Points to current character in buffer. */ -+static char *yy_c_buf_p = (char *) 0; -+static int yy_init = 0; /* whether we need to initialize */ -+static int yy_start = 0; /* start state number */ -+ -+/* Flag which is used to allow yywrap()'s to do buffer switches -+ * instead of setting up a fresh yyin. A bit of a hack ... -+ */ -+static int yy_did_buffer_switch_on_eof; -+ -+void yyrestart (FILE *input_file ); -+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ); -+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ); -+void yy_delete_buffer (YY_BUFFER_STATE b ); -+void yy_flush_buffer (YY_BUFFER_STATE b ); -+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ); -+void yypop_buffer_state (void ); -+ -+static void yyensure_buffer_stack (void ); -+static void yy_load_buffer_state (void ); -+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ); -+ -+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ) -+ -+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ); -+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ); -+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ); -+ -+void *yyalloc (yy_size_t ); -+void *yyrealloc (void *,yy_size_t ); -+void yyfree (void * ); -+ -+#define yy_new_buffer yy_create_buffer -+ -+#define yy_set_interactive(is_interactive) \ -+ { \ -+ if ( ! YY_CURRENT_BUFFER ){ \ -+ yyensure_buffer_stack (); \ -+ YY_CURRENT_BUFFER_LVALUE = \ -+ yy_create_buffer(yyin,YY_BUF_SIZE ); \ -+ } \ -+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ -+ } -+ -+#define yy_set_bol(at_bol) \ -+ { \ -+ if ( ! YY_CURRENT_BUFFER ){\ -+ yyensure_buffer_stack (); \ -+ YY_CURRENT_BUFFER_LVALUE = \ -+ yy_create_buffer(yyin,YY_BUF_SIZE ); \ -+ } \ -+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ -+ } -+ -+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) -+ -+/* Begin user sect3 */ -+ -+#define yywrap(n) 1 -+#define YY_SKIP_YYWRAP -+ -+typedef unsigned char YY_CHAR; -+ -+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; -+ -+typedef int yy_state_type; -+ -+extern int yylineno; -+ -+int yylineno = 1; -+ -+extern char yytext[]; -+ -+static yy_state_type yy_get_previous_state (void ); -+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); -+static int yy_get_next_buffer (void ); -+static void yy_fatal_error (yyconst char msg[] ); -+ -+/* Done after the current pattern has been matched and before the -+ * corresponding action - sets up yytext. -+ */ -+#define YY_DO_BEFORE_ACTION \ -+ (yytext_ptr) = yy_bp; \ -+ yyleng = (size_t) (yy_cp - yy_bp); \ -+ (yy_hold_char) = *yy_cp; \ -+ *yy_cp = '\0'; \ -+ if ( yyleng >= YYLMAX ) \ -+ YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \ -+ yy_flex_strncpy( yytext, (yytext_ptr), yyleng + 1 ); \ -+ (yy_c_buf_p) = yy_cp; -+ -+#define YY_NUM_RULES 17 -+#define YY_END_OF_BUFFER 18 -+/* This struct is not used in this scanner, -+ but its presence is necessary. */ -+struct yy_trans_info -+ { -+ flex_int32_t yy_verify; -+ flex_int32_t yy_nxt; -+ }; -+static yyconst flex_int16_t yy_accept[444] = -+ { 0, -+ 0, 0, 18, 16, 15, 15, 12, 12, 16, 12, -+ 12, 12, 12, 12, 12, 16, 16, 16, 16, 16, -+ 16, 16, 16, 16, 16, 15, 0, 12, 12, 14, -+ 0, 0, 0, 12, 0, 0, 12, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, -+ 0, 0, 0, 0, 0, 0, 12, 12, 14, 7, -+ 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, -+ -+ 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, -+ 11, 0, 0, 0, 0, 0, 0, 0, 6, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 13, 0, 0, 6, 0, 0, 0, 0, 0, 0, -+ 0, 3, 0, 9, 6, 0, 0, 5, 0, 0, -+ 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, -+ 0, 0, 0, 9, 0, 0, 0, 0, 0, 8, -+ 0, 13, 0, 0, 0, 0, 0, 9, 0, 0, -+ 0, 0, 0, 0, 2, 8, 13, 1, 0, 9, -+ 0, 0, 0, 0, 0, 0, 8, 13, 0, 9, -+ -+ 0, 0, 10, 0, 0, 0, 13, 0, 9, 0, -+ 0, 0, 0, 0, 13, 0, 9, 0, 0, 0, -+ 13, 0, 9, 0, 0, 13, 9, 0, 0, 13, -+ 9, 13, 9, 13, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, -+ 9, 9, 0 -+ } ; -+ -+static yyconst flex_int32_t yy_ec[256] = -+ { 0, -+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 4, 5, 1, 6, 6, 7, -+ 6, 6, 6, 8, 6, 6, 6, 9, 1, 1, -+ 1, 1, 1, 1, 10, 10, 10, 10, 10, 10, -+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, -+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, -+ 1, 12, 1, 1, 1, 1, 13, 14, 15, 16, -+ -+ 17, 18, 19, 20, 21, 11, 22, 23, 24, 25, -+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 11, -+ 11, 35, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1 -+ } ; -+ -+static yyconst flex_int32_t yy_meta[36] = -+ { 0, -+ 1, 1, 1, 2, 3, 4, 4, 4, 5, 4, -+ 2, 6, 4, 4, 4, 4, 4, 4, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2 -+ } ; -+ -+static yyconst flex_int16_t yy_base[680] = -+ { 0, -+ 0, 0, 1198, 1199, 34, 36, 35, 1192, 0, 39, -+ 40, 41, 47, 42, 44, 29, 67, 1176, 1182, 1179, -+ 1180, 86, 1174, 1161, 1174, 51, 69, 73, 1184, 0, -+ 1175, 1165, 1160, 43, 1172, 1171, 51, 1168, 1152, 1161, -+ 1157, 1166, 1163, 1162, 1156, 1158, 1142, 1160, 25, 1147, -+ 85, 1146, 1153, 1139, 1147, 1133, 1135, 1135, 1199, 1151, -+ 1136, 1148, 1130, 1146, 1142, 80, 1153, 1152, 0, 1199, -+ 1126, 1124, 1128, 1126, 1136, 1199, 1124, 1128, 1132, 1131, -+ 1131, 1116, 1132, 1119, 1119, 1113, 1133, 1135, 1109, 1122, -+ 1107, 1123, 1122, 1130, 1112, 1119, 1110, 1114, 1199, 1104, -+ -+ 1101, 1094, 97, 106, 0, 1105, 42, 1101, 94, 1108, -+ 1090, 1093, 1096, 1104, 1098, 1091, 1100, 1085, 1199, 0, -+ 1094, 1090, 1099, 1086, 1095, 1093, 1105, 1087, 117, 1102, -+ 0, 1071, 1076, 104, 1088, 1069, 1073, 1093, 1075, 1086, -+ 1069, 1199, 99, 0, 1093, 1079, 1069, 1199, 1065, 1062, -+ 1063, 1076, 121, 125, 0, 1073, 1070, 1059, 1056, 1069, -+ 1061, 1068, 1049, 0, 120, 1056, 1077, 1063, 1062, 131, -+ 1073, 0, 1047, 1059, 1055, 1043, 1056, 0, 1046, 1050, -+ 1044, 1038, 1044, 1036, 1199, 134, 0, 1199, 1035, 0, -+ 1039, 1034, 1046, 1046, 1048, 1031, 1199, 0, 1030, 0, -+ -+ 1027, 1035, 1199, 1039, 1025, 1033, 0, 1032, 0, 1039, -+ 137, 1018, 1028, 1032, 0, 1031, 0, 1018, 1025, 1015, -+ 0, 1014, 0, 145, 142, 0, 0, 120, 132, 0, -+ 0, 0, 0, 1199, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 1199, 1199, 149, 152, 156, 159, 163, 144, 166, -+ 143, 170, 142, 174, 131, 178, 115, 182, 112, 186, -+ 92, 190, 89, 194, 87, 198, 85, 202, 67, 206, -+ 56, 210, 214, 218, 222, 226, 230, 234, 238, 242, -+ 246, 250, 254, 258, 262, 266, 270, 274, 278, 282, -+ 286, 290, 294, 298, 302, 306, 310, 314, 318, 322, -+ -+ 326, 330, 334, 338, 342, 346, 350, 354, 358, 362, -+ 366, 370, 374, 378, 382, 386, 390, 394, 398, 402, -+ 406, 410, 414, 418, 422, 426, 430, 434, 438, 442, -+ 446, 450, 454, 458, 462, 466, 470, 474, 478, 482, -+ 486, 490, 494, 498, 502, 506, 510, 514, 518, 522, -+ 526, 530, 534, 538, 542, 546, 550, 554, 558, 562, -+ 566, 570, 574, 578, 582, 586, 590, 594, 598, 602, -+ 606, 610, 614, 618, 622, 626, 630, 634, 638, 642, -+ 646, 650, 654, 658, 662, 666, 670, 674, 678, 682, -+ 686, 690, 694, 698, 702, 706, 710, 714, 718, 722, -+ -+ 726, 730, 734, 738, 742, 746, 750, 754, 758, 762, -+ 766, 770, 774, 778, 782, 786, 790, 794, 798, 802, -+ 806, 810, 814, 818, 822, 826, 830, 834, 838, 842, -+ 846, 850, 854, 858, 862, 866, 870, 874, 878, 882, -+ 886, 890, 894, 898, 902, 906, 910, 914, 918, 922, -+ 926, 930, 934, 938, 942, 946, 950, 954, 958, 962, -+ 966, 970, 974, 978, 982, 986, 990, 994, 998, 1002, -+ 1006, 1010, 1014, 1018, 1022, 1026, 1030, 1034, 1038 -+ } ; -+ -+static yyconst flex_int16_t yy_def[680] = -+ { 0, -+ 443, 1, 443, 443, 443, 443, 444, 444, 445, 444, -+ 444, 444, 444, 444, 444, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 446, 446, 447, -+ 443, 443, 443, 446, 443, 443, 446, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 448, 448, 447, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ -+ 443, 443, 443, 443, 449, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 450, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 451, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 452, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 453, 443, 443, 443, 443, 443, -+ 443, 443, 443, 454, 443, 443, 443, 443, 443, 443, -+ 443, 455, 443, 443, 443, 443, 443, 456, 443, 443, -+ 443, 443, 443, 443, 443, 443, 457, 443, 443, 458, -+ 443, 443, 443, 443, 443, 443, 443, 459, 443, 460, -+ -+ 443, 443, 443, 443, 443, 443, 461, 443, 462, 443, -+ 443, 443, 443, 443, 463, 443, 464, 443, 443, 443, -+ 465, 443, 466, 443, 443, 467, 468, 443, 443, 469, -+ 470, 471, 472, 443, 473, 474, 475, 476, 477, 478, -+ 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, -+ 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, -+ 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, -+ 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, -+ 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, -+ 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, -+ -+ 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, -+ 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, -+ 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, -+ 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, -+ 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, -+ 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, -+ 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, -+ 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, -+ 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, -+ 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, -+ -+ 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, -+ 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, -+ 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, -+ 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, -+ 679, 443, 0, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443 -+ } ; -+ -+static yyconst flex_int16_t yy_nxt[1235] = -+ { 0, -+ 4, 5, 6, 4, 4, 7, 7, 7, 4, 8, -+ 4, 9, 10, 11, 12, 13, 14, 15, 16, 4, -+ 17, 4, 18, 4, 19, 4, 20, 4, 21, 22, -+ 23, 24, 25, 4, 4, 26, 26, 26, 26, 27, -+ 28, 28, 28, 443, 443, 443, 443, 443, 443, 42, -+ 86, 443, 26, 26, 133, 443, 34, 87, 43, 234, -+ 35, 36, 32, 37, 41, 33, 38, 39, 134, 31, -+ 232, 73, 40, 44, 66, 66, 66, 27, 67, 67, -+ 67, 45, 46, 76, 103, 104, 104, 104, 230, 47, -+ 226, 48, 221, 49, 50, 215, 51, 52, 57, 89, -+ -+ 58, 59, 129, 129, 129, 90, 60, 158, 61, 91, -+ 103, 130, 130, 130, 135, 207, 159, 62, 198, 162, -+ 136, 153, 154, 154, 154, 163, 170, 170, 170, 153, -+ 171, 171, 171, 179, 187, 180, 186, 186, 186, 197, -+ 197, 197, 203, 203, 203, 172, 155, 131, 188, 188, -+ 181, 29, 29, 30, 30, 30, 229, 30, 68, 68, -+ 69, 69, 69, 228, 69, 105, 105, 144, 144, 144, -+ 144, 164, 164, 164, 164, 178, 178, 178, 178, 190, -+ 190, 190, 190, 200, 200, 200, 200, 209, 209, 209, -+ 209, 217, 217, 217, 217, 223, 223, 223, 223, 227, -+ -+ 227, 227, 227, 231, 231, 231, 231, 233, 233, 233, -+ 233, 235, 235, 235, 235, 236, 236, 236, 236, 237, -+ 237, 237, 237, 238, 238, 238, 238, 239, 239, 239, -+ 239, 240, 240, 240, 240, 241, 241, 241, 241, 242, -+ 242, 242, 242, 243, 243, 243, 243, 244, 244, 244, -+ 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, -+ 247, 247, 247, 248, 248, 248, 248, 249, 249, 249, -+ 249, 250, 250, 250, 250, 251, 251, 251, 251, 252, -+ 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, -+ 254, 255, 255, 255, 255, 256, 256, 256, 256, 257, -+ -+ 257, 257, 257, 258, 258, 258, 258, 259, 259, 259, -+ 259, 260, 260, 260, 260, 261, 261, 261, 261, 262, -+ 262, 262, 262, 263, 263, 263, 263, 264, 264, 264, -+ 264, 265, 265, 265, 265, 266, 266, 266, 266, 267, -+ 267, 267, 267, 268, 268, 268, 268, 269, 269, 269, -+ 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, -+ 272, 272, 272, 273, 273, 273, 273, 274, 274, 274, -+ 274, 275, 275, 275, 275, 276, 276, 276, 276, 277, -+ 277, 277, 277, 278, 278, 278, 278, 279, 279, 279, -+ 279, 280, 280, 280, 280, 281, 281, 281, 281, 282, -+ -+ 282, 282, 282, 283, 283, 283, 283, 284, 284, 284, -+ 284, 285, 285, 285, 285, 286, 286, 286, 286, 287, -+ 287, 287, 287, 288, 288, 288, 288, 289, 289, 289, -+ 289, 290, 290, 290, 290, 291, 291, 291, 291, 292, -+ 292, 292, 292, 293, 293, 293, 293, 294, 294, 294, -+ 294, 295, 295, 295, 295, 296, 296, 296, 296, 297, -+ 297, 297, 297, 298, 298, 298, 298, 299, 299, 299, -+ 299, 300, 300, 300, 300, 301, 301, 301, 301, 302, -+ 302, 302, 302, 303, 303, 303, 303, 304, 304, 304, -+ 304, 305, 305, 305, 305, 306, 306, 306, 306, 307, -+ -+ 307, 307, 307, 308, 308, 308, 308, 309, 309, 309, -+ 309, 310, 310, 310, 310, 311, 311, 311, 311, 312, -+ 312, 312, 312, 313, 313, 313, 313, 314, 314, 314, -+ 314, 315, 315, 315, 315, 316, 316, 316, 316, 317, -+ 317, 317, 317, 318, 318, 318, 318, 319, 319, 319, -+ 319, 320, 320, 320, 320, 321, 321, 321, 321, 322, -+ 322, 322, 322, 323, 323, 323, 323, 324, 324, 324, -+ 324, 325, 325, 325, 325, 326, 326, 326, 326, 327, -+ 327, 327, 327, 328, 328, 328, 328, 329, 329, 329, -+ 329, 330, 330, 330, 330, 331, 331, 331, 331, 332, -+ -+ 332, 332, 332, 333, 333, 333, 333, 334, 334, 334, -+ 334, 335, 335, 335, 335, 336, 336, 336, 336, 337, -+ 337, 337, 337, 338, 338, 338, 338, 339, 339, 339, -+ 339, 340, 340, 340, 340, 341, 341, 341, 341, 342, -+ 342, 342, 342, 343, 343, 343, 343, 344, 344, 344, -+ 344, 345, 345, 345, 345, 346, 346, 346, 346, 347, -+ 347, 347, 347, 348, 348, 348, 348, 349, 349, 349, -+ 349, 350, 350, 350, 350, 351, 351, 351, 351, 352, -+ 352, 352, 352, 353, 353, 353, 353, 354, 354, 354, -+ 354, 355, 355, 355, 355, 356, 356, 356, 356, 357, -+ -+ 357, 357, 357, 358, 358, 358, 358, 359, 359, 359, -+ 359, 360, 360, 360, 360, 361, 361, 361, 361, 362, -+ 362, 362, 362, 363, 363, 363, 363, 364, 364, 364, -+ 364, 365, 365, 365, 365, 366, 366, 366, 366, 367, -+ 367, 367, 367, 368, 368, 368, 368, 369, 369, 369, -+ 369, 370, 370, 370, 370, 371, 371, 371, 371, 372, -+ 372, 372, 372, 373, 373, 373, 373, 374, 374, 374, -+ 374, 375, 375, 375, 375, 376, 376, 376, 376, 377, -+ 377, 377, 377, 378, 378, 378, 378, 379, 379, 379, -+ 379, 380, 380, 380, 380, 381, 381, 381, 381, 382, -+ -+ 382, 382, 382, 383, 383, 383, 383, 384, 384, 384, -+ 384, 385, 385, 385, 385, 386, 386, 386, 386, 387, -+ 387, 387, 387, 388, 388, 388, 388, 389, 389, 389, -+ 389, 390, 390, 390, 390, 391, 391, 391, 391, 392, -+ 392, 392, 392, 393, 393, 393, 393, 394, 394, 394, -+ 394, 395, 395, 395, 395, 396, 396, 396, 396, 397, -+ 397, 397, 397, 398, 398, 398, 398, 399, 399, 399, -+ 399, 400, 400, 400, 400, 401, 401, 401, 401, 402, -+ 402, 402, 402, 403, 403, 403, 403, 404, 404, 404, -+ 404, 405, 405, 405, 405, 406, 406, 406, 406, 407, -+ -+ 407, 407, 407, 408, 408, 408, 408, 409, 409, 409, -+ 409, 410, 410, 410, 410, 411, 411, 411, 411, 412, -+ 412, 412, 412, 413, 413, 413, 413, 414, 414, 414, -+ 414, 415, 415, 415, 415, 416, 416, 416, 416, 417, -+ 417, 417, 417, 418, 418, 418, 418, 419, 419, 419, -+ 419, 420, 420, 420, 420, 421, 421, 421, 421, 422, -+ 422, 422, 422, 423, 423, 423, 423, 424, 424, 424, -+ 424, 425, 425, 425, 425, 426, 426, 426, 426, 427, -+ 427, 427, 427, 428, 428, 428, 428, 429, 429, 429, -+ 429, 430, 430, 430, 430, 431, 431, 431, 431, 432, -+ -+ 432, 432, 432, 433, 433, 433, 433, 434, 434, 434, -+ 434, 435, 435, 435, 435, 436, 436, 436, 436, 437, -+ 437, 437, 437, 438, 438, 438, 438, 439, 439, 439, -+ 439, 440, 440, 440, 440, 441, 441, 441, 441, 442, -+ 442, 442, 442, 99, 99, 225, 224, 222, 220, 99, -+ 219, 218, 216, 214, 213, 212, 211, 210, 208, 206, -+ 205, 204, 203, 202, 201, 199, 196, 195, 194, 193, -+ 192, 191, 99, 59, 188, 189, 188, 153, 185, 184, -+ 183, 182, 99, 99, 177, 176, 175, 174, 173, 99, -+ 169, 168, 167, 99, 166, 99, 165, 99, 161, 160, -+ -+ 119, 99, 99, 99, 157, 156, 103, 152, 151, 150, -+ 149, 148, 147, 146, 145, 99, 99, 143, 142, 141, -+ 140, 139, 138, 137, 59, 132, 128, 127, 126, 125, -+ 70, 70, 124, 123, 70, 122, 99, 99, 121, 120, -+ 119, 118, 117, 99, 116, 115, 114, 113, 112, 59, -+ 111, 110, 109, 108, 107, 106, 443, 27, 102, 70, -+ 101, 100, 99, 98, 97, 96, 95, 70, 94, 93, -+ 92, 88, 85, 84, 70, 83, 70, 82, 81, 80, -+ 79, 78, 77, 75, 74, 72, 71, 70, 443, 65, -+ 64, 63, 56, 55, 54, 53, 443, 443, 3, 443, -+ -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443 -+ } ; -+ -+static yyconst flex_int16_t yy_chk[1235] = -+ { 0, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 5, 5, 6, 6, 7, -+ 7, 7, 7, 10, 11, 12, 14, 34, 15, 16, -+ 49, 13, 26, 26, 107, 37, 12, 49, 16, 471, -+ 12, 12, 11, 13, 15, 11, 13, 13, 107, 10, -+ 469, 34, 14, 17, 27, 27, 27, 28, 28, 28, -+ 28, 17, 17, 37, 66, 66, 66, 66, 467, 17, -+ 465, 17, 463, 17, 17, 461, 17, 17, 22, 51, -+ -+ 22, 22, 103, 103, 103, 51, 22, 134, 22, 51, -+ 104, 104, 104, 104, 109, 459, 134, 22, 457, 143, -+ 109, 129, 129, 129, 129, 143, 153, 153, 153, 154, -+ 154, 154, 154, 165, 455, 165, 170, 170, 170, 186, -+ 186, 186, 211, 211, 211, 453, 451, 449, 229, 228, -+ 165, 444, 444, 445, 445, 445, 225, 445, 446, 446, -+ 447, 447, 447, 224, 447, 448, 448, 450, 450, 450, -+ 450, 452, 452, 452, 452, 454, 454, 454, 454, 456, -+ 456, 456, 456, 458, 458, 458, 458, 460, 460, 460, -+ 460, 462, 462, 462, 462, 464, 464, 464, 464, 466, -+ -+ 466, 466, 466, 468, 468, 468, 468, 470, 470, 470, -+ 470, 472, 472, 472, 472, 473, 473, 473, 473, 474, -+ 474, 474, 474, 475, 475, 475, 475, 476, 476, 476, -+ 476, 477, 477, 477, 477, 478, 478, 478, 478, 479, -+ 479, 479, 479, 480, 480, 480, 480, 481, 481, 481, -+ 481, 482, 482, 482, 482, 483, 483, 483, 483, 484, -+ 484, 484, 484, 485, 485, 485, 485, 486, 486, 486, -+ 486, 487, 487, 487, 487, 488, 488, 488, 488, 489, -+ 489, 489, 489, 490, 490, 490, 490, 491, 491, 491, -+ 491, 492, 492, 492, 492, 493, 493, 493, 493, 494, -+ -+ 494, 494, 494, 495, 495, 495, 495, 496, 496, 496, -+ 496, 497, 497, 497, 497, 498, 498, 498, 498, 499, -+ 499, 499, 499, 500, 500, 500, 500, 501, 501, 501, -+ 501, 502, 502, 502, 502, 503, 503, 503, 503, 504, -+ 504, 504, 504, 505, 505, 505, 505, 506, 506, 506, -+ 506, 507, 507, 507, 507, 508, 508, 508, 508, 509, -+ 509, 509, 509, 510, 510, 510, 510, 511, 511, 511, -+ 511, 512, 512, 512, 512, 513, 513, 513, 513, 514, -+ 514, 514, 514, 515, 515, 515, 515, 516, 516, 516, -+ 516, 517, 517, 517, 517, 518, 518, 518, 518, 519, -+ -+ 519, 519, 519, 520, 520, 520, 520, 521, 521, 521, -+ 521, 522, 522, 522, 522, 523, 523, 523, 523, 524, -+ 524, 524, 524, 525, 525, 525, 525, 526, 526, 526, -+ 526, 527, 527, 527, 527, 528, 528, 528, 528, 529, -+ 529, 529, 529, 530, 530, 530, 530, 531, 531, 531, -+ 531, 532, 532, 532, 532, 533, 533, 533, 533, 534, -+ 534, 534, 534, 535, 535, 535, 535, 536, 536, 536, -+ 536, 537, 537, 537, 537, 538, 538, 538, 538, 539, -+ 539, 539, 539, 540, 540, 540, 540, 541, 541, 541, -+ 541, 542, 542, 542, 542, 543, 543, 543, 543, 544, -+ -+ 544, 544, 544, 545, 545, 545, 545, 546, 546, 546, -+ 546, 547, 547, 547, 547, 548, 548, 548, 548, 549, -+ 549, 549, 549, 550, 550, 550, 550, 551, 551, 551, -+ 551, 552, 552, 552, 552, 553, 553, 553, 553, 554, -+ 554, 554, 554, 555, 555, 555, 555, 556, 556, 556, -+ 556, 557, 557, 557, 557, 558, 558, 558, 558, 559, -+ 559, 559, 559, 560, 560, 560, 560, 561, 561, 561, -+ 561, 562, 562, 562, 562, 563, 563, 563, 563, 564, -+ 564, 564, 564, 565, 565, 565, 565, 566, 566, 566, -+ 566, 567, 567, 567, 567, 568, 568, 568, 568, 569, -+ -+ 569, 569, 569, 570, 570, 570, 570, 571, 571, 571, -+ 571, 572, 572, 572, 572, 573, 573, 573, 573, 574, -+ 574, 574, 574, 575, 575, 575, 575, 576, 576, 576, -+ 576, 577, 577, 577, 577, 578, 578, 578, 578, 579, -+ 579, 579, 579, 580, 580, 580, 580, 581, 581, 581, -+ 581, 582, 582, 582, 582, 583, 583, 583, 583, 584, -+ 584, 584, 584, 585, 585, 585, 585, 586, 586, 586, -+ 586, 587, 587, 587, 587, 588, 588, 588, 588, 589, -+ 589, 589, 589, 590, 590, 590, 590, 591, 591, 591, -+ 591, 592, 592, 592, 592, 593, 593, 593, 593, 594, -+ -+ 594, 594, 594, 595, 595, 595, 595, 596, 596, 596, -+ 596, 597, 597, 597, 597, 598, 598, 598, 598, 599, -+ 599, 599, 599, 600, 600, 600, 600, 601, 601, 601, -+ 601, 602, 602, 602, 602, 603, 603, 603, 603, 604, -+ 604, 604, 604, 605, 605, 605, 605, 606, 606, 606, -+ 606, 607, 607, 607, 607, 608, 608, 608, 608, 609, -+ 609, 609, 609, 610, 610, 610, 610, 611, 611, 611, -+ 611, 612, 612, 612, 612, 613, 613, 613, 613, 614, -+ 614, 614, 614, 615, 615, 615, 615, 616, 616, 616, -+ 616, 617, 617, 617, 617, 618, 618, 618, 618, 619, -+ -+ 619, 619, 619, 620, 620, 620, 620, 621, 621, 621, -+ 621, 622, 622, 622, 622, 623, 623, 623, 623, 624, -+ 624, 624, 624, 625, 625, 625, 625, 626, 626, 626, -+ 626, 627, 627, 627, 627, 628, 628, 628, 628, 629, -+ 629, 629, 629, 630, 630, 630, 630, 631, 631, 631, -+ 631, 632, 632, 632, 632, 633, 633, 633, 633, 634, -+ 634, 634, 634, 635, 635, 635, 635, 636, 636, 636, -+ 636, 637, 637, 637, 637, 638, 638, 638, 638, 639, -+ 639, 639, 639, 640, 640, 640, 640, 641, 641, 641, -+ 641, 642, 642, 642, 642, 643, 643, 643, 643, 644, -+ -+ 644, 644, 644, 645, 645, 645, 645, 646, 646, 646, -+ 646, 647, 647, 647, 647, 648, 648, 648, 648, 649, -+ 649, 649, 649, 650, 650, 650, 650, 651, 651, 651, -+ 651, 652, 652, 652, 652, 653, 653, 653, 653, 654, -+ 654, 654, 654, 655, 655, 655, 655, 656, 656, 656, -+ 656, 657, 657, 657, 657, 658, 658, 658, 658, 659, -+ 659, 659, 659, 660, 660, 660, 660, 661, 661, 661, -+ 661, 662, 662, 662, 662, 663, 663, 663, 663, 664, -+ 664, 664, 664, 665, 665, 665, 665, 666, 666, 666, -+ 666, 667, 667, 667, 667, 668, 668, 668, 668, 669, -+ -+ 669, 669, 669, 670, 670, 670, 670, 671, 671, 671, -+ 671, 672, 672, 672, 672, 673, 673, 673, 673, 674, -+ 674, 674, 674, 675, 675, 675, 675, 676, 676, 676, -+ 676, 677, 677, 677, 677, 678, 678, 678, 678, 679, -+ 679, 679, 679, 222, 220, 219, 218, 216, 214, 213, -+ 212, 210, 208, 206, 205, 204, 202, 201, 199, 196, -+ 195, 194, 193, 192, 191, 189, 184, 183, 182, 181, -+ 180, 179, 177, 176, 175, 174, 173, 171, 169, 168, -+ 167, 166, 163, 162, 161, 160, 159, 158, 157, 156, -+ 152, 151, 150, 149, 147, 146, 145, 141, 140, 139, -+ -+ 138, 137, 136, 135, 133, 132, 130, 128, 127, 126, -+ 125, 124, 123, 122, 121, 118, 117, 116, 115, 114, -+ 113, 112, 111, 110, 108, 106, 102, 101, 100, 98, -+ 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, -+ 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, -+ 77, 75, 74, 73, 72, 71, 68, 67, 65, 64, -+ 63, 62, 61, 60, 58, 57, 56, 55, 54, 53, -+ 52, 50, 48, 47, 46, 45, 44, 43, 42, 41, -+ 40, 39, 38, 36, 35, 33, 32, 31, 29, 25, -+ 24, 23, 21, 20, 19, 18, 8, 3, 443, 443, -+ -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, -+ 443, 443, 443, 443 -+ } ; -+ -+static yy_state_type yy_last_accepting_state; -+static char *yy_last_accepting_cpos; -+ -+extern int yy_flex_debug; -+int yy_flex_debug = 0; -+ -+/* The intent behind this definition is that it'll catch -+ * any uses of REJECT which flex missed. -+ */ -+#define REJECT reject_used_but_not_detected -+#define yymore() yymore_used_but_not_detected -+#define YY_MORE_ADJ 0 -+#define YY_RESTORE_YY_MORE_OFFSET -+#ifndef YYLMAX -+#define YYLMAX 2048 -+#endif -+ -+char yytext[YYLMAX]; -+char *yytext_ptr; -+#line 1 "prom_lex.l" -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * -+ * 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, see . -+ */ -+/* definitions */ -+#line 23 "prom_lex.l" -+#include "prom_parse.h" -+ -+#undef LEXDEBUG -+#ifdef LEXDEBUG -+#define dbg(a) dbgprint((a)) -+#else -+#define dbg(a) do {} while (0) -+#endif /* LEXDEBUG */ -+ -+#define upval(d) \ -+ dbg(#d); \ -+ yylval.str[0] = 0; \ -+ strcat(yylval.str, yytext); \ -+ yylloc.first_column = yylloc.last_column; \ -+ yylloc.last_column += yyleng; \ -+ return d -+ -+void dbgprint(const char *item) { fprintf(stderr, "%s: \"%s\" len=%d ", item, yytext, yyleng);} -+ -+/* CHOSEN uses only boot related paths. */ -+#line 969 "" -+ -+#define INITIAL 0 -+ -+#ifndef YY_NO_UNISTD_H -+/* Special case for "unistd.h", since it is non-ANSI. We include it way -+ * down here because we want the user's section 1 to have been scanned first. -+ * The user has a chance to override it with an option. -+ */ -+#include -+#endif -+ -+#ifndef YY_EXTRA_TYPE -+#define YY_EXTRA_TYPE void * -+#endif -+ -+static int yy_init_globals (void ); -+ -+/* Macros after this point can all be overridden by user definitions in -+ * section 1. -+ */ -+ -+#ifndef YY_SKIP_YYWRAP -+#ifdef __cplusplus -+extern "C" int yywrap (void ); -+#else -+extern int yywrap (void ); -+#endif -+#endif -+ -+ static void yyunput (int c,char *buf_ptr ); -+ -+#ifndef yytext_ptr -+static void yy_flex_strncpy (char *,yyconst char *,int ); -+#endif -+ -+#ifdef YY_NEED_STRLEN -+static int yy_flex_strlen (yyconst char * ); -+#endif -+ -+#ifndef YY_NO_INPUT -+ -+#ifdef __cplusplus -+static int yyinput (void ); -+#else -+static int input (void ); -+#endif -+ -+#endif -+ -+/* Amount of stuff to slurp up with each read. */ -+#ifndef YY_READ_BUF_SIZE -+#define YY_READ_BUF_SIZE 8192 -+#endif -+ -+/* Copy whatever the last rule matched to the standard output. */ -+#ifndef ECHO -+/* This used to be an fputs(), but since the string might contain NUL's, -+ * we now use fwrite(). -+ */ -+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) -+#endif -+ -+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, -+ * is returned in "result". -+ */ -+#ifndef YY_INPUT -+#define YY_INPUT(buf,result,max_size) \ -+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ -+ { \ -+ int c = '*'; \ -+ size_t n; \ -+ for ( n = 0; n < max_size && \ -+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ -+ buf[n] = (char) c; \ -+ if ( c == '\n' ) \ -+ buf[n++] = (char) c; \ -+ if ( c == EOF && ferror( yyin ) ) \ -+ YY_FATAL_ERROR( "input in flex scanner failed" ); \ -+ result = n; \ -+ } \ -+ else \ -+ { \ -+ errno=0; \ -+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ -+ { \ -+ if( errno != EINTR) \ -+ { \ -+ YY_FATAL_ERROR( "input in flex scanner failed" ); \ -+ break; \ -+ } \ -+ errno=0; \ -+ clearerr(yyin); \ -+ } \ -+ }\ -+\ -+ -+#endif -+ -+/* No semi-colon after return; correct usage is to write "yyterminate();" - -+ * we don't want an extra ';' after the "return" because that will cause -+ * some compilers to complain about unreachable statements. -+ */ -+#ifndef yyterminate -+#define yyterminate() return YY_NULL -+#endif -+ -+/* Number of entries by which start-condition stack grows. */ -+#ifndef YY_START_STACK_INCR -+#define YY_START_STACK_INCR 25 -+#endif -+ -+/* Report a fatal error. */ -+#ifndef YY_FATAL_ERROR -+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) -+#endif -+ -+/* end tables serialization structures and prototypes */ -+ -+/* Default declaration of generated scanner - a define so the user can -+ * easily add parameters. -+ */ -+#ifndef YY_DECL -+#define YY_DECL_IS_OURS 1 -+ -+extern int yylex (void); -+ -+#define YY_DECL int yylex (void) -+#endif /* !YY_DECL */ -+ -+/* Code executed at the beginning of each rule, after yytext and yyleng -+ * have been set up. -+ */ -+#ifndef YY_USER_ACTION -+#define YY_USER_ACTION -+#endif -+ -+/* Code executed at the end of each rule. */ -+#ifndef YY_BREAK -+#define YY_BREAK break; -+#endif -+ -+#define YY_RULE_SETUP \ -+ YY_USER_ACTION -+ -+/** The main scanner function which does all the work. -+ */ -+YY_DECL -+{ -+ register yy_state_type yy_current_state; -+ register char *yy_cp, *yy_bp; -+ register int yy_act; -+ -+#line 63 "prom_lex.l" -+ -+ -+#line 1125 "" -+ -+ if ( !(yy_init) ) -+ { -+ (yy_init) = 1; -+ -+#ifdef YY_USER_INIT -+ YY_USER_INIT; -+#endif -+ -+ if ( ! (yy_start) ) -+ (yy_start) = 1; /* first start state */ -+ -+ if ( ! yyin ) -+ yyin = stdin; -+ -+ if ( ! yyout ) -+ yyout = stdout; -+ -+ if ( ! YY_CURRENT_BUFFER ) { -+ yyensure_buffer_stack (); -+ YY_CURRENT_BUFFER_LVALUE = -+ yy_create_buffer(yyin,YY_BUF_SIZE ); -+ } -+ -+ yy_load_buffer_state( ); -+ } -+ -+ while ( 1 ) /* loops until end-of-file is reached */ -+ { -+ yy_cp = (yy_c_buf_p); -+ -+ /* Support of yytext. */ -+ *yy_cp = (yy_hold_char); -+ -+ /* yy_bp points to the position in yy_ch_buf of the start of -+ * the current run. -+ */ -+ yy_bp = yy_cp; -+ -+ yy_current_state = (yy_start); -+yy_match: -+ do -+ { -+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; -+ if ( yy_accept[yy_current_state] ) -+ { -+ (yy_last_accepting_state) = yy_current_state; -+ (yy_last_accepting_cpos) = yy_cp; -+ } -+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) -+ { -+ yy_current_state = (int) yy_def[yy_current_state]; -+ if ( yy_current_state >= 444 ) -+ yy_c = yy_meta[(unsigned int) yy_c]; -+ } -+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; -+ ++yy_cp; -+ } -+ while ( yy_current_state != 443 ); -+ yy_cp = (yy_last_accepting_cpos); -+ yy_current_state = (yy_last_accepting_state); -+ -+yy_find_action: -+ yy_act = yy_accept[yy_current_state]; -+ -+ YY_DO_BEFORE_ACTION; -+ -+do_action: /* This label is used only to access EOF actions. */ -+ -+ switch ( yy_act ) -+ { /* beginning of action switch */ -+ case 0: /* must back up */ -+ /* undo the effects of YY_DO_BEFORE_ACTION */ -+ *yy_cp = (yy_hold_char); -+ yy_cp = (yy_last_accepting_cpos); -+ yy_current_state = (yy_last_accepting_state); -+ goto yy_find_action; -+ -+case 1: -+YY_RULE_SETUP -+#line 65 "prom_lex.l" -+{ upval(CHOSEN); } -+ YY_BREAK -+case 2: -+YY_RULE_SETUP -+#line 66 "prom_lex.l" -+{ upval(VDEVICE); } -+ YY_BREAK -+case 3: -+YY_RULE_SETUP -+#line 67 "prom_lex.l" -+{ upval(VDEVINST); } -+ YY_BREAK -+case 4: -+YY_RULE_SETUP -+#line 68 "prom_lex.l" -+{ upval(VDEVDEV); } -+ YY_BREAK -+case 5: -+YY_RULE_SETUP -+#line 69 "prom_lex.l" -+{ upval(VDEVRAW); } -+ YY_BREAK -+case 6: -+YY_RULE_SETUP -+#line 70 "prom_lex.l" -+{ upval(OBPQUAL); } -+ YY_BREAK -+case 7: -+YY_RULE_SETUP -+#line 71 "prom_lex.l" -+{ upval(BUSNAME); } -+ YY_BREAK -+case 8: -+YY_RULE_SETUP -+#line 72 "prom_lex.l" -+{ upval(IPV4); } -+ YY_BREAK -+case 9: -+YY_RULE_SETUP -+#line 73 "prom_lex.l" -+{ upval(IQN); } -+ YY_BREAK -+case 10: -+YY_RULE_SETUP -+#line 74 "prom_lex.l" -+{ upval(BOOTDEV); } -+ YY_BREAK -+case 11: -+YY_RULE_SETUP -+#line 75 "prom_lex.l" -+{ upval(OBPPARM); } -+ YY_BREAK -+case 12: -+YY_RULE_SETUP -+#line 76 "prom_lex.l" -+{ upval(HEX4); } -+ YY_BREAK -+case 13: -+YY_RULE_SETUP -+#line 77 "prom_lex.l" -+{ upval(HEX16); } -+ YY_BREAK -+case 14: -+YY_RULE_SETUP -+#line 78 "prom_lex.l" -+{ upval(FILENAME); } -+ YY_BREAK -+case 15: -+/* rule 15 can match eol */ -+YY_RULE_SETUP -+#line 79 "prom_lex.l" -+{ /* eat all whitespace. */ -+ yylloc.first_column = yylloc.last_column; -+ yylloc.last_column += yyleng; -+} -+ YY_BREAK -+case 16: -+YY_RULE_SETUP -+#line 83 "prom_lex.l" -+{ /* any other single char. */ -+ dbg("??"); -+ yylloc.first_column = yylloc.last_column; -+ yylloc.last_column += yyleng; -+ return *yytext; -+} -+ YY_BREAK -+case YY_STATE_EOF(INITIAL): -+#line 90 "prom_lex.l" -+yyterminate(); -+ YY_BREAK -+case 17: -+YY_RULE_SETUP -+#line 91 "prom_lex.l" -+ECHO; -+ YY_BREAK -+#line 1302 "" -+ -+ case YY_END_OF_BUFFER: -+ { -+ /* Amount of text matched not including the EOB char. */ -+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; -+ -+ /* Undo the effects of YY_DO_BEFORE_ACTION. */ -+ *yy_cp = (yy_hold_char); -+ YY_RESTORE_YY_MORE_OFFSET -+ -+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) -+ { -+ /* We're scanning a new file or input source. It's -+ * possible that this happened because the user -+ * just pointed yyin at a new source and called -+ * yylex(). If so, then we have to assure -+ * consistency between YY_CURRENT_BUFFER and our -+ * globals. Here is the right place to do so, because -+ * this is the first action (other than possibly a -+ * back-up) that will match for the new input source. -+ */ -+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; -+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; -+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; -+ } -+ -+ /* Note that here we test for yy_c_buf_p "<=" to the position -+ * of the first EOB in the buffer, since yy_c_buf_p will -+ * already have been incremented past the NUL character -+ * (since all states make transitions on EOB to the -+ * end-of-buffer state). Contrast this with the test -+ * in input(). -+ */ -+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) -+ { /* This was really a NUL. */ -+ yy_state_type yy_next_state; -+ -+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; -+ -+ yy_current_state = yy_get_previous_state( ); -+ -+ /* Okay, we're now positioned to make the NUL -+ * transition. We couldn't have -+ * yy_get_previous_state() go ahead and do it -+ * for us because it doesn't know how to deal -+ * with the possibility of jamming (and we don't -+ * want to build jamming into it because then it -+ * will run more slowly). -+ */ -+ -+ yy_next_state = yy_try_NUL_trans( yy_current_state ); -+ -+ yy_bp = (yytext_ptr) + YY_MORE_ADJ; -+ -+ if ( yy_next_state ) -+ { -+ /* Consume the NUL. */ -+ yy_cp = ++(yy_c_buf_p); -+ yy_current_state = yy_next_state; -+ goto yy_match; -+ } -+ -+ else -+ { -+ yy_cp = (yy_last_accepting_cpos); -+ yy_current_state = (yy_last_accepting_state); -+ goto yy_find_action; -+ } -+ } -+ -+ else switch ( yy_get_next_buffer( ) ) -+ { -+ case EOB_ACT_END_OF_FILE: -+ { -+ (yy_did_buffer_switch_on_eof) = 0; -+ -+ if ( yywrap( ) ) -+ { -+ /* Note: because we've taken care in -+ * yy_get_next_buffer() to have set up -+ * yytext, we can now set up -+ * yy_c_buf_p so that if some total -+ * hoser (like flex itself) wants to -+ * call the scanner after we return the -+ * YY_NULL, it'll still work - another -+ * YY_NULL will get returned. -+ */ -+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; -+ -+ yy_act = YY_STATE_EOF(YY_START); -+ goto do_action; -+ } -+ -+ else -+ { -+ if ( ! (yy_did_buffer_switch_on_eof) ) -+ YY_NEW_FILE; -+ } -+ break; -+ } -+ -+ case EOB_ACT_CONTINUE_SCAN: -+ (yy_c_buf_p) = -+ (yytext_ptr) + yy_amount_of_matched_text; -+ -+ yy_current_state = yy_get_previous_state( ); -+ -+ yy_cp = (yy_c_buf_p); -+ yy_bp = (yytext_ptr) + YY_MORE_ADJ; -+ goto yy_match; -+ -+ case EOB_ACT_LAST_MATCH: -+ (yy_c_buf_p) = -+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; -+ -+ yy_current_state = yy_get_previous_state( ); -+ -+ yy_cp = (yy_c_buf_p); -+ yy_bp = (yytext_ptr) + YY_MORE_ADJ; -+ goto yy_find_action; -+ } -+ break; -+ } -+ -+ default: -+ YY_FATAL_ERROR( -+ "fatal flex scanner internal error--no action found" ); -+ } /* end of action switch */ -+ } /* end of scanning one token */ -+} /* end of yylex */ -+ -+/* yy_get_next_buffer - try to read in a new buffer -+ * -+ * Returns a code representing an action: -+ * EOB_ACT_LAST_MATCH - -+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position -+ * EOB_ACT_END_OF_FILE - end of file -+ */ -+static int yy_get_next_buffer (void) -+{ -+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; -+ register char *source = (yytext_ptr); -+ register int number_to_move, i; -+ int ret_val; -+ -+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) -+ YY_FATAL_ERROR( -+ "fatal flex scanner internal error--end of buffer missed" ); -+ -+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) -+ { /* Don't try to fill the buffer, so this is an EOF. */ -+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) -+ { -+ /* We matched a single character, the EOB, so -+ * treat this as a final EOF. -+ */ -+ return EOB_ACT_END_OF_FILE; -+ } -+ -+ else -+ { -+ /* We matched some text prior to the EOB, first -+ * process it. -+ */ -+ return EOB_ACT_LAST_MATCH; -+ } -+ } -+ -+ /* Try to read more data. */ -+ -+ /* First move last chars to start of buffer. */ -+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; -+ -+ for ( i = 0; i < number_to_move; ++i ) -+ *(dest++) = *(source++); -+ -+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) -+ /* don't do the read, it's not guaranteed to return an EOF, -+ * just force an EOF -+ */ -+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; -+ -+ else -+ { -+ int num_to_read = -+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; -+ -+ while ( num_to_read <= 0 ) -+ { /* Not enough room in the buffer - grow it. */ -+ -+ /* just a shorter name for the current buffer */ -+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER; -+ -+ int yy_c_buf_p_offset = -+ (int) ((yy_c_buf_p) - b->yy_ch_buf); -+ -+ if ( b->yy_is_our_buffer ) -+ { -+ int new_size = b->yy_buf_size * 2; -+ -+ if ( new_size <= 0 ) -+ b->yy_buf_size += b->yy_buf_size / 8; -+ else -+ b->yy_buf_size *= 2; -+ -+ b->yy_ch_buf = (char *) -+ /* Include room in for 2 EOB chars. */ -+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); -+ } -+ else -+ /* Can't grow it, we don't own it. */ -+ b->yy_ch_buf = 0; -+ -+ if ( ! b->yy_ch_buf ) -+ YY_FATAL_ERROR( -+ "fatal error - scanner input buffer overflow" ); -+ -+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; -+ -+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - -+ number_to_move - 1; -+ -+ } -+ -+ if ( num_to_read > YY_READ_BUF_SIZE ) -+ num_to_read = YY_READ_BUF_SIZE; -+ -+ /* Read in more data. */ -+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), -+ (yy_n_chars), num_to_read ); -+ -+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); -+ } -+ -+ if ( (yy_n_chars) == 0 ) -+ { -+ if ( number_to_move == YY_MORE_ADJ ) -+ { -+ ret_val = EOB_ACT_END_OF_FILE; -+ yyrestart(yyin ); -+ } -+ -+ else -+ { -+ ret_val = EOB_ACT_LAST_MATCH; -+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = -+ YY_BUFFER_EOF_PENDING; -+ } -+ } -+ -+ else -+ ret_val = EOB_ACT_CONTINUE_SCAN; -+ -+ (yy_n_chars) += number_to_move; -+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; -+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; -+ -+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; -+ -+ return ret_val; -+} -+ -+/* yy_get_previous_state - get the state just before the EOB char was reached */ -+ -+ static yy_state_type yy_get_previous_state (void) -+{ -+ register yy_state_type yy_current_state; -+ register char *yy_cp; -+ -+ yy_current_state = (yy_start); -+ -+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) -+ { -+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); -+ if ( yy_accept[yy_current_state] ) -+ { -+ (yy_last_accepting_state) = yy_current_state; -+ (yy_last_accepting_cpos) = yy_cp; -+ } -+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) -+ { -+ yy_current_state = (int) yy_def[yy_current_state]; -+ if ( yy_current_state >= 444 ) -+ yy_c = yy_meta[(unsigned int) yy_c]; -+ } -+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; -+ } -+ -+ return yy_current_state; -+} -+ -+/* yy_try_NUL_trans - try to make a transition on the NUL character -+ * -+ * synopsis -+ * next_state = yy_try_NUL_trans( current_state ); -+ */ -+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) -+{ -+ register int yy_is_jam; -+ register char *yy_cp = (yy_c_buf_p); -+ -+ register YY_CHAR yy_c = 1; -+ if ( yy_accept[yy_current_state] ) -+ { -+ (yy_last_accepting_state) = yy_current_state; -+ (yy_last_accepting_cpos) = yy_cp; -+ } -+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) -+ { -+ yy_current_state = (int) yy_def[yy_current_state]; -+ if ( yy_current_state >= 444 ) -+ yy_c = yy_meta[(unsigned int) yy_c]; -+ } -+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; -+ yy_is_jam = (yy_current_state == 443); -+ -+ return yy_is_jam ? 0 : yy_current_state; -+} -+ -+ static void yyunput (int c, register char * yy_bp ) -+{ -+ register char *yy_cp; -+ -+ yy_cp = (yy_c_buf_p); -+ -+ /* undo effects of setting up yytext */ -+ *yy_cp = (yy_hold_char); -+ -+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) -+ { /* need to shift things up to make room */ -+ /* +2 for EOB chars. */ -+ register int number_to_move = (yy_n_chars) + 2; -+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ -+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; -+ register char *source = -+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; -+ -+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) -+ *--dest = *--source; -+ -+ yy_cp += (int) (dest - source); -+ yy_bp += (int) (dest - source); -+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = -+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; -+ -+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) -+ YY_FATAL_ERROR( "flex scanner push-back overflow" ); -+ } -+ -+ *--yy_cp = (char) c; -+ -+ (yytext_ptr) = yy_bp; -+ (yy_hold_char) = *yy_cp; -+ (yy_c_buf_p) = yy_cp; -+} -+ -+#ifndef YY_NO_INPUT -+#ifdef __cplusplus -+ static int yyinput (void) -+#else -+ static int input (void) -+#endif -+ -+{ -+ int c; -+ -+ *(yy_c_buf_p) = (yy_hold_char); -+ -+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) -+ { -+ /* yy_c_buf_p now points to the character we want to return. -+ * If this occurs *before* the EOB characters, then it's a -+ * valid NUL; if not, then we've hit the end of the buffer. -+ */ -+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) -+ /* This was really a NUL. */ -+ *(yy_c_buf_p) = '\0'; -+ -+ else -+ { /* need more input */ -+ int offset = (yy_c_buf_p) - (yytext_ptr); -+ ++(yy_c_buf_p); -+ -+ switch ( yy_get_next_buffer( ) ) -+ { -+ case EOB_ACT_LAST_MATCH: -+ /* This happens because yy_g_n_b() -+ * sees that we've accumulated a -+ * token and flags that we need to -+ * try matching the token before -+ * proceeding. But for input(), -+ * there's no matching to consider. -+ * So convert the EOB_ACT_LAST_MATCH -+ * to EOB_ACT_END_OF_FILE. -+ */ -+ -+ /* Reset buffer status. */ -+ yyrestart(yyin ); -+ -+ /*FALLTHROUGH*/ -+ -+ case EOB_ACT_END_OF_FILE: -+ { -+ if ( yywrap( ) ) -+ return EOF; -+ -+ if ( ! (yy_did_buffer_switch_on_eof) ) -+ YY_NEW_FILE; -+#ifdef __cplusplus -+ return yyinput(); -+#else -+ return input(); -+#endif -+ } -+ -+ case EOB_ACT_CONTINUE_SCAN: -+ (yy_c_buf_p) = (yytext_ptr) + offset; -+ break; -+ } -+ } -+ } -+ -+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ -+ *(yy_c_buf_p) = '\0'; /* preserve yytext */ -+ (yy_hold_char) = *++(yy_c_buf_p); -+ -+ return c; -+} -+#endif /* ifndef YY_NO_INPUT */ -+ -+/** Immediately switch to a different input stream. -+ * @param input_file A readable stream. -+ * -+ * @note This function does not reset the start condition to @c INITIAL . -+ */ -+ void yyrestart (FILE * input_file ) -+{ -+ -+ if ( ! YY_CURRENT_BUFFER ){ -+ yyensure_buffer_stack (); -+ YY_CURRENT_BUFFER_LVALUE = -+ yy_create_buffer(yyin,YY_BUF_SIZE ); -+ } -+ -+ yy_init_buffer(YY_CURRENT_BUFFER,input_file ); -+ yy_load_buffer_state( ); -+} -+ -+/** Switch to a different input buffer. -+ * @param new_buffer The new input buffer. -+ * -+ */ -+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) -+{ -+ -+ /* TODO. We should be able to replace this entire function body -+ * with -+ * yypop_buffer_state(); -+ * yypush_buffer_state(new_buffer); -+ */ -+ yyensure_buffer_stack (); -+ if ( YY_CURRENT_BUFFER == new_buffer ) -+ return; -+ -+ if ( YY_CURRENT_BUFFER ) -+ { -+ /* Flush out information for old buffer. */ -+ *(yy_c_buf_p) = (yy_hold_char); -+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); -+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); -+ } -+ -+ YY_CURRENT_BUFFER_LVALUE = new_buffer; -+ yy_load_buffer_state( ); -+ -+ /* We don't actually know whether we did this switch during -+ * EOF (yywrap()) processing, but the only time this flag -+ * is looked at is after yywrap() is called, so it's safe -+ * to go ahead and always set it. -+ */ -+ (yy_did_buffer_switch_on_eof) = 1; -+} -+ -+static void yy_load_buffer_state (void) -+{ -+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; -+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; -+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; -+ (yy_hold_char) = *(yy_c_buf_p); -+} -+ -+/** Allocate and initialize an input buffer state. -+ * @param file A readable stream. -+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. -+ * -+ * @return the allocated buffer state. -+ */ -+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) -+{ -+ YY_BUFFER_STATE b; -+ -+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); -+ if ( ! b ) -+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); -+ -+ b->yy_buf_size = size; -+ -+ /* yy_ch_buf has to be 2 characters longer than the size given because -+ * we need to put in 2 end-of-buffer characters. -+ */ -+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ); -+ if ( ! b->yy_ch_buf ) -+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); -+ -+ b->yy_is_our_buffer = 1; -+ -+ yy_init_buffer(b,file ); -+ -+ return b; -+} -+ -+/** Destroy the buffer. -+ * @param b a buffer created with yy_create_buffer() -+ * -+ */ -+ void yy_delete_buffer (YY_BUFFER_STATE b ) -+{ -+ -+ if ( ! b ) -+ return; -+ -+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ -+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; -+ -+ if ( b->yy_is_our_buffer ) -+ yyfree((void *) b->yy_ch_buf ); -+ -+ yyfree((void *) b ); -+} -+ -+/* Initializes or reinitializes a buffer. -+ * This function is sometimes called more than once on the same buffer, -+ * such as during a yyrestart() or at EOF. -+ */ -+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) -+ -+{ -+ int oerrno = errno; -+ -+ yy_flush_buffer(b ); -+ -+ b->yy_input_file = file; -+ b->yy_fill_buffer = 1; -+ -+ /* If b is the current buffer, then yy_init_buffer was _probably_ -+ * called from yyrestart() or through yy_get_next_buffer. -+ * In that case, we don't want to reset the lineno or column. -+ */ -+ if (b != YY_CURRENT_BUFFER){ -+ b->yy_bs_lineno = 1; -+ b->yy_bs_column = 0; -+ } -+ -+ b->yy_is_interactive = 0; -+ -+ errno = oerrno; -+} -+ -+/** Discard all buffered characters. On the next scan, YY_INPUT will be called. -+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. -+ * -+ */ -+ void yy_flush_buffer (YY_BUFFER_STATE b ) -+{ -+ if ( ! b ) -+ return; -+ -+ b->yy_n_chars = 0; -+ -+ /* We always need two end-of-buffer characters. The first causes -+ * a transition to the end-of-buffer state. The second causes -+ * a jam in that state. -+ */ -+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; -+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; -+ -+ b->yy_buf_pos = &b->yy_ch_buf[0]; -+ -+ b->yy_at_bol = 1; -+ b->yy_buffer_status = YY_BUFFER_NEW; -+ -+ if ( b == YY_CURRENT_BUFFER ) -+ yy_load_buffer_state( ); -+} -+ -+/** Pushes the new state onto the stack. The new state becomes -+ * the current state. This function will allocate the stack -+ * if necessary. -+ * @param new_buffer The new state. -+ * -+ */ -+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) -+{ -+ if (new_buffer == NULL) -+ return; -+ -+ yyensure_buffer_stack(); -+ -+ /* This block is copied from yy_switch_to_buffer. */ -+ if ( YY_CURRENT_BUFFER ) -+ { -+ /* Flush out information for old buffer. */ -+ *(yy_c_buf_p) = (yy_hold_char); -+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); -+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); -+ } -+ -+ /* Only push if top exists. Otherwise, replace top. */ -+ if (YY_CURRENT_BUFFER) -+ (yy_buffer_stack_top)++; -+ YY_CURRENT_BUFFER_LVALUE = new_buffer; -+ -+ /* copied from yy_switch_to_buffer. */ -+ yy_load_buffer_state( ); -+ (yy_did_buffer_switch_on_eof) = 1; -+} -+ -+/** Removes and deletes the top of the stack, if present. -+ * The next element becomes the new top. -+ * -+ */ -+void yypop_buffer_state (void) -+{ -+ if (!YY_CURRENT_BUFFER) -+ return; -+ -+ yy_delete_buffer(YY_CURRENT_BUFFER ); -+ YY_CURRENT_BUFFER_LVALUE = NULL; -+ if ((yy_buffer_stack_top) > 0) -+ --(yy_buffer_stack_top); -+ -+ if (YY_CURRENT_BUFFER) { -+ yy_load_buffer_state( ); -+ (yy_did_buffer_switch_on_eof) = 1; -+ } -+} -+ -+/* Allocates the stack if it does not exist. -+ * Guarantees space for at least one push. -+ */ -+static void yyensure_buffer_stack (void) -+{ -+ int num_to_alloc; -+ -+ if (!(yy_buffer_stack)) { -+ -+ /* First allocation is just for 2 elements, since we don't know if this -+ * scanner will even need a stack. We use 2 instead of 1 to avoid an -+ * immediate realloc on the next call. -+ */ -+ num_to_alloc = 1; -+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc -+ (num_to_alloc * sizeof(struct yy_buffer_state*) -+ ); -+ -+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); -+ -+ (yy_buffer_stack_max) = num_to_alloc; -+ (yy_buffer_stack_top) = 0; -+ return; -+ } -+ -+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ -+ -+ /* Increase the buffer to prepare for a possible push. */ -+ int grow_size = 8 /* arbitrary grow size */; -+ -+ num_to_alloc = (yy_buffer_stack_max) + grow_size; -+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc -+ ((yy_buffer_stack), -+ num_to_alloc * sizeof(struct yy_buffer_state*) -+ ); -+ -+ /* zero only the new slots.*/ -+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); -+ (yy_buffer_stack_max) = num_to_alloc; -+ } -+} -+ -+/** Setup the input buffer state to scan directly from a user-specified character buffer. -+ * @param base the character buffer -+ * @param size the size in bytes of the character buffer -+ * -+ * @return the newly allocated buffer state object. -+ */ -+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) -+{ -+ YY_BUFFER_STATE b; -+ -+ if ( size < 2 || -+ base[size-2] != YY_END_OF_BUFFER_CHAR || -+ base[size-1] != YY_END_OF_BUFFER_CHAR ) -+ /* They forgot to leave room for the EOB's. */ -+ return 0; -+ -+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ); -+ if ( ! b ) -+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); -+ -+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ -+ b->yy_buf_pos = b->yy_ch_buf = base; -+ b->yy_is_our_buffer = 0; -+ b->yy_input_file = 0; -+ b->yy_n_chars = b->yy_buf_size; -+ b->yy_is_interactive = 0; -+ b->yy_at_bol = 1; -+ b->yy_fill_buffer = 0; -+ b->yy_buffer_status = YY_BUFFER_NEW; -+ -+ yy_switch_to_buffer(b ); -+ -+ return b; -+} -+ -+/** Setup the input buffer state to scan a string. The next call to yylex() will -+ * scan from a @e copy of @a str. -+ * @param str a NUL-terminated string to scan -+ * -+ * @return the newly allocated buffer state object. -+ * @note If you want to scan bytes that may contain NUL values, then use -+ * yy_scan_bytes() instead. -+ */ -+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) -+{ -+ -+ return yy_scan_bytes(yystr,strlen(yystr) ); -+} -+ -+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will -+ * scan from a @e copy of @a bytes. -+ * @param bytes the byte buffer to scan -+ * @param len the number of bytes in the buffer pointed to by @a bytes. -+ * -+ * @return the newly allocated buffer state object. -+ */ -+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) -+{ -+ YY_BUFFER_STATE b; -+ char *buf; -+ yy_size_t n; -+ int i; -+ -+ /* Get memory for full buffer, including space for trailing EOB's. */ -+ n = _yybytes_len + 2; -+ buf = (char *) yyalloc(n ); -+ if ( ! buf ) -+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); -+ -+ for ( i = 0; i < _yybytes_len; ++i ) -+ buf[i] = yybytes[i]; -+ -+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; -+ -+ b = yy_scan_buffer(buf,n ); -+ if ( ! b ) -+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); -+ -+ /* It's okay to grow etc. this buffer, and we should throw it -+ * away when we're done. -+ */ -+ b->yy_is_our_buffer = 1; -+ -+ return b; -+} -+ -+#ifndef YY_EXIT_FAILURE -+#define YY_EXIT_FAILURE 2 -+#endif -+ -+static void yy_fatal_error (yyconst char* msg ) -+{ -+ (void) fprintf( stderr, "%s\n", msg ); -+ exit( YY_EXIT_FAILURE ); -+} -+ -+/* Redefine yyless() so it works in section 3 code. */ -+ -+#undef yyless -+#define yyless(n) \ -+ do \ -+ { \ -+ /* Undo effects of setting up yytext. */ \ -+ int yyless_macro_arg = (n); \ -+ YY_LESS_LINENO(yyless_macro_arg);\ -+ yytext[yyleng] = (yy_hold_char); \ -+ (yy_c_buf_p) = yytext + yyless_macro_arg; \ -+ (yy_hold_char) = *(yy_c_buf_p); \ -+ *(yy_c_buf_p) = '\0'; \ -+ yyleng = yyless_macro_arg; \ -+ } \ -+ while ( 0 ) -+ -+/* Accessor methods (get/set functions) to struct members. */ -+ -+/** Get the current line number. -+ * -+ */ -+int yyget_lineno (void) -+{ -+ -+ return yylineno; -+} -+ -+/** Get the input stream. -+ * -+ */ -+FILE *yyget_in (void) -+{ -+ return yyin; -+} -+ -+/** Get the output stream. -+ * -+ */ -+FILE *yyget_out (void) -+{ -+ return yyout; -+} -+ -+/** Get the length of the current token. -+ * -+ */ -+int yyget_leng (void) -+{ -+ return yyleng; -+} -+ -+/** Get the current token. -+ * -+ */ -+ -+char *yyget_text (void) -+{ -+ return yytext; -+} -+ -+/** Set the current line number. -+ * @param line_number -+ * -+ */ -+void yyset_lineno (int line_number ) -+{ -+ -+ yylineno = line_number; -+} -+ -+/** Set the input stream. This does not discard the current -+ * input buffer. -+ * @param in_str A readable stream. -+ * -+ * @see yy_switch_to_buffer -+ */ -+void yyset_in (FILE * in_str ) -+{ -+ yyin = in_str ; -+} -+ -+void yyset_out (FILE * out_str ) -+{ -+ yyout = out_str ; -+} -+ -+int yyget_debug (void) -+{ -+ return yy_flex_debug; -+} -+ -+void yyset_debug (int bdebug ) -+{ -+ yy_flex_debug = bdebug ; -+} -+ -+static int yy_init_globals (void) -+{ -+ /* Initialization is the same as for the non-reentrant scanner. -+ * This function is called from yylex_destroy(), so don't allocate here. -+ */ -+ -+ (yy_buffer_stack) = 0; -+ (yy_buffer_stack_top) = 0; -+ (yy_buffer_stack_max) = 0; -+ (yy_c_buf_p) = (char *) 0; -+ (yy_init) = 0; -+ (yy_start) = 0; -+ -+/* Defined in main.c */ -+#ifdef YY_STDINIT -+ yyin = stdin; -+ yyout = stdout; -+#else -+ yyin = (FILE *) 0; -+ yyout = (FILE *) 0; -+#endif -+ -+ /* For future reference: Set errno on error, since we are called by -+ * yylex_init() -+ */ -+ return 0; -+} -+ -+/* yylex_destroy is for both reentrant and non-reentrant scanners. */ -+int yylex_destroy (void) -+{ -+ -+ /* Pop the buffer stack, destroying each element. */ -+ while(YY_CURRENT_BUFFER){ -+ yy_delete_buffer(YY_CURRENT_BUFFER ); -+ YY_CURRENT_BUFFER_LVALUE = NULL; -+ yypop_buffer_state(); -+ } -+ -+ /* Destroy the stack itself. */ -+ yyfree((yy_buffer_stack) ); -+ (yy_buffer_stack) = NULL; -+ -+ /* Reset the globals. This is important in a non-reentrant scanner so the next time -+ * yylex() is called, initialization will occur. */ -+ yy_init_globals( ); -+ -+ return 0; -+} -+ -+/* -+ * Internal utility routines. -+ */ -+ -+#ifndef yytext_ptr -+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) -+{ -+ register int i; -+ for ( i = 0; i < n; ++i ) -+ s1[i] = s2[i]; -+} -+#endif -+ -+#ifdef YY_NEED_STRLEN -+static int yy_flex_strlen (yyconst char * s ) -+{ -+ register int n; -+ for ( n = 0; s[n]; ++n ) -+ ; -+ -+ return n; -+} -+#endif -+ -+void *yyalloc (yy_size_t size ) -+{ -+ return (void *) malloc( size ); -+} -+ -+void *yyrealloc (void * ptr, yy_size_t size ) -+{ -+ /* The cast to (char *) in the following accommodates both -+ * implementations that use char* generic pointers, and those -+ * that use void* generic pointers. It works with the latter -+ * because both ANSI C and C++ allow castless assignment from -+ * any pointer type to void*, and deal with argument conversions -+ * as though doing an assignment. -+ */ -+ return (void *) realloc( (char *) ptr, size ); -+} -+ -+void yyfree (void * ptr ) -+{ -+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ -+} -+ -+#define YYTABLES_NAME "yytables" -+ -+#line 91 "prom_lex.l" -+ -+ -+ -diff --git a/utils/fwparam_ibft/prom_lex.l b/utils/fwparam_ibft/prom_lex.l -new file mode 100644 -index 0000000..208046b ---- /dev/null -+++ b/utils/fwparam_ibft/prom_lex.l -@@ -0,0 +1,91 @@ -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * -+ * 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, see . -+ */ -+ -+/* definitions */ -+%option array -+ -+%{ -+#include "prom_parse.h" -+ -+#undef LEXDEBUG -+#ifdef LEXDEBUG -+#define dbg(a) dbgprint((a)) -+#else -+#define dbg(a) do {} while (0) -+#endif /* LEXDEBUG */ -+ -+#define upval(d) \ -+ dbg(#d); \ -+ yylval.str[0] = 0; \ -+ strcat(yylval.str, yytext); \ -+ yylloc.first_column = yylloc.last_column; \ -+ yylloc.last_column += yyleng; \ -+ return d -+ -+void dbgprint(const char *item) { fprintf(stderr, "%s: \"%s\" len=%d ", item, yytext, yyleng);} -+ -+%} -+ -+%option noyywrap -+%option never-interactive -+ -+VDEVICE vdevice -+VDEVINST gscsi -+VDEVDEV dev -+VDEVRAW rawio -+ /* CHOSEN uses only boot related paths. */ -+CHOSEN bootpath|bootargs|iscsi-bootargs|nas-bootdevice -+BUSNAME ata|i2c|ide|pci|sata|scsi|usb|lhea -+BOOTDEV cdrom|disk|ethernet|iscsi-(disk[0-9]|toe)|sd -+HEX4 [[:xdigit:]]{1,4} -+HEX16 [[:xdigit:]]{5,16} -+IPV4 [0-9]{1,3}(\.[0-9]{1,3}){3} -+IQN iqn\.[-[:alnum:]:.]{1,219} -+OBPQUAL bootp|ipv6|iscsi|dhcpv6 -+OBPPARM blksize|bootp-retries|chapid|chappw|ciaddr|dhcp|filename|giaddr|ichapid|ichappw|ilun|iname|iport|isid|isns|itname|siaddr|slp|subnet-mask|tftp-retries -+FILENAME \\[-[:alnum:]\\\.]{1,} -+ -+%% /* rules */ -+ -+{CHOSEN} { upval(CHOSEN); } -+{VDEVICE} { upval(VDEVICE); } -+{VDEVINST} { upval(VDEVINST); } -+{VDEVDEV} { upval(VDEVDEV); } -+{VDEVRAW} { upval(VDEVRAW); } -+{OBPQUAL} { upval(OBPQUAL); } -+{BUSNAME} { upval(BUSNAME); } -+{IPV4} { upval(IPV4); } -+{IQN} { upval(IQN); } -+{BOOTDEV} { upval(BOOTDEV); } -+{OBPPARM} { upval(OBPPARM); } -+{HEX4} { upval(HEX4); } -+{HEX16} { upval(HEX16); } -+{FILENAME} { upval(FILENAME); } -+[ \t\n]+ { /* eat all whitespace. */ -+ yylloc.first_column = yylloc.last_column; -+ yylloc.last_column += yyleng; -+} -+. { /* any other single char. */ -+ dbg("??"); -+ yylloc.first_column = yylloc.last_column; -+ yylloc.last_column += yyleng; -+ return *yytext; -+} -+ -+<> yyterminate(); -+%% /* user code */ -diff --git a/utils/fwparam_ibft/prom_parse.h b/utils/fwparam_ibft/prom_parse.h -new file mode 100644 -index 0000000..00cffff ---- /dev/null -+++ b/utils/fwparam_ibft/prom_parse.h -@@ -0,0 +1,39 @@ -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * -+ * 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, see . -+ */ -+ -+#ifndef PROM_PARSE_H_ -+#define PROM_PARSE_H_ -+ -+#include -+#include -+#include "iscsi_obp.h" -+ -+struct ofw_dev; -+void yyerror(struct ofw_dev *ofwdev, const char *msg); -+extern int yyleng; -+extern int yydebug; -+#include -+extern FILE *yyin; -+extern char yytext[]; -+int yylex(void); -+ -+#define YY_NO_UNPUT 1 /* match this with %option never-interactive. */ -+#include "prom_parse.tab.h" -+ -+ -+#endif /* PROM_PARSE_H_ */ -diff --git a/utils/fwparam_ibft/prom_parse.tab.c b/utils/fwparam_ibft/prom_parse.tab.c -new file mode 100644 -index 0000000..1694de4 ---- /dev/null -+++ b/utils/fwparam_ibft/prom_parse.tab.c -@@ -0,0 +1,2069 @@ -+/* A Bison parser, made by GNU Bison 2.3. */ -+ -+/* Skeleton implementation for Bison's Yacc-like parsers in C -+ -+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 -+ Free Software Foundation, Inc. -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, -+ Boston, MA 02110-1301, USA. */ -+ -+/* As a special exception, you may create a larger work that contains -+ part or all of the Bison parser skeleton and distribute that work -+ under terms of your choice, so long as that work isn't itself a -+ parser generator using the skeleton or a modified version thereof -+ as a parser skeleton. Alternatively, if you modify or redistribute -+ the parser skeleton itself, you may (at your option) remove this -+ special exception, which will cause the skeleton and the resulting -+ Bison output files to be licensed under the GNU General Public -+ License without this special exception. -+ -+ This special exception was added by the Free Software Foundation in -+ version 2.2 of Bison. */ -+ -+/* C LALR(1) parser skeleton written by Richard Stallman, by -+ simplifying the original so-called "semantic" parser. */ -+ -+/* All symbols defined below should begin with yy or YY, to avoid -+ infringing on user name space. This should be done even for local -+ variables, as they might otherwise be expanded by user macros. -+ There are some unavoidable exceptions within include files to -+ define necessary library symbols; they are noted "INFRINGES ON -+ USER NAME SPACE" below. */ -+ -+/* Identify Bison output. */ -+#define YYBISON 1 -+ -+/* Bison version. */ -+#define YYBISON_VERSION "2.3" -+ -+/* Skeleton name. */ -+#define YYSKELETON_NAME "yacc.c" -+ -+/* Pure parsers. */ -+#define YYPURE 0 -+ -+/* Using locations. */ -+#define YYLSP_NEEDED 1 -+ -+ -+ -+/* Tokens. */ -+#ifndef YYTOKENTYPE -+# define YYTOKENTYPE -+ /* Put the tokens into the symbol table, so that GDB and other debuggers -+ know about them. */ -+ enum yytokentype { -+ BUSNAME = 258, -+ BOOTDEV = 259, -+ IPV4 = 260, -+ IQN = 261, -+ OBPPARM = 262, -+ OBPQUAL = 263, -+ HEX4 = 264, -+ HEX16 = 265, -+ VDEVICE = 266, -+ VDEVINST = 267, -+ VDEVDEV = 268, -+ VDEVRAW = 269, -+ CHOSEN = 270, -+ FILENAME = 271 -+ }; -+#endif -+/* Tokens. */ -+#define BUSNAME 258 -+#define BOOTDEV 259 -+#define IPV4 260 -+#define IQN 261 -+#define OBPPARM 262 -+#define OBPQUAL 263 -+#define HEX4 264 -+#define HEX16 265 -+#define VDEVICE 266 -+#define VDEVINST 267 -+#define VDEVDEV 268 -+#define VDEVRAW 269 -+#define CHOSEN 270 -+#define FILENAME 271 -+ -+ -+ -+ -+/* Copy the first part of user declarations. */ -+#line 21 "prom_parse.y" -+ -+ /* literal block. include lines, decls, defns. */ -+//#define YYDEBUG 1 -+#if YYDEBUG -+#define DPRINT(fmt,...) printf(fmt,__VA_ARGS__) -+#else -+#define DPRINT(fmt,...) do {} while(0) -+#endif -+#include "prom_parse.h" -+#include "iscsi_obp.h" -+ -+ -+ -+/* Enabling traces. */ -+#ifndef YYDEBUG -+# define YYDEBUG 0 -+#endif -+ -+/* Enabling verbose error messages. */ -+#ifdef YYERROR_VERBOSE -+# undef YYERROR_VERBOSE -+# define YYERROR_VERBOSE 1 -+#else -+# define YYERROR_VERBOSE 0 -+#endif -+ -+/* Enabling the token table. */ -+#ifndef YYTOKEN_TABLE -+# define YYTOKEN_TABLE 0 -+#endif -+ -+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -+typedef union YYSTYPE -+#line 33 "prom_parse.y" -+{ -+ char str[256]; -+} -+/* Line 187 of yacc.c. */ -+#line 145 "prom_parse.tab.c" -+ YYSTYPE; -+# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -+# define YYSTYPE_IS_DECLARED 1 -+# define YYSTYPE_IS_TRIVIAL 1 -+#endif -+ -+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED -+typedef struct YYLTYPE -+{ -+ int first_line; -+ int first_column; -+ int last_line; -+ int last_column; -+} YYLTYPE; -+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ -+# define YYLTYPE_IS_DECLARED 1 -+# define YYLTYPE_IS_TRIVIAL 1 -+#endif -+ -+ -+/* Copy the second part of user declarations. */ -+ -+ -+/* Line 216 of yacc.c. */ -+#line 170 "prom_parse.tab.c" -+ -+#ifdef short -+# undef short -+#endif -+ -+#ifdef YYTYPE_UINT8 -+typedef YYTYPE_UINT8 yytype_uint8; -+#else -+typedef unsigned char yytype_uint8; -+#endif -+ -+#ifdef YYTYPE_INT8 -+typedef YYTYPE_INT8 yytype_int8; -+#elif (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+typedef signed char yytype_int8; -+#else -+typedef short int yytype_int8; -+#endif -+ -+#ifdef YYTYPE_UINT16 -+typedef YYTYPE_UINT16 yytype_uint16; -+#else -+typedef unsigned short int yytype_uint16; -+#endif -+ -+#ifdef YYTYPE_INT16 -+typedef YYTYPE_INT16 yytype_int16; -+#else -+typedef short int yytype_int16; -+#endif -+ -+#ifndef YYSIZE_T -+# ifdef __SIZE_TYPE__ -+# define YYSIZE_T __SIZE_TYPE__ -+# elif defined size_t -+# define YYSIZE_T size_t -+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+# include /* INFRINGES ON USER NAME SPACE */ -+# define YYSIZE_T size_t -+# else -+# define YYSIZE_T unsigned int -+# endif -+#endif -+ -+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) -+ -+#ifndef YY_ -+# if YYENABLE_NLS -+# if ENABLE_NLS -+# include /* INFRINGES ON USER NAME SPACE */ -+# define YY_(msgid) dgettext ("bison-runtime", msgid) -+# endif -+# endif -+# ifndef YY_ -+# define YY_(msgid) msgid -+# endif -+#endif -+ -+/* Suppress unused-variable warnings by "using" E. */ -+#if ! defined lint || defined __GNUC__ -+# define YYUSE(e) ((void) (e)) -+#else -+# define YYUSE(e) /* empty */ -+#endif -+ -+/* Identity function, used to suppress warnings about constant conditions. */ -+#ifndef lint -+# define YYID(n) (n) -+#else -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static int -+YYID (int i) -+#else -+static int -+YYID (i) -+ int i; -+#endif -+{ -+ return i; -+} -+#endif -+ -+#if ! defined yyoverflow || YYERROR_VERBOSE -+ -+/* The parser invokes alloca or malloc; define the necessary symbols. */ -+ -+# ifdef YYSTACK_USE_ALLOCA -+# if YYSTACK_USE_ALLOCA -+# ifdef __GNUC__ -+# define YYSTACK_ALLOC __builtin_alloca -+# elif defined __BUILTIN_VA_ARG_INCR -+# include /* INFRINGES ON USER NAME SPACE */ -+# elif defined _AIX -+# define YYSTACK_ALLOC __alloca -+# elif defined _MSC_VER -+# include /* INFRINGES ON USER NAME SPACE */ -+# define alloca _alloca -+# else -+# define YYSTACK_ALLOC alloca -+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+# include /* INFRINGES ON USER NAME SPACE */ -+# ifndef _STDLIB_H -+# define _STDLIB_H 1 -+# endif -+# endif -+# endif -+# endif -+# endif -+ -+# ifdef YYSTACK_ALLOC -+ /* Pacify GCC's `empty if-body' warning. */ -+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) -+# ifndef YYSTACK_ALLOC_MAXIMUM -+ /* The OS might guarantee only one guard page at the bottom of the stack, -+ and a page size can be as small as 4096 bytes. So we cannot safely -+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number -+ to allow for a few compiler-allocated temporary stack slots. */ -+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -+# endif -+# else -+# define YYSTACK_ALLOC YYMALLOC -+# define YYSTACK_FREE YYFREE -+# ifndef YYSTACK_ALLOC_MAXIMUM -+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -+# endif -+# if (defined __cplusplus && ! defined _STDLIB_H \ -+ && ! ((defined YYMALLOC || defined malloc) \ -+ && (defined YYFREE || defined free))) -+# include /* INFRINGES ON USER NAME SPACE */ -+# ifndef _STDLIB_H -+# define _STDLIB_H 1 -+# endif -+# endif -+# ifndef YYMALLOC -+# define YYMALLOC malloc -+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -+# endif -+# endif -+# ifndef YYFREE -+# define YYFREE free -+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+void free (void *); /* INFRINGES ON USER NAME SPACE */ -+# endif -+# endif -+# endif -+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ -+ -+ -+#if (! defined yyoverflow \ -+ && (! defined __cplusplus \ -+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ -+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) -+ -+/* A type that is properly aligned for any stack member. */ -+union yyalloc -+{ -+ yytype_int16 yyss; -+ YYSTYPE yyvs; -+ YYLTYPE yyls; -+}; -+ -+/* The size of the maximum gap between one aligned stack and the next. */ -+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) -+ -+/* The size of an array large to enough to hold all stacks, each with -+ N elements. */ -+# define YYSTACK_BYTES(N) \ -+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ -+ + 2 * YYSTACK_GAP_MAXIMUM) -+ -+/* Copy COUNT objects from FROM to TO. The source and destination do -+ not overlap. */ -+# ifndef YYCOPY -+# if defined __GNUC__ && 1 < __GNUC__ -+# define YYCOPY(To, From, Count) \ -+ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -+# else -+# define YYCOPY(To, From, Count) \ -+ do \ -+ { \ -+ YYSIZE_T yyi; \ -+ for (yyi = 0; yyi < (Count); yyi++) \ -+ (To)[yyi] = (From)[yyi]; \ -+ } \ -+ while (YYID (0)) -+# endif -+# endif -+ -+/* Relocate STACK from its old location to the new one. The -+ local variables YYSIZE and YYSTACKSIZE give the old and new number of -+ elements in the stack, and YYPTR gives the new location of the -+ stack. Advance YYPTR to a properly aligned location for the next -+ stack. */ -+# define YYSTACK_RELOCATE(Stack) \ -+ do \ -+ { \ -+ YYSIZE_T yynewbytes; \ -+ YYCOPY (&yyptr->Stack, Stack, yysize); \ -+ Stack = &yyptr->Stack; \ -+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ -+ yyptr += yynewbytes / sizeof (*yyptr); \ -+ } \ -+ while (YYID (0)) -+ -+#endif -+ -+/* YYFINAL -- State number of the termination state. */ -+#define YYFINAL 8 -+/* YYLAST -- Last index in YYTABLE. */ -+#define YYLAST 103 -+ -+/* YYNTOKENS -- Number of terminals. */ -+#define YYNTOKENS 24 -+/* YYNNTS -- Number of nonterminals. */ -+#define YYNNTS 19 -+/* YYNRULES -- Number of rules. */ -+#define YYNRULES 51 -+/* YYNRULES -- Number of states. */ -+#define YYNSTATES 93 -+ -+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ -+#define YYUNDEFTOK 2 -+#define YYMAXUTOK 273 -+ -+#define YYTRANSLATE(YYX) \ -+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) -+ -+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ -+static const yytype_uint8 yytranslate[] = -+{ -+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 19, 2, 2, 17, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 20, 2, -+ 2, 21, 2, 2, 18, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, -+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -+ 15, 16, 22, 23 -+}; -+ -+#if YYDEBUG -+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in -+ YYRHS. */ -+static const yytype_uint8 yyprhs[] = -+{ -+ 0, 0, 3, 5, 9, 14, 20, 27, 35, 37, -+ 41, 43, 47, 53, 57, 63, 67, 73, 81, 85, -+ 88, 92, 96, 100, 103, 107, 111, 113, 117, 121, -+ 125, 129, 133, 135, 139, 141, 143, 145, 147, 149, -+ 151, 155, 157, 160, 164, 167, 169, 173, 175, 178, -+ 184, 187 -+}; -+ -+/* YYRHS -- A `-1'-separated list of the rules' RHS. */ -+static const yytype_int8 yyrhs[] = -+{ -+ 25, 0, -1, 17, -1, 17, 26, 28, -1, 17, -+ 26, 28, 41, -1, 17, 26, 28, 34, 32, -1, -+ 17, 26, 28, 34, 32, 41, -1, 17, 29, 28, -+ 30, 34, 32, 41, -1, 27, -1, 26, 17, 27, -+ -1, 3, -1, 3, 18, 9, -1, 3, 18, 9, -+ 19, 9, -1, 3, 18, 10, -1, 3, 19, 9, -+ 18, 10, -1, 17, 4, 20, -1, 17, 4, 18, -+ 9, 20, -1, 17, 4, 18, 9, 19, 9, 20, -+ -1, 11, 17, 12, -1, 20, 31, -1, 30, 19, -+ 31, -1, 30, 19, 14, -1, 13, 21, 15, -1, -+ 19, 33, -1, 32, 19, 33, -1, 32, 19, 41, -+ -1, 9, -1, 7, 21, 10, -1, 7, 21, 36, -+ -1, 7, 21, 6, -1, 7, 21, 9, -1, 7, -+ 21, 16, -1, 35, -1, 34, 19, 35, -1, 8, -+ -1, 31, -1, 37, -1, 38, -1, 5, -1, 39, -+ -1, 39, 20, 37, -1, 40, -1, 39, 22, -1, -+ 39, 22, 40, -1, 22, 40, -1, 9, -1, 40, -+ 23, 9, -1, 42, -1, 9, 42, -1, 18, 9, -+ 19, 9, 42, -1, 20, 9, -1, 20, 9, 19, -+ 16, -1 -+}; -+ -+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ -+static const yytype_uint16 yyrline[] = -+{ -+ 0, 58, 58, 61, 64, 72, 80, 87, 94, 97, -+ 102, 105, 108, 111, 114, 120, 123, 126, 131, 136, -+ 139, 142, 147, 152, 155, 158, 163, 166, 171, 175, -+ 179, 183, 189, 192, 197, 200, 205, 208, 213, 218, -+ 221, 226, 229, 232, 235, 240, 243, 248, 251, 254, -+ 259, 262 -+}; -+#endif -+ -+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE -+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. -+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -+static const char *const yytname[] = -+{ -+ "$end", "error", "$undefined", "BUSNAME", "BOOTDEV", "IPV4", "IQN", -+ "OBPPARM", "OBPQUAL", "HEX4", "HEX16", "VDEVICE", "VDEVINST", "VDEVDEV", -+ "VDEVRAW", "CHOSEN", "FILENAME", "'/'", "'@'", "','", "':'", "'='", -+ "\"::\"", "\":\"", "$accept", "devpath", "busses", "bus", "bootdev", -+ "vdevice", "vdev_parms", "vdev_parm", "obp_params", "obp_param", -+ "obp_quals", "obp_qual", "ipaddr", "ipv4", "ipv6", "hexpart", "hexseq", -+ "disklabel", "diskpart", 0 -+}; -+#endif -+ -+# ifdef YYPRINT -+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to -+ token YYLEX-NUM. */ -+static const yytype_uint16 yytoknum[] = -+{ -+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, -+ 265, 266, 267, 268, 269, 270, 271, 47, 64, 44, -+ 58, 61, 272, 273 -+}; -+# endif -+ -+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -+static const yytype_uint8 yyr1[] = -+{ -+ 0, 24, 25, 25, 25, 25, 25, 25, 26, 26, -+ 27, 27, 27, 27, 27, 28, 28, 28, 29, 30, -+ 30, 30, 31, 32, 32, 32, 33, 33, 33, 33, -+ 33, 33, 34, 34, 35, 35, 36, 36, 37, 38, -+ 38, 39, 39, 39, 39, 40, 40, 41, 41, 41, -+ 42, 42 -+}; -+ -+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ -+static const yytype_uint8 yyr2[] = -+{ -+ 0, 2, 1, 3, 4, 5, 6, 7, 1, 3, -+ 1, 3, 5, 3, 5, 3, 5, 7, 3, 2, -+ 3, 3, 3, 2, 3, 3, 1, 3, 3, 3, -+ 3, 3, 1, 3, 1, 1, 1, 1, 1, 1, -+ 3, 1, 2, 3, 2, 1, 3, 1, 2, 5, -+ 2, 4 -+}; -+ -+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state -+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero -+ means the default is an error. */ -+static const yytype_uint8 yydefact[] = -+{ -+ 0, 2, 0, 10, 0, 0, 8, 0, 1, 0, -+ 0, 0, 0, 3, 0, 0, 11, 13, 0, 18, -+ 0, 9, 34, 0, 0, 0, 0, 35, 0, 32, -+ 4, 47, 0, 0, 0, 0, 0, 15, 48, 0, -+ 0, 50, 0, 5, 19, 0, 0, 12, 14, 0, -+ 22, 0, 0, 0, 26, 23, 33, 0, 6, 21, -+ 20, 0, 0, 16, 0, 51, 0, 26, 24, 25, -+ 7, 0, 49, 38, 29, 30, 27, 31, 0, 28, -+ 36, 37, 39, 41, 17, 45, 44, 0, 42, 0, -+ 40, 43, 46 -+}; -+ -+/* YYDEFGOTO[NTERM-NUM]. */ -+static const yytype_int8 yydefgoto[] = -+{ -+ -1, 2, 5, 6, 13, 7, 33, 27, 43, 55, -+ 28, 29, 79, 80, 81, 82, 83, 30, 31 -+}; -+ -+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing -+ STATE-NUM. */ -+#define YYPACT_NINF -73 -+static const yytype_int8 yypact[] = -+{ -+ -15, 19, 13, 20, 18, 23, -73, 25, -73, 39, -+ 51, 50, 49, 16, 46, 43, 42, -73, 47, -73, -+ -9, -73, -73, 44, 45, 58, 59, -73, 52, -73, -+ -73, -73, 56, 24, 61, 62, 64, -73, -73, 60, -+ 55, 57, 38, 8, -73, 41, 52, -73, -73, 37, -+ -73, 68, 63, 65, -73, -73, -73, 3, -73, -73, -+ -73, 8, 69, -73, 44, -73, -2, 44, -73, -73, -+ -73, 67, -73, -73, -73, 36, -73, -73, 71, -73, -+ -73, -73, 11, 66, -73, -73, 66, 76, 71, 73, -+ -73, 66, -73 -+}; -+ -+/* YYPGOTO[NTERM-NUM]. */ -+static const yytype_int8 yypgoto[] = -+{ -+ -73, -73, -73, 72, 78, -73, -73, -27, 48, 26, -+ 70, 53, -73, 1, -73, -73, -72, -42, -23 -+}; -+ -+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If -+ positive, shift that token. If negative, reduce the rule which -+ number is the opposite. If zero, do what YYDEFACT says. -+ If YYTABLE_NINF, syntax error. */ -+#define YYTABLE_NINF -46 -+static const yytype_int8 yytable[] = -+{ -+ 38, 58, 1, 73, 74, 44, 86, 75, 76, 36, -+ 53, 37, 67, 8, 77, 69, 91, 23, 60, 70, -+ 78, 25, 3, 26, 22, 23, 25, 57, 26, 24, -+ 4, 87, 22, 88, 25, 11, 26, 24, 9, 10, -+ 12, 72, 14, 45, 38, 53, 22, 54, 16, 17, -+ 20, 24, 3, 20, 24, 59, 62, 63, -45, -45, -+ 18, 34, 19, 32, 26, 35, 39, 40, 41, 24, -+ 47, 42, 48, 49, 51, 50, 52, 64, 71, 65, -+ 85, 73, 92, 68, 21, 15, 66, 84, 90, 89, -+ 0, 0, 0, 0, 61, 56, 0, 0, 0, 0, -+ 0, 0, 0, 46 -+}; -+ -+static const yytype_int8 yycheck[] = -+{ -+ 23, 43, 17, 5, 6, 32, 78, 9, 10, 18, -+ 7, 20, 9, 0, 16, 57, 88, 9, 45, 61, -+ 22, 18, 3, 20, 8, 9, 18, 19, 20, 13, -+ 11, 20, 8, 22, 18, 17, 20, 13, 18, 19, -+ 17, 64, 17, 19, 67, 7, 8, 9, 9, 10, -+ 4, 13, 3, 4, 13, 14, 19, 20, 22, 23, -+ 9, 19, 12, 20, 20, 18, 21, 9, 9, 13, -+ 9, 19, 10, 9, 19, 15, 19, 9, 9, 16, -+ 9, 5, 9, 57, 12, 7, 21, 20, 87, 23, -+ -1, -1, -1, -1, 46, 42, -1, -1, -1, -1, -+ -1, -1, -1, 33 -+}; -+ -+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing -+ symbol of state STATE-NUM. */ -+static const yytype_uint8 yystos[] = -+{ -+ 0, 17, 25, 3, 11, 26, 27, 29, 0, 18, -+ 19, 17, 17, 28, 17, 28, 9, 10, 9, 12, -+ 4, 27, 8, 9, 13, 18, 20, 31, 34, 35, -+ 41, 42, 20, 30, 19, 18, 18, 20, 42, 21, -+ 9, 9, 19, 32, 31, 19, 34, 9, 10, 9, -+ 15, 19, 19, 7, 9, 33, 35, 19, 41, 14, -+ 31, 32, 19, 20, 9, 16, 21, 9, 33, 41, -+ 41, 9, 42, 5, 6, 9, 10, 16, 22, 36, -+ 37, 38, 39, 40, 20, 9, 40, 20, 22, 23, -+ 37, 40, 9 -+}; -+ -+#define yyerrok (yyerrstatus = 0) -+#define yyclearin (yychar = YYEMPTY) -+#define YYEMPTY (-2) -+#define YYEOF 0 -+ -+#define YYACCEPT goto yyacceptlab -+#define YYABORT goto yyabortlab -+#define YYERROR goto yyerrorlab -+ -+ -+/* Like YYERROR except do call yyerror. This remains here temporarily -+ to ease the transition to the new meaning of YYERROR, for GCC. -+ Once GCC version 2 has supplanted version 1, this can go. */ -+ -+#define YYFAIL goto yyerrlab -+ -+#define YYRECOVERING() (!!yyerrstatus) -+ -+#define YYBACKUP(Token, Value) \ -+do \ -+ if (yychar == YYEMPTY && yylen == 1) \ -+ { \ -+ yychar = (Token); \ -+ yylval = (Value); \ -+ yytoken = YYTRANSLATE (yychar); \ -+ YYPOPSTACK (1); \ -+ goto yybackup; \ -+ } \ -+ else \ -+ { \ -+ yyerror (ofwdev, YY_("syntax error: cannot back up")); \ -+ YYERROR; \ -+ } \ -+while (YYID (0)) -+ -+ -+#define YYTERROR 1 -+#define YYERRCODE 256 -+ -+ -+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. -+ If N is 0, then set CURRENT to the empty location which ends -+ the previous symbol: RHS[0] (always defined). */ -+ -+#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -+#ifndef YYLLOC_DEFAULT -+# define YYLLOC_DEFAULT(Current, Rhs, N) \ -+ do \ -+ if (YYID (N)) \ -+ { \ -+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ -+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ -+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ -+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ -+ } \ -+ else \ -+ { \ -+ (Current).first_line = (Current).last_line = \ -+ YYRHSLOC (Rhs, 0).last_line; \ -+ (Current).first_column = (Current).last_column = \ -+ YYRHSLOC (Rhs, 0).last_column; \ -+ } \ -+ while (YYID (0)) -+#endif -+ -+ -+/* YY_LOCATION_PRINT -- Print the location on the stream. -+ This macro was not mandated originally: define only if we know -+ we won't break user code: when these are the locations we know. */ -+ -+#ifndef YY_LOCATION_PRINT -+# if YYLTYPE_IS_TRIVIAL -+# define YY_LOCATION_PRINT(File, Loc) \ -+ fprintf (File, "%d.%d-%d.%d", \ -+ (Loc).first_line, (Loc).first_column, \ -+ (Loc).last_line, (Loc).last_column) -+# else -+# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -+# endif -+#endif -+ -+ -+/* YYLEX -- calling `yylex' with the right arguments. */ -+ -+#ifdef YYLEX_PARAM -+# define YYLEX yylex (YYLEX_PARAM) -+#else -+# define YYLEX yylex () -+#endif -+ -+/* Enable debugging if requested. */ -+#if YYDEBUG -+ -+# ifndef YYFPRINTF -+# include /* INFRINGES ON USER NAME SPACE */ -+# define YYFPRINTF fprintf -+# endif -+ -+# define YYDPRINTF(Args) \ -+do { \ -+ if (yydebug) \ -+ YYFPRINTF Args; \ -+} while (YYID (0)) -+ -+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -+do { \ -+ if (yydebug) \ -+ { \ -+ YYFPRINTF (stderr, "%s ", Title); \ -+ yy_symbol_print (stderr, \ -+ Type, Value, Location, ofwdev); \ -+ YYFPRINTF (stderr, "\n"); \ -+ } \ -+} while (YYID (0)) -+ -+ -+/*--------------------------------. -+| Print this symbol on YYOUTPUT. | -+`--------------------------------*/ -+ -+/*ARGSUSED*/ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static void -+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct ofw_dev *ofwdev) -+#else -+static void -+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, ofwdev) -+ FILE *yyoutput; -+ int yytype; -+ YYSTYPE const * const yyvaluep; -+ YYLTYPE const * const yylocationp; -+ struct ofw_dev *ofwdev; -+#endif -+{ -+ if (!yyvaluep) -+ return; -+ YYUSE (yylocationp); -+ YYUSE (ofwdev); -+# ifdef YYPRINT -+ if (yytype < YYNTOKENS) -+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -+# else -+ YYUSE (yyoutput); -+# endif -+ switch (yytype) -+ { -+ default: -+ break; -+ } -+} -+ -+ -+/*--------------------------------. -+| Print this symbol on YYOUTPUT. | -+`--------------------------------*/ -+ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static void -+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct ofw_dev *ofwdev) -+#else -+static void -+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp, ofwdev) -+ FILE *yyoutput; -+ int yytype; -+ YYSTYPE const * const yyvaluep; -+ YYLTYPE const * const yylocationp; -+ struct ofw_dev *ofwdev; -+#endif -+{ -+ if (yytype < YYNTOKENS) -+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); -+ else -+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); -+ -+ YY_LOCATION_PRINT (yyoutput, *yylocationp); -+ YYFPRINTF (yyoutput, ": "); -+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, ofwdev); -+ YYFPRINTF (yyoutput, ")"); -+} -+ -+/*------------------------------------------------------------------. -+| yy_stack_print -- Print the state stack from its BOTTOM up to its | -+| TOP (included). | -+`------------------------------------------------------------------*/ -+ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static void -+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) -+#else -+static void -+yy_stack_print (bottom, top) -+ yytype_int16 *bottom; -+ yytype_int16 *top; -+#endif -+{ -+ YYFPRINTF (stderr, "Stack now"); -+ for (; bottom <= top; ++bottom) -+ YYFPRINTF (stderr, " %d", *bottom); -+ YYFPRINTF (stderr, "\n"); -+} -+ -+# define YY_STACK_PRINT(Bottom, Top) \ -+do { \ -+ if (yydebug) \ -+ yy_stack_print ((Bottom), (Top)); \ -+} while (YYID (0)) -+ -+ -+/*------------------------------------------------. -+| Report that the YYRULE is going to be reduced. | -+`------------------------------------------------*/ -+ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static void -+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, struct ofw_dev *ofwdev) -+#else -+static void -+yy_reduce_print (yyvsp, yylsp, yyrule, ofwdev) -+ YYSTYPE *yyvsp; -+ YYLTYPE *yylsp; -+ int yyrule; -+ struct ofw_dev *ofwdev; -+#endif -+{ -+ int yynrhs = yyr2[yyrule]; -+ int yyi; -+ unsigned long int yylno = yyrline[yyrule]; -+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", -+ yyrule - 1, yylno); -+ /* The symbols being reduced. */ -+ for (yyi = 0; yyi < yynrhs; yyi++) -+ { -+ fprintf (stderr, " $%d = ", yyi + 1); -+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], -+ &(yyvsp[(yyi + 1) - (yynrhs)]) -+ , &(yylsp[(yyi + 1) - (yynrhs)]) , ofwdev); -+ fprintf (stderr, "\n"); -+ } -+} -+ -+# define YY_REDUCE_PRINT(Rule) \ -+do { \ -+ if (yydebug) \ -+ yy_reduce_print (yyvsp, yylsp, Rule, ofwdev); \ -+} while (YYID (0)) -+ -+/* Nonzero means print parse trace. It is left uninitialized so that -+ multiple parsers can coexist. */ -+int yydebug; -+#else /* !YYDEBUG */ -+# define YYDPRINTF(Args) -+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) -+# define YY_STACK_PRINT(Bottom, Top) -+# define YY_REDUCE_PRINT(Rule) -+#endif /* !YYDEBUG */ -+ -+ -+/* YYINITDEPTH -- initial size of the parser's stacks. */ -+#ifndef YYINITDEPTH -+# define YYINITDEPTH 200 -+#endif -+ -+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only -+ if the built-in stack extension method is used). -+ -+ Do not make this value too large; the results are undefined if -+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) -+ evaluated with infinite-precision integer arithmetic. */ -+ -+#ifndef YYMAXDEPTH -+# define YYMAXDEPTH 10000 -+#endif -+ -+ -+ -+#if YYERROR_VERBOSE -+ -+# ifndef yystrlen -+# if defined __GLIBC__ && defined _STRING_H -+# define yystrlen strlen -+# else -+/* Return the length of YYSTR. */ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static YYSIZE_T -+yystrlen (const char *yystr) -+#else -+static YYSIZE_T -+yystrlen (yystr) -+ const char *yystr; -+#endif -+{ -+ YYSIZE_T yylen; -+ for (yylen = 0; yystr[yylen]; yylen++) -+ continue; -+ return yylen; -+} -+# endif -+# endif -+ -+# ifndef yystpcpy -+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -+# define yystpcpy stpcpy -+# else -+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in -+ YYDEST. */ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static char * -+yystpcpy (char *yydest, const char *yysrc) -+#else -+static char * -+yystpcpy (yydest, yysrc) -+ char *yydest; -+ const char *yysrc; -+#endif -+{ -+ char *yyd = yydest; -+ const char *yys = yysrc; -+ -+ while ((*yyd++ = *yys++) != '\0') -+ continue; -+ -+ return yyd - 1; -+} -+# endif -+# endif -+ -+# ifndef yytnamerr -+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary -+ quotes and backslashes, so that it's suitable for yyerror. The -+ heuristic is that double-quoting is unnecessary unless the string -+ contains an apostrophe, a comma, or backslash (other than -+ backslash-backslash). YYSTR is taken from yytname. If YYRES is -+ null, do not copy; instead, return the length of what the result -+ would have been. */ -+static YYSIZE_T -+yytnamerr (char *yyres, const char *yystr) -+{ -+ if (*yystr == '"') -+ { -+ YYSIZE_T yyn = 0; -+ char const *yyp = yystr; -+ -+ for (;;) -+ switch (*++yyp) -+ { -+ case '\'': -+ case ',': -+ goto do_not_strip_quotes; -+ -+ case '\\': -+ if (*++yyp != '\\') -+ goto do_not_strip_quotes; -+ /* Fall through. */ -+ default: -+ if (yyres) -+ yyres[yyn] = *yyp; -+ yyn++; -+ break; -+ -+ case '"': -+ if (yyres) -+ yyres[yyn] = '\0'; -+ return yyn; -+ } -+ do_not_strip_quotes: ; -+ } -+ -+ if (! yyres) -+ return yystrlen (yystr); -+ -+ return yystpcpy (yyres, yystr) - yyres; -+} -+# endif -+ -+/* Copy into YYRESULT an error message about the unexpected token -+ YYCHAR while in state YYSTATE. Return the number of bytes copied, -+ including the terminating null byte. If YYRESULT is null, do not -+ copy anything; just return the number of bytes that would be -+ copied. As a special case, return 0 if an ordinary "syntax error" -+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during -+ size calculation. */ -+static YYSIZE_T -+yysyntax_error (char *yyresult, int yystate, int yychar) -+{ -+ int yyn = yypact[yystate]; -+ -+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) -+ return 0; -+ else -+ { -+ int yytype = YYTRANSLATE (yychar); -+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); -+ YYSIZE_T yysize = yysize0; -+ YYSIZE_T yysize1; -+ int yysize_overflow = 0; -+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; -+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; -+ int yyx; -+ -+# if 0 -+ /* This is so xgettext sees the translatable formats that are -+ constructed on the fly. */ -+ YY_("syntax error, unexpected %s"); -+ YY_("syntax error, unexpected %s, expecting %s"); -+ YY_("syntax error, unexpected %s, expecting %s or %s"); -+ YY_("syntax error, unexpected %s, expecting %s or %s or %s"); -+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -+# endif -+ char *yyfmt; -+ char const *yyf; -+ static char const yyunexpected[] = "syntax error, unexpected %s"; -+ static char const yyexpecting[] = ", expecting %s"; -+ static char const yyor[] = " or %s"; -+ char yyformat[sizeof yyunexpected -+ + sizeof yyexpecting - 1 -+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) -+ * (sizeof yyor - 1))]; -+ char const *yyprefix = yyexpecting; -+ -+ /* Start YYX at -YYN if negative to avoid negative indexes in -+ YYCHECK. */ -+ int yyxbegin = yyn < 0 ? -yyn : 0; -+ -+ /* Stay within bounds of both yycheck and yytname. */ -+ int yychecklim = YYLAST - yyn + 1; -+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; -+ int yycount = 1; -+ -+ yyarg[0] = yytname[yytype]; -+ yyfmt = yystpcpy (yyformat, yyunexpected); -+ -+ for (yyx = yyxbegin; yyx < yyxend; ++yyx) -+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) -+ { -+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) -+ { -+ yycount = 1; -+ yysize = yysize0; -+ yyformat[sizeof yyunexpected - 1] = '\0'; -+ break; -+ } -+ yyarg[yycount++] = yytname[yyx]; -+ yysize1 = yysize + yytnamerr (0, yytname[yyx]); -+ yysize_overflow |= (yysize1 < yysize); -+ yysize = yysize1; -+ yyfmt = yystpcpy (yyfmt, yyprefix); -+ yyprefix = yyor; -+ } -+ -+ yyf = YY_(yyformat); -+ yysize1 = yysize + yystrlen (yyf); -+ yysize_overflow |= (yysize1 < yysize); -+ yysize = yysize1; -+ -+ if (yysize_overflow) -+ return YYSIZE_MAXIMUM; -+ -+ if (yyresult) -+ { -+ /* Avoid sprintf, as that infringes on the user's name space. -+ Don't have undefined behavior even if the translation -+ produced a string with the wrong number of "%s"s. */ -+ char *yyp = yyresult; -+ int yyi = 0; -+ while ((*yyp = *yyf) != '\0') -+ { -+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) -+ { -+ yyp += yytnamerr (yyp, yyarg[yyi++]); -+ yyf += 2; -+ } -+ else -+ { -+ yyp++; -+ yyf++; -+ } -+ } -+ } -+ return yysize; -+ } -+} -+#endif /* YYERROR_VERBOSE */ -+ -+ -+/*-----------------------------------------------. -+| Release the memory associated to this symbol. | -+`-----------------------------------------------*/ -+ -+/*ARGSUSED*/ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+static void -+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, struct ofw_dev *ofwdev) -+#else -+static void -+yydestruct (yymsg, yytype, yyvaluep, yylocationp, ofwdev) -+ const char *yymsg; -+ int yytype; -+ YYSTYPE *yyvaluep; -+ YYLTYPE *yylocationp; -+ struct ofw_dev *ofwdev; -+#endif -+{ -+ YYUSE (yyvaluep); -+ YYUSE (yylocationp); -+ YYUSE (ofwdev); -+ -+ if (!yymsg) -+ yymsg = "Deleting"; -+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); -+ -+ switch (yytype) -+ { -+ -+ default: -+ break; -+ } -+} -+ -+ -+/* Prevent warnings from -Wmissing-prototypes. */ -+ -+#ifdef YYPARSE_PARAM -+#if defined __STDC__ || defined __cplusplus -+int yyparse (void *YYPARSE_PARAM); -+#else -+int yyparse (); -+#endif -+#else /* ! YYPARSE_PARAM */ -+#if defined __STDC__ || defined __cplusplus -+int yyparse (struct ofw_dev *ofwdev); -+#else -+int yyparse (); -+#endif -+#endif /* ! YYPARSE_PARAM */ -+ -+ -+ -+/* The look-ahead symbol. */ -+int yychar; -+ -+/* The semantic value of the look-ahead symbol. */ -+YYSTYPE yylval; -+ -+/* Number of syntax errors so far. */ -+int yynerrs; -+/* Location data for the look-ahead symbol. */ -+YYLTYPE yylloc; -+ -+ -+ -+/*----------. -+| yyparse. | -+`----------*/ -+ -+#ifdef YYPARSE_PARAM -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+int -+yyparse (void *YYPARSE_PARAM) -+#else -+int -+yyparse (YYPARSE_PARAM) -+ void *YYPARSE_PARAM; -+#endif -+#else /* ! YYPARSE_PARAM */ -+#if (defined __STDC__ || defined __C99__FUNC__ \ -+ || defined __cplusplus || defined _MSC_VER) -+int -+yyparse (struct ofw_dev *ofwdev) -+#else -+int -+yyparse (ofwdev) -+ struct ofw_dev *ofwdev; -+#endif -+#endif -+{ -+ -+ int yystate; -+ int yyn; -+ int yyresult; -+ /* Number of tokens to shift before error messages enabled. */ -+ int yyerrstatus; -+ /* Look-ahead token as an internal (translated) token number. */ -+ int yytoken = 0; -+#if YYERROR_VERBOSE -+ /* Buffer for error messages, and its allocated size. */ -+ char yymsgbuf[128]; -+ char *yymsg = yymsgbuf; -+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -+#endif -+ -+ /* Three stacks and their tools: -+ `yyss': related to states, -+ `yyvs': related to semantic values, -+ `yyls': related to locations. -+ -+ Refer to the stacks thru separate pointers, to allow yyoverflow -+ to reallocate them elsewhere. */ -+ -+ /* The state stack. */ -+ yytype_int16 yyssa[YYINITDEPTH]; -+ yytype_int16 *yyss = yyssa; -+ yytype_int16 *yyssp; -+ -+ /* The semantic value stack. */ -+ YYSTYPE yyvsa[YYINITDEPTH]; -+ YYSTYPE *yyvs = yyvsa; -+ YYSTYPE *yyvsp; -+ -+ /* The location stack. */ -+ YYLTYPE yylsa[YYINITDEPTH]; -+ YYLTYPE *yyls = yylsa; -+ YYLTYPE *yylsp; -+ /* The locations where the error started and ended. */ -+ YYLTYPE yyerror_range[2]; -+ -+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) -+ -+ YYSIZE_T yystacksize = YYINITDEPTH; -+ -+ /* The variables used to return semantic value and location from the -+ action routines. */ -+ YYSTYPE yyval; -+ YYLTYPE yyloc; -+ -+ /* The number of symbols on the RHS of the reduced rule. -+ Keep to zero when no symbol should be popped. */ -+ int yylen = 0; -+ -+ YYDPRINTF ((stderr, "Starting parse\n")); -+ -+ yystate = 0; -+ yyerrstatus = 0; -+ yynerrs = 0; -+ yychar = YYEMPTY; /* Cause a token to be read. */ -+ -+ /* Initialize stack pointers. -+ Waste one element of value and location stack -+ so that they stay on the same level as the state stack. -+ The wasted elements are never initialized. */ -+ -+ yyssp = yyss; -+ yyvsp = yyvs; -+ yylsp = yyls; -+#if YYLTYPE_IS_TRIVIAL -+ /* Initialize the default location before parsing starts. */ -+ yylloc.first_line = yylloc.last_line = 1; -+ yylloc.first_column = yylloc.last_column = 0; -+#endif -+ -+ goto yysetstate; -+ -+/*------------------------------------------------------------. -+| yynewstate -- Push a new state, which is found in yystate. | -+`------------------------------------------------------------*/ -+ yynewstate: -+ /* In all cases, when you get here, the value and location stacks -+ have just been pushed. So pushing a state here evens the stacks. */ -+ yyssp++; -+ -+ yysetstate: -+ *yyssp = yystate; -+ -+ if (yyss + yystacksize - 1 <= yyssp) -+ { -+ /* Get the current used size of the three stacks, in elements. */ -+ YYSIZE_T yysize = yyssp - yyss + 1; -+ -+#ifdef yyoverflow -+ { -+ /* Give user a chance to reallocate the stack. Use copies of -+ these so that the &'s don't force the real ones into -+ memory. */ -+ YYSTYPE *yyvs1 = yyvs; -+ yytype_int16 *yyss1 = yyss; -+ YYLTYPE *yyls1 = yyls; -+ -+ /* Each stack pointer address is followed by the size of the -+ data in use in that stack, in bytes. This used to be a -+ conditional around just the two extra args, but that might -+ be undefined if yyoverflow is a macro. */ -+ yyoverflow (YY_("memory exhausted"), -+ &yyss1, yysize * sizeof (*yyssp), -+ &yyvs1, yysize * sizeof (*yyvsp), -+ &yyls1, yysize * sizeof (*yylsp), -+ &yystacksize); -+ yyls = yyls1; -+ yyss = yyss1; -+ yyvs = yyvs1; -+ } -+#else /* no yyoverflow */ -+# ifndef YYSTACK_RELOCATE -+ goto yyexhaustedlab; -+# else -+ /* Extend the stack our own way. */ -+ if (YYMAXDEPTH <= yystacksize) -+ goto yyexhaustedlab; -+ yystacksize *= 2; -+ if (YYMAXDEPTH < yystacksize) -+ yystacksize = YYMAXDEPTH; -+ -+ { -+ yytype_int16 *yyss1 = yyss; -+ union yyalloc *yyptr = -+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); -+ if (! yyptr) -+ goto yyexhaustedlab; -+ YYSTACK_RELOCATE (yyss); -+ YYSTACK_RELOCATE (yyvs); -+ YYSTACK_RELOCATE (yyls); -+# undef YYSTACK_RELOCATE -+ if (yyss1 != yyssa) -+ YYSTACK_FREE (yyss1); -+ } -+# endif -+#endif /* no yyoverflow */ -+ -+ yyssp = yyss + yysize - 1; -+ yyvsp = yyvs + yysize - 1; -+ yylsp = yyls + yysize - 1; -+ -+ YYDPRINTF ((stderr, "Stack size increased to %lu\n", -+ (unsigned long int) yystacksize)); -+ -+ if (yyss + yystacksize - 1 <= yyssp) -+ YYABORT; -+ } -+ -+ YYDPRINTF ((stderr, "Entering state %d\n", yystate)); -+ -+ goto yybackup; -+ -+/*-----------. -+| yybackup. | -+`-----------*/ -+yybackup: -+ -+ /* Do appropriate processing given the current state. Read a -+ look-ahead token if we need one and don't already have one. */ -+ -+ /* First try to decide what to do without reference to look-ahead token. */ -+ yyn = yypact[yystate]; -+ if (yyn == YYPACT_NINF) -+ goto yydefault; -+ -+ /* Not known => get a look-ahead token if don't already have one. */ -+ -+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ -+ if (yychar == YYEMPTY) -+ { -+ YYDPRINTF ((stderr, "Reading a token: ")); -+ yychar = YYLEX; -+ } -+ -+ if (yychar <= YYEOF) -+ { -+ yychar = yytoken = YYEOF; -+ YYDPRINTF ((stderr, "Now at end of input.\n")); -+ } -+ else -+ { -+ yytoken = YYTRANSLATE (yychar); -+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); -+ } -+ -+ /* If the proper action on seeing token YYTOKEN is to reduce or to -+ detect an error, take that action. */ -+ yyn += yytoken; -+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) -+ goto yydefault; -+ yyn = yytable[yyn]; -+ if (yyn <= 0) -+ { -+ if (yyn == 0 || yyn == YYTABLE_NINF) -+ goto yyerrlab; -+ yyn = -yyn; -+ goto yyreduce; -+ } -+ -+ if (yyn == YYFINAL) -+ YYACCEPT; -+ -+ /* Count tokens shifted since error; after three, turn off error -+ status. */ -+ if (yyerrstatus) -+ yyerrstatus--; -+ -+ /* Shift the look-ahead token. */ -+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); -+ -+ /* Discard the shifted token unless it is eof. */ -+ if (yychar != YYEOF) -+ yychar = YYEMPTY; -+ -+ yystate = yyn; -+ *++yyvsp = yylval; -+ *++yylsp = yylloc; -+ goto yynewstate; -+ -+ -+/*-----------------------------------------------------------. -+| yydefault -- do the default action for the current state. | -+`-----------------------------------------------------------*/ -+yydefault: -+ yyn = yydefact[yystate]; -+ if (yyn == 0) -+ goto yyerrlab; -+ goto yyreduce; -+ -+ -+/*-----------------------------. -+| yyreduce -- Do a reduction. | -+`-----------------------------*/ -+yyreduce: -+ /* yyn is the number of a rule to reduce with. */ -+ yylen = yyr2[yyn]; -+ -+ /* If YYLEN is nonzero, implement the default value of the action: -+ `$$ = $1'. -+ -+ Otherwise, the following line sets YYVAL to garbage. -+ This behavior is undocumented and Bison -+ users should not rely upon it. Assigning to YYVAL -+ unconditionally makes the parser a bit smaller, and it avoids a -+ GCC warning that YYVAL may be used uninitialized. */ -+ yyval = yyvsp[1-yylen]; -+ -+ /* Default location. */ -+ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); -+ YY_REDUCE_PRINT (yyn); -+ switch (yyn) -+ { -+ case 2: -+#line 58 "prom_parse.y" -+ { -+ DPRINT("****rootonly: \"%s\"\n", "/"); -+ ;} -+ break; -+ -+ case 3: -+#line 61 "prom_parse.y" -+ { -+ DPRINT("****devpath busses:\n/%s/%s\n", (yyvsp[(2) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 4: -+#line 64 "prom_parse.y" -+ { -+ ofwdev->dev_path = malloc(strlen((yyvsp[(2) - (4)].str)) + -+ strlen((yyvsp[(3) - (4)].str)) + 3); -+ sprintf(ofwdev->dev_path, "/%s%s", (yyvsp[(2) - (4)].str), (yyvsp[(3) - (4)].str)); -+ DPRINT("****devpath busses bootdev " -+ "disklabel:\n/%s/%s%s\n", -+ (yyvsp[(2) - (4)].str), (yyvsp[(3) - (4)].str), (yyvsp[(4) - (4)].str)); -+ ;} -+ break; -+ -+ case 5: -+#line 72 "prom_parse.y" -+ { -+ ofwdev->dev_path = malloc(strlen((yyvsp[(2) - (5)].str)) + -+ strlen((yyvsp[(3) - (5)].str)) + 3); -+ sprintf(ofwdev->dev_path, "/%s%s", (yyvsp[(2) - (5)].str), (yyvsp[(3) - (5)].str)); -+ DPRINT("****busses bootdev obp_quals obp_parms:\n" -+ "/%s/%s:%s%s\n", -+ (yyvsp[(2) - (5)].str), (yyvsp[(3) - (5)].str), (yyvsp[(4) - (5)].str), (yyvsp[(5) - (5)].str)); -+ ;} -+ break; -+ -+ case 6: -+#line 80 "prom_parse.y" -+ { -+ ofwdev->dev_path = malloc(strlen((yyvsp[(2) - (6)].str)) + -+ strlen((yyvsp[(3) - (6)].str)) + 3); -+ sprintf(ofwdev->dev_path, "/%s%s", (yyvsp[(2) - (6)].str), (yyvsp[(3) - (6)].str)); -+ DPRINT("****busses bootdev obp_quals obp_parms " -+ "disklabel:\n/%s:%s%s%s\n", (yyvsp[(2) - (6)].str), (yyvsp[(4) - (6)].str), (yyvsp[(5) - (6)].str), (yyvsp[(6) - (6)].str)); -+ ;} -+ break; -+ -+ case 7: -+#line 87 "prom_parse.y" -+ { -+ DPRINT("****vdevice bootdev obp_parms " -+ "disklabel:\n/%s:%s%s%s%s\n", -+ (yyvsp[(2) - (7)].str), (yyvsp[(4) - (7)].str), (yyvsp[(5) - (7)].str), (yyvsp[(6) - (7)].str), (yyvsp[(7) - (7)].str)); -+ ;} -+ break; -+ -+ case 8: -+#line 94 "prom_parse.y" -+ { -+ strcpy((yyval.str), (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 9: -+#line 97 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s/%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 10: -+#line 102 "prom_parse.y" -+ { -+ strcpy((yyval.str), (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 11: -+#line 105 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s@%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 12: -+#line 108 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s@%s,%s", (yyvsp[(1) - (5)].str), (yyvsp[(3) - (5)].str), (yyvsp[(5) - (5)].str)); -+ ;} -+ break; -+ -+ case 13: -+#line 111 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s@%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 14: -+#line 114 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s,%s@%s", (yyvsp[(1) - (5)].str), (yyvsp[(3) - (5)].str), (yyvsp[(5) - (5)].str)); -+ ;} -+ break; -+ -+ case 15: -+#line 120 "prom_parse.y" -+ { -+ sprintf((yyval.str), "/%s", (yyvsp[(2) - (3)].str)); -+ ;} -+ break; -+ -+ case 16: -+#line 123 "prom_parse.y" -+ { -+ sprintf((yyval.str), "/%s@%s", (yyvsp[(2) - (5)].str), (yyvsp[(4) - (5)].str)); -+ ;} -+ break; -+ -+ case 17: -+#line 126 "prom_parse.y" -+ { -+ sprintf((yyval.str), "/%s@%s,%s", (yyvsp[(2) - (7)].str), (yyvsp[(4) - (7)].str), (yyvsp[(6) - (7)].str)); -+ ;} -+ break; -+ -+ case 18: -+#line 131 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s/%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 19: -+#line 136 "prom_parse.y" -+ { -+ sprintf((yyval.str), ":%s", (yyvsp[(2) - (2)].str)); -+ ;} -+ break; -+ -+ case 20: -+#line 139 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s,%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 21: -+#line 142 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s,%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 22: -+#line 147 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s=%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 23: -+#line 152 "prom_parse.y" -+ { -+ sprintf((yyval.str), ",%s", (yyvsp[(2) - (2)].str)); -+ ;} -+ break; -+ -+ case 24: -+#line 155 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s,%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 25: -+#line 158 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s,%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 26: -+#line 163 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 27: -+#line 166 "prom_parse.y" -+ { -+ /* luns > 0 are the SAM-3+ hex representation. */ -+ obp_parm_hexnum(ofwdev, (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ sprintf((yyval.str), "%s=%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 28: -+#line 171 "prom_parse.y" -+ { -+ obp_parm_addr(ofwdev, (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ sprintf((yyval.str), "%s=%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 29: -+#line 175 "prom_parse.y" -+ { -+ obp_parm_iqn(ofwdev, (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ sprintf((yyval.str), "%s=%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 30: -+#line 179 "prom_parse.y" -+ { -+ obp_parm_hexnum(ofwdev, (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ sprintf((yyval.str), "%s=%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 31: -+#line 183 "prom_parse.y" -+ { -+ obp_parm_str(ofwdev, (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ sprintf((yyval.str), "%s=%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 32: -+#line 189 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 33: -+#line 192 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s,%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 34: -+#line 197 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", obp_qual_set(ofwdev, (yyvsp[(1) - (1)].str))); -+ ;} -+ break; -+ -+ case 35: -+#line 200 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 36: -+#line 205 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 37: -+#line 208 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 38: -+#line 213 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 39: -+#line 218 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 40: -+#line 221 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 41: -+#line 226 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 42: -+#line 229 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s::", (yyvsp[(1) - (2)].str)); -+ ;} -+ break; -+ -+ case 43: -+#line 232 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s::%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 44: -+#line 235 "prom_parse.y" -+ { -+ sprintf((yyval.str), "::%s", (yyvsp[(2) - (2)].str)); -+ ;} -+ break; -+ -+ case 45: -+#line 240 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 46: -+#line 243 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s:%s", (yyvsp[(1) - (3)].str), (yyvsp[(3) - (3)].str)); -+ ;} -+ break; -+ -+ case 47: -+#line 248 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s", (yyvsp[(1) - (1)].str)); -+ ;} -+ break; -+ -+ case 48: -+#line 251 "prom_parse.y" -+ { -+ sprintf((yyval.str), "%s%s", (yyvsp[(1) - (2)].str), (yyvsp[(2) - (2)].str)); -+ ;} -+ break; -+ -+ case 49: -+#line 254 "prom_parse.y" -+ { -+ sprintf((yyval.str), "@%s,%s%s", (yyvsp[(2) - (5)].str), (yyvsp[(4) - (5)].str), (yyvsp[(5) - (5)].str)); -+ ;} -+ break; -+ -+ case 50: -+#line 259 "prom_parse.y" -+ { -+ sprintf((yyval.str), ":%s", (yyvsp[(2) - (2)].str)); -+ ;} -+ break; -+ -+ case 51: -+#line 262 "prom_parse.y" -+ { -+ sprintf((yyval.str), ":%s,%s", (yyvsp[(2) - (4)].str), (yyvsp[(4) - (4)].str)); -+ ;} -+ break; -+ -+ -+/* Line 1267 of yacc.c. */ -+#line 1848 "prom_parse.tab.c" -+ default: break; -+ } -+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); -+ -+ YYPOPSTACK (yylen); -+ yylen = 0; -+ YY_STACK_PRINT (yyss, yyssp); -+ -+ *++yyvsp = yyval; -+ *++yylsp = yyloc; -+ -+ /* Now `shift' the result of the reduction. Determine what state -+ that goes to, based on the state we popped back to and the rule -+ number reduced by. */ -+ -+ yyn = yyr1[yyn]; -+ -+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; -+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) -+ yystate = yytable[yystate]; -+ else -+ yystate = yydefgoto[yyn - YYNTOKENS]; -+ -+ goto yynewstate; -+ -+ -+/*------------------------------------. -+| yyerrlab -- here on detecting error | -+`------------------------------------*/ -+yyerrlab: -+ /* If not already recovering from an error, report this error. */ -+ if (!yyerrstatus) -+ { -+ ++yynerrs; -+#if ! YYERROR_VERBOSE -+ yyerror (ofwdev, YY_("syntax error")); -+#else -+ { -+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); -+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) -+ { -+ YYSIZE_T yyalloc = 2 * yysize; -+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) -+ yyalloc = YYSTACK_ALLOC_MAXIMUM; -+ if (yymsg != yymsgbuf) -+ YYSTACK_FREE (yymsg); -+ yymsg = (char *) YYSTACK_ALLOC (yyalloc); -+ if (yymsg) -+ yymsg_alloc = yyalloc; -+ else -+ { -+ yymsg = yymsgbuf; -+ yymsg_alloc = sizeof yymsgbuf; -+ } -+ } -+ -+ if (0 < yysize && yysize <= yymsg_alloc) -+ { -+ (void) yysyntax_error (yymsg, yystate, yychar); -+ yyerror (ofwdev, yymsg); -+ } -+ else -+ { -+ yyerror (ofwdev, YY_("syntax error")); -+ if (yysize != 0) -+ goto yyexhaustedlab; -+ } -+ } -+#endif -+ } -+ -+ yyerror_range[0] = yylloc; -+ -+ if (yyerrstatus == 3) -+ { -+ /* If just tried and failed to reuse look-ahead token after an -+ error, discard it. */ -+ -+ if (yychar <= YYEOF) -+ { -+ /* Return failure if at end of input. */ -+ if (yychar == YYEOF) -+ YYABORT; -+ } -+ else -+ { -+ yydestruct ("Error: discarding", -+ yytoken, &yylval, &yylloc, ofwdev); -+ yychar = YYEMPTY; -+ } -+ } -+ -+ /* Else will try to reuse look-ahead token after shifting the error -+ token. */ -+ goto yyerrlab1; -+ -+ -+/*---------------------------------------------------. -+| yyerrorlab -- error raised explicitly by YYERROR. | -+`---------------------------------------------------*/ -+yyerrorlab: -+ -+ /* Pacify compilers like GCC when the user code never invokes -+ YYERROR and the label yyerrorlab therefore never appears in user -+ code. */ -+ if (/*CONSTCOND*/ 0) -+ goto yyerrorlab; -+ -+ yyerror_range[0] = yylsp[1-yylen]; -+ /* Do not reclaim the symbols of the rule which action triggered -+ this YYERROR. */ -+ YYPOPSTACK (yylen); -+ yylen = 0; -+ YY_STACK_PRINT (yyss, yyssp); -+ yystate = *yyssp; -+ goto yyerrlab1; -+ -+ -+/*-------------------------------------------------------------. -+| yyerrlab1 -- common code for both syntax error and YYERROR. | -+`-------------------------------------------------------------*/ -+yyerrlab1: -+ yyerrstatus = 3; /* Each real token shifted decrements this. */ -+ -+ for (;;) -+ { -+ yyn = yypact[yystate]; -+ if (yyn != YYPACT_NINF) -+ { -+ yyn += YYTERROR; -+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) -+ { -+ yyn = yytable[yyn]; -+ if (0 < yyn) -+ break; -+ } -+ } -+ -+ /* Pop the current state because it cannot handle the error token. */ -+ if (yyssp == yyss) -+ YYABORT; -+ -+ yyerror_range[0] = *yylsp; -+ yydestruct ("Error: popping", -+ yystos[yystate], yyvsp, yylsp, ofwdev); -+ YYPOPSTACK (1); -+ yystate = *yyssp; -+ YY_STACK_PRINT (yyss, yyssp); -+ } -+ -+ if (yyn == YYFINAL) -+ YYACCEPT; -+ -+ *++yyvsp = yylval; -+ -+ yyerror_range[1] = yylloc; -+ /* Using YYLLOC is tempting, but would change the location of -+ the look-ahead. YYLOC is available though. */ -+ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2); -+ *++yylsp = yyloc; -+ -+ /* Shift the error token. */ -+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); -+ -+ yystate = yyn; -+ goto yynewstate; -+ -+ -+/*-------------------------------------. -+| yyacceptlab -- YYACCEPT comes here. | -+`-------------------------------------*/ -+yyacceptlab: -+ yyresult = 0; -+ goto yyreturn; -+ -+/*-----------------------------------. -+| yyabortlab -- YYABORT comes here. | -+`-----------------------------------*/ -+yyabortlab: -+ yyresult = 1; -+ goto yyreturn; -+ -+#ifndef yyoverflow -+/*-------------------------------------------------. -+| yyexhaustedlab -- memory exhaustion comes here. | -+`-------------------------------------------------*/ -+yyexhaustedlab: -+ yyerror (ofwdev, YY_("memory exhausted")); -+ yyresult = 2; -+ /* Fall through. */ -+#endif -+ -+yyreturn: -+ if (yychar != YYEOF && yychar != YYEMPTY) -+ yydestruct ("Cleanup: discarding lookahead", -+ yytoken, &yylval, &yylloc, ofwdev); -+ /* Do not reclaim the symbols of the rule which action triggered -+ this YYABORT or YYACCEPT. */ -+ YYPOPSTACK (yylen); -+ YY_STACK_PRINT (yyss, yyssp); -+ while (yyssp != yyss) -+ { -+ yydestruct ("Cleanup: popping", -+ yystos[*yyssp], yyvsp, yylsp, ofwdev); -+ YYPOPSTACK (1); -+ } -+#ifndef yyoverflow -+ if (yyss != yyssa) -+ YYSTACK_FREE (yyss); -+#endif -+#if YYERROR_VERBOSE -+ if (yymsg != yymsgbuf) -+ YYSTACK_FREE (yymsg); -+#endif -+ /* Make sure YYID is used. */ -+ return YYID (yyresult); -+} -+ -+ -+#line 267 "prom_parse.y" -+ -+ -diff --git a/utils/fwparam_ibft/prom_parse.tab.h b/utils/fwparam_ibft/prom_parse.tab.h -new file mode 100644 -index 0000000..d161994 ---- /dev/null -+++ b/utils/fwparam_ibft/prom_parse.tab.h -@@ -0,0 +1,106 @@ -+/* A Bison parser, made by GNU Bison 2.3. */ -+ -+/* Skeleton interface for Bison's Yacc-like parsers in C -+ -+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 -+ Free Software Foundation, Inc. -+ -+ This program is free software; you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation; either version 2, 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., 51 Franklin Street, Fifth Floor, -+ Boston, MA 02110-1301, USA. */ -+ -+/* As a special exception, you may create a larger work that contains -+ part or all of the Bison parser skeleton and distribute that work -+ under terms of your choice, so long as that work isn't itself a -+ parser generator using the skeleton or a modified version thereof -+ as a parser skeleton. Alternatively, if you modify or redistribute -+ the parser skeleton itself, you may (at your option) remove this -+ special exception, which will cause the skeleton and the resulting -+ Bison output files to be licensed under the GNU General Public -+ License without this special exception. -+ -+ This special exception was added by the Free Software Foundation in -+ version 2.2 of Bison. */ -+ -+/* Tokens. */ -+#ifndef YYTOKENTYPE -+# define YYTOKENTYPE -+ /* Put the tokens into the symbol table, so that GDB and other debuggers -+ know about them. */ -+ enum yytokentype { -+ BUSNAME = 258, -+ BOOTDEV = 259, -+ IPV4 = 260, -+ IQN = 261, -+ OBPPARM = 262, -+ OBPQUAL = 263, -+ HEX4 = 264, -+ HEX16 = 265, -+ VDEVICE = 266, -+ VDEVINST = 267, -+ VDEVDEV = 268, -+ VDEVRAW = 269, -+ CHOSEN = 270, -+ FILENAME = 271 -+ }; -+#endif -+/* Tokens. */ -+#define BUSNAME 258 -+#define BOOTDEV 259 -+#define IPV4 260 -+#define IQN 261 -+#define OBPPARM 262 -+#define OBPQUAL 263 -+#define HEX4 264 -+#define HEX16 265 -+#define VDEVICE 266 -+#define VDEVINST 267 -+#define VDEVDEV 268 -+#define VDEVRAW 269 -+#define CHOSEN 270 -+#define FILENAME 271 -+ -+ -+ -+ -+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED -+typedef union YYSTYPE -+#line 33 "prom_parse.y" -+{ -+ char str[256]; -+} -+/* Line 1489 of yacc.c. */ -+#line 85 "prom_parse.tab.h" -+ YYSTYPE; -+# define yystype YYSTYPE /* obsolescent; will be withdrawn */ -+# define YYSTYPE_IS_DECLARED 1 -+# define YYSTYPE_IS_TRIVIAL 1 -+#endif -+ -+extern YYSTYPE yylval; -+ -+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED -+typedef struct YYLTYPE -+{ -+ int first_line; -+ int first_column; -+ int last_line; -+ int last_column; -+} YYLTYPE; -+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ -+# define YYLTYPE_IS_DECLARED 1 -+# define YYLTYPE_IS_TRIVIAL 1 -+#endif -+ -+extern YYLTYPE yylloc; -diff --git a/utils/fwparam_ibft/prom_parse.y b/utils/fwparam_ibft/prom_parse.y -new file mode 100644 -index 0000000..23302d7 ---- /dev/null -+++ b/utils/fwparam_ibft/prom_parse.y -@@ -0,0 +1,267 @@ -+/* -+ * Copyright (C) IBM Corporation. 2007 -+ * Author: Doug Maxey -+ * -+ * 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, see . -+ */ -+ -+/* - DEFINITION section. */ -+ -+%{ -+ /* literal block. include lines, decls, defns. */ -+//#define YYDEBUG 1 -+#if YYDEBUG -+#define DPRINT(fmt,...) printf(fmt,__VA_ARGS__) -+#else -+#define DPRINT(fmt,...) do {} while(0) -+#endif -+#include "prom_parse.h" -+#include "iscsi_obp.h" -+ -+%} -+%union { -+ char str[256]; -+} -+ -+/* definitions. */ -+%token BUSNAME BOOTDEV -+%token IPV4 IQN -+%token OBPPARM OBPQUAL -+%token HEX4 HEX16 -+%token VDEVICE VDEVINST VDEVDEV VDEVRAW -+%token CHOSEN -+%token FILENAME -+ -+%type devpath busses bus bootdev -+%type disklabel diskpart -+%type vdevice vdev_parms vdev_parm -+%type obp_quals obp_qual obp_params obp_param -+%type ipaddr ipv4 ipv6 -+%type hexpart hexseq -+ -+%locations -+%parse-param {struct ofw_dev *ofwdev} -+ -+%% -+ -+devpath: '/' { -+ DPRINT("****rootonly: \"%s\"\n", "/"); -+ } -+ | '/' busses bootdev { -+ DPRINT("****devpath busses:\n/%s/%s\n", $2, $3); -+ } -+ | '/' busses bootdev disklabel { -+ ofwdev->dev_path = malloc(strlen($2) + -+ strlen($3) + 3); -+ sprintf(ofwdev->dev_path, "/%s%s", $2, $3); -+ DPRINT("****devpath busses bootdev " -+ "disklabel:\n/%s/%s%s\n", -+ $2, $3, $4); -+ } -+ | '/' busses bootdev obp_quals obp_params { -+ ofwdev->dev_path = malloc(strlen($2) + -+ strlen($3) + 3); -+ sprintf(ofwdev->dev_path, "/%s%s", $2, $3); -+ DPRINT("****busses bootdev obp_quals obp_parms:\n" -+ "/%s/%s:%s%s\n", -+ $2, $3, $4, $5); -+ } -+ | '/' busses bootdev obp_quals obp_params disklabel { -+ ofwdev->dev_path = malloc(strlen($2) + -+ strlen($3) + 3); -+ sprintf(ofwdev->dev_path, "/%s%s", $2, $3); -+ DPRINT("****busses bootdev obp_quals obp_parms " -+ "disklabel:\n/%s:%s%s%s\n", $2, $4, $5, $6); -+ } -+ | '/' vdevice bootdev vdev_parms obp_quals obp_params disklabel { -+ DPRINT("****vdevice bootdev obp_parms " -+ "disklabel:\n/%s:%s%s%s%s\n", -+ $2, $4, $5, $6, $7); -+ } -+ ; -+ -+busses: bus { -+ strcpy($$, $1); -+ } -+ | busses '/' bus { -+ sprintf($$, "%s/%s", $1, $3); -+ } -+ ; -+ -+bus: BUSNAME { -+ strcpy($$, $1); -+ } -+ | BUSNAME '@' HEX4 { -+ sprintf($$, "%s@%s", $1, $3); -+ } -+ | BUSNAME '@' HEX4 ',' HEX4 { -+ sprintf($$, "%s@%s,%s", $1, $3, $5); -+ } -+ | BUSNAME '@' HEX16 { -+ sprintf($$, "%s@%s", $1, $3); -+ } -+ | BUSNAME ',' HEX4 '@' HEX16 { -+ sprintf($$, "%s,%s@%s", $1, $3, $5); -+ } -+ ; -+ -+ -+bootdev: '/' BOOTDEV ':' { -+ sprintf($$, "/%s", $2); -+ } -+ | '/' BOOTDEV '@' HEX4 ':' { -+ sprintf($$, "/%s@%s", $2, $4); -+ } -+ | '/' BOOTDEV '@' HEX4 ',' HEX4 ':' { -+ sprintf($$, "/%s@%s,%s", $2, $4, $6); -+ } -+ ; -+ -+vdevice: VDEVICE '/' VDEVINST { -+ sprintf($$, "%s/%s", $1, $3); -+ } -+ ; -+ -+vdev_parms: ':' vdev_parm { -+ sprintf($$, ":%s", $2); -+ } -+ | vdev_parms ',' vdev_parm { -+ sprintf($$, "%s,%s", $1, $3); -+ } -+ | vdev_parms ',' VDEVRAW { -+ sprintf($$, "%s,%s", $1, $3); -+ } -+ ; -+ -+vdev_parm: VDEVDEV '=' CHOSEN { -+ sprintf($$, "%s=%s", $1, $3); -+ } -+ ; -+ -+obp_params: ',' obp_param { -+ sprintf($$, ",%s", $2); -+ } -+ | obp_params ',' obp_param { -+ sprintf($$, "%s,%s", $1, $3); -+ } -+ | obp_params ',' disklabel { -+ sprintf($$, "%s,%s", $1, $3); -+ } -+ ; -+ -+obp_param: HEX4 { -+ sprintf($$, "%s", $1); -+ } -+ | OBPPARM '=' HEX16 { -+ /* luns > 0 are the SAM-3+ hex representation. */ -+ obp_parm_hexnum(ofwdev, $1, $3); -+ sprintf($$, "%s=%s", $1, $3); -+ } -+ | OBPPARM '=' ipaddr { -+ obp_parm_addr(ofwdev, $1, $3); -+ sprintf($$, "%s=%s", $1, $3); -+ } -+ | OBPPARM '=' IQN { -+ obp_parm_iqn(ofwdev, $1, $3); -+ sprintf($$, "%s=%s", $1, $3); -+ } -+ | OBPPARM '=' HEX4 { -+ obp_parm_hexnum(ofwdev, $1, $3); -+ sprintf($$, "%s=%s", $1, $3); -+ } -+ | OBPPARM '=' FILENAME { -+ obp_parm_str(ofwdev, $1, $3); -+ sprintf($$, "%s=%s", $1, $3); -+ } -+ ; -+ -+obp_quals: obp_qual { -+ sprintf($$, "%s", $1); -+ } -+ | obp_quals ',' obp_qual { -+ sprintf($$, "%s,%s", $1, $3); -+ } -+ ; -+ -+obp_qual: OBPQUAL { -+ sprintf($$, "%s", obp_qual_set(ofwdev, $1)); -+ } -+ | vdev_parm { -+ sprintf($$, "%s", $1); -+ } -+ ; -+ -+ipaddr: ipv4 { -+ sprintf($$, "%s", $1); -+ } -+ | ipv6 { -+ sprintf($$, "%s", $1); -+ } -+ ; -+ -+ipv4: IPV4 { -+ sprintf($$, "%s", $1); -+ } -+ ; -+ -+ipv6: hexpart { -+ sprintf($$, "%s", $1); -+ } -+ | hexpart ':' ipv4 { -+ sprintf($$, "%s:%s", $1, $3); -+ } -+ ; -+ -+hexpart: hexseq { -+ sprintf($$, "%s", $1); -+ } -+ | hexpart "::" { -+ sprintf($$, "%s::", $1); -+ } -+ | hexpart "::" hexseq { -+ sprintf($$, "%s::%s", $1, $3); -+ } -+ | "::" hexseq { -+ sprintf($$, "::%s", $2); -+ } -+ ; -+ -+hexseq: HEX4 { -+ sprintf($$, "%s", $1); -+ } -+ | hexseq ":" HEX4 { -+ sprintf($$, "%s:%s", $1, $3); -+ } -+ ; -+ -+disklabel: diskpart { -+ sprintf($$, "%s", $1); -+ } -+ | HEX4 diskpart { -+ sprintf($$, "%s%s", $1, $2); -+ } -+ | '@' HEX4 ',' HEX4 diskpart { -+ sprintf($$, "@%s,%s%s", $2, $4, $5); -+ } -+ ; -+ -+diskpart: ':' HEX4 { -+ sprintf($$, ":%s", $2); -+ } -+ | ':' HEX4 ',' FILENAME { -+ sprintf($$, ":%s,%s", $2, $4); -+ } -+ ; -+ -+%% -diff --git a/utils/iscsi_discovery b/utils/iscsi_discovery -old mode 100644 -new mode 100755 -index 7c7083f..9f1e7cf ---- a/utils/iscsi_discovery -+++ b/utils/iscsi_discovery -@@ -24,45 +24,84 @@ - # * tries to login - # * if succeeds, - # o logout, --# o mark record autmatic -+# o mark record autmatic - # * else - # o reset transport type to TCP - # o try to login - # o if succeeded - # + logout --# + mark record automatic -+# + mark record automatic - # - -+usage() -+{ -+ echo "Usage: $0 [-p ] [-d] [-t [-f]] [-m] [-l]" -+ echo "Options:" -+ echo "-p set the port number (defualt is 3260)." -+ echo "-d print debugging information" -+ echo "-t set trasnpot (default is tcp)." -+ echo "-f force specific transport -disable the fallback to tcp (default is fallback enabled)." -+ echo " force the transport specified by the argument of the -t flag." -+ echo "-m manual startup - will set manual startup (default is automatic startup)." -+ echo "-l login to the new discovered nodes (defualt is false)." -+} -+ - dbg() - { -- $debug || echo $@ -+ $debug && echo $@ - } - - initialize() - { - trap "exit" 2 -- usage="Usage: $0 [-d] []" - debug=false -+ force="0" -+ log_out="1" -+ startup_manual="0" -+ #set defualt transport to tcp -+ transport=tcp -+ #set defualt port to 3260 -+ port=3260; - } - - parse_cmdline() - { -- if [ "$1" = "-d" ]; then -- debug=true -- shift -- fi - if [ $# -lt 1 ]; then -- echo ${usage} -+ usage - exit 1 - fi - -- ip=$1 -- port=${2:-3260} -+ # check if the IP address is valid -+ ip=`echo $1 | awk -F'.' '$1 != "" && $1 <=255 && $2 != "" && $2 <= 255 && $3 != "" && $3 <= 255 && $4 != "" && $4 <= 255 {print $0}'` -+ if [ -z "$ip" ]; then -+ echo "$1 is not a vaild IP address!" -+ exit 1 -+ fi -+ shift -+ while getopts "dfmlt:p:" options; do -+ case $options in -+ d ) debug=true;; -+ f ) force="1";; -+ t ) transport=$OPTARG;; -+ p ) port=$OPTARG;; -+ m ) startup_manual="1";; -+ l ) log_out=0;; -+ \? ) usage -+ exit 1;; -+ * ) usage -+ exit 1;; -+ esac -+ done - } - -- - discover() - { -+ # If open-iscsi is already logged in to the portal, exit -+ if [ $(iscsiadm -m session | grep -c ${ip}:${port}) -ne 0 ]; then -+ echo "Please logout from all targets on ${ip}:${port} before trying to run discovery on that portal" -+ exit 2 -+ fi -+ - connected=0 - discovered=0 - df=/tmp/discovered.$$ -@@ -79,21 +118,28 @@ discover() - if [ ${discovered} = 0 ]; then - echo "failed to discover targets at ${ip}" - exit 2 -- else -- echo "discovered ${discovered} targets at ${ip}, connected to ${connected}" -+ else -+ echo "discovered ${discovered} targets at ${ip}" - fi - /bin/rm -f ${df} - } - --set_auto_if_login() -+try_login() - { -+ if [ "$startup_manual" != "1" ]; then -+ iscsiadm -m node --targetname ${target} --portal ${portal} --op update -n node.conn[0].startup -v automatic -+ fi - iscsiadm -m node --targetname ${target} --portal ${portal} --login >/dev/null 2>&1 - ret=$? - if [ ${ret} = 0 ]; then -- iscsiadm -m node --targetname ${target} --portal ${portal} --logout -- iscsiadm -m node --targetname ${target} --portal ${portal} --op update -n node.conn[0].startup -v automatic - echo "Set target ${target} to automatic login over ${transport} to portal ${portal}" - ((connected++)) -+ if [ "$log_out" = "1" ]; then -+ iscsiadm -m node --targetname ${target} --portal ${portal} --logout -+ fi -+ else -+ echo "Cannot login over ${transport} to portal ${portal}" -+ iscsiadm -m node --targetname ${target} --portal ${portal} --op update -n node.conn[0].startup -v manual - fi - return ${ret} - } -@@ -101,24 +147,42 @@ set_auto_if_login() - set_transport() - { - transport=$1 -+ if [ "$transport" == "iser" ];then -+ iscsiadm -m node --targetname ${target} --portal ${portal} \ -+ --op update -n node.conn[0].iscsi.HeaderDigest -v None -+ iscsiadm -m node --targetname ${target} --portal ${portal} \ -+ --op update -n node.conn[0].iscsi.DataDigest -v None -+ fi -+ transport_name=`iscsiadm -m node -p ${portal} -T ${target} |awk '/transport_name/ {print $1}'` - iscsiadm -m node --targetname ${target} --portal ${portal} \ -- --op update -n node.conn[0].iscsi.HeaderDigest -v None -- iscsiadm -m node --targetname ${target} --portal ${portal} \ -- --op update -n node.transport_name -v ${transport} -+ --op update -n ${transport_name} -v ${transport} - } - - select_transport() - { -- set_transport iser -- dbg "Testing iser-login to target ${target} portal ${portal}" -- set_auto_if_login -- if [ $? != 0 ]; then -+ set_transport $transport -+ dbg "Testing $transport-login to target ${target} portal ${portal}" -+ try_login; -+ if [ $? != 0 -a "$force" = "0" ]; then - set_transport tcp - dbg "starting to test tcp-login to target ${target} portal ${portal}" -- set_auto_if_login -+ try_login; -+ fi -+} -+ -+check_iscsid() -+{ -+ #check if iscsid is running -+ pidof iscsid &>/dev/null -+ ret=$? -+ if [ $ret -ne 0 ]; then -+ echo "iscsid is not running" -+ echo "Exiting..." -+ exit 1 - fi - } - -+check_iscsid - initialize - parse_cmdline "$@" - discover diff --git a/open-iscsi-ibft-fill-initiator-values b/open-iscsi-ibft-fill-initiator-values deleted file mode 100644 index 7d93a3e..0000000 --- a/open-iscsi-ibft-fill-initiator-values +++ /dev/null @@ -1,110 +0,0 @@ -commit 18cbe8c5f68e5e545d02f773da03f52deadfd45b -Author: Hannes Reinecke -Date: Wed Mar 12 10:36:30 2008 +0100 - - Fill in initiator values for iBFT context - - The iBFT context already contains the fields for the initiator, - so we can as well fill them with something sensible. - - Signed-off-by: Hannes Reinecke - -diff --git a/include/fw_context.h b/include/fw_context.h -index 47ac6ae..f6999e3 100644 ---- a/include/fw_context.h -+++ b/include/fw_context.h -@@ -33,8 +33,9 @@ struct boot_context { - char chap_password_in[16]; - char iface[42]; - char mac[18]; -- char ipaddr[18]; -- char mask[18]; -+ char ipaddr[32]; -+ char mask[19]; -+ char gwaddr[32]; - char lun[17]; - char vlan[15]; - char isid[10]; -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index 4397f94..3801028 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -150,6 +150,32 @@ format_ipaddr(char *buf, size_t size, uint8_t *ip) - - } - -+void -+format_netmask(char *buf, size_t size, uint8_t mask) -+{ -+ uint32_t tmp; -+ -+ tmp = 0xffffffff << (32 - mask); -+ sprintf(buf,"%d.%d.%d.%d", -+ (tmp >> 24) & 0xff, -+ (tmp >> 16) & 0xff, -+ (tmp >> 8) & 0xff, -+ tmp & 0xff); -+} -+ -+void -+format_mac(char *buf, size_t size, uint8_t *mac) -+{ -+ int i; -+ -+ for (i = 0; i < 5; i++) { -+ sprintf(buf, "%02x:", mac[i]); -+ buf += 3; -+ } -+ sprintf(buf, "%02x", mac[i]); -+} -+ -+ - /* - * Dump the 16 byte ipaddr, as IPV6 or IPV4. - */ -@@ -385,6 +411,45 @@ dump_ibft(void *ibft_loc, struct boot_context *context) - (char *)ibft_loc+initiator->initiator_name_off, - initiator->initiator_name_len + 1); - -+ if (nic0 && (nic0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) { -+ format_ipaddr(buf, sizeof(buf), -+ nic0->ip_addr); -+ strcpy((char *)context->ipaddr, buf); -+ -+ format_ipaddr(buf, sizeof(buf), -+ nic0->gateway); -+ strcpy((char *)context->gwaddr, buf); -+ -+ format_mac(buf, sizeof(buf), -+ nic0->mac); -+ strcpy((char *)context->mac, buf); -+ -+ format_netmask(buf, sizeof(buf), -+ nic0->subnet_mask_prefix); -+ strcpy((char *)context->mask, buf); -+ } -+ -+ if (nic1 && (nic1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) { -+ format_ipaddr(buf, sizeof(buf), -+ nic1->ip_addr); -+ strncpy((char *)context->ipaddr, buf, -+ sizeof(buf)); -+ format_ipaddr(buf, sizeof(buf), -+ nic1->gateway); -+ strncpy((char *)context->gwaddr, buf, -+ sizeof(buf)); -+ -+ format_mac(buf, sizeof(buf), -+ nic1->mac); -+ strncpy((char *)context->mac, buf, -+ sizeof(buf)); -+ -+ format_netmask(buf, sizeof(buf), -+ nic1->subnet_mask_prefix); -+ strncpy((char *)context->mask, buf, -+ sizeof(buf)); -+ } -+ - if (tgt0 && (tgt0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) { - strncpy((char *)context->targetname, - (char *)(ibft_loc+tgt0->tgt_name_off), diff --git a/open-iscsi-ibft-print-ifspec b/open-iscsi-ibft-print-ifspec deleted file mode 100644 index e59b8dc..0000000 --- a/open-iscsi-ibft-print-ifspec +++ /dev/null @@ -1,105 +0,0 @@ -commit 01ce5f6e4f9859aab2b3a7b36ca59e010af1d10a -Author: Hannes Reinecke -Date: Wed Mar 12 10:37:47 2008 +0100 - - Implement '-i' to print out ifspec line - - For the initrd we can easily print out the required - ifspec line, saving us quite some hassle there. - - Signed-off-by: Hannes Reinecke - -diff --git a/utils/fwparam_ibft/fwparam_main.c b/utils/fwparam_ibft/fwparam_main.c -index 3a91b52..95a1a4d 100644 ---- a/utils/fwparam_ibft/fwparam_main.c -+++ b/utils/fwparam_ibft/fwparam_main.c -@@ -27,22 +27,56 @@ - #include - #include - #include -+#include -+#include - - #include "fwparam_ibft.h" - #include "fw_context.h" - - extern int debug; - -+int get_ifnum_from_mac(char *mac) -+{ -+ int ifnum = -1, fd; -+ DIR *d; -+ struct dirent *dent; -+ char buf[20], attr[64]; -+ -+ d = opendir("/sys/class/net"); -+ while ((dent = readdir(d))) { -+ if (dent->d_name[0] == '.') -+ continue; -+ -+ sprintf(attr,"/sys/class/net/%s/address", dent->d_name); -+ fd = open(attr,O_RDONLY); -+ if (!fd) -+ continue; -+ -+ read(fd, buf, 18); -+ close(fd); -+ -+ if (strncmp(mac, buf, strlen(mac))) -+ continue; -+ -+ if (sscanf(dent->d_name,"eth%d", &ifnum) == 1) -+ break; -+ } -+ closedir(d); -+ -+ return ifnum; -+} -+ - int - main (int argc, char **argv) - { -- int option, ret; -+ int option, ret, do_ipconfig = 0; - char *progname, *filebuf = NULL; -+ struct boot_context ctxt; - - progname = argv[0]; - - while (1) { -- option = getopt(argc, argv, "f:vhb"); -+ option = getopt(argc, argv, "f:ivhb"); - if (option == -1) - break; - switch (option) { -@@ -52,6 +86,9 @@ main (int argc, char **argv) - case 'f': - filebuf = optarg; - break; -+ case 'i': -+ do_ipconfig = 1; -+ break; - case 'v': - debug++; - break; -@@ -67,7 +104,18 @@ main (int argc, char **argv) - } - } - -- ret = fwparam_ibft(NULL, filebuf); -- -+ if (!do_ipconfig) -+ ret = fwparam_ibft(NULL, filebuf); -+ else { -+ ret = fwparam_ibft(&ctxt, filebuf); -+ if (!ret) -+ /* -+ * Format is: -+ * ipaddr:peeraddr:gwaddr:mask:hostname:iface:none -+ */ -+ printf("%s::%s:%s::eth%d:ibft\n", -+ ctxt.ipaddr, ctxt.gwaddr, -+ ctxt.mask, get_ifnum_from_mac(ctxt.mac)); -+ } - exit(ret); - } diff --git a/open-iscsi-ibft-scan-memory-area b/open-iscsi-ibft-scan-memory-area deleted file mode 100644 index f496eed..0000000 --- a/open-iscsi-ibft-scan-memory-area +++ /dev/null @@ -1,48 +0,0 @@ -commit a58c90313743e5e1614aa8a0522d6a441dc79ed3 -Author: Hannes Reinecke -Date: Mon Apr 21 11:28:25 2008 +0200 - - Scan Memory area for iBFT correctly - - The iBFT might be constructed in the main memory area, too. - For this we don't have a ROM header, so we have to skip the - checks for this, too. - - Signed-off-by: Hannes Reinecke - -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index dc0fc6b..84502bb 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -502,18 +502,24 @@ char *search_ibft(unsigned char *start, int start_addr, int length) - { - unsigned char *cur_ptr, *rom_end; - struct ibft_table_hdr *ibft_hdr; -- unsigned char check_sum, rom_size; -+ unsigned char check_sum; -+ short rom_size = -1; - uint32_t i; - - cur_ptr = (unsigned char *)start; - while (cur_ptr < (start + length)) { -- if (memcmp(cur_ptr, ID_ROMEXT, strlen(ID_ROMEXT)) != 0) { -- /* Skip this block */ -- cur_ptr += 512; -- continue; -+ if (rom_size < 0) { -+ /* Scan the upper memory area */ -+ rom_size = 256; -+ } else { -+ /* Scan extenions in the ROM area */ -+ if (memcmp(cur_ptr, ID_ROMEXT, strlen(ID_ROMEXT)) != 0) { -+ /* Skip this block */ -+ cur_ptr += 512; -+ continue; -+ } -+ memcpy(&rom_size, cur_ptr + 2, 1); - } -- memcpy(&rom_size, cur_ptr + 2, 1); -- - if (debug > 1) - fprintf(stderr, "Found rom at %x of size %d\n", - ((int)(cur_ptr - start) + start_addr), diff --git a/open-iscsi-print-ibft-error-to-stderr b/open-iscsi-print-ibft-error-to-stderr deleted file mode 100644 index bbe8c0c..0000000 --- a/open-iscsi-print-ibft-error-to-stderr +++ /dev/null @@ -1,24 +0,0 @@ -commit daa06c2b0c0351fda9a667dabed426f976e40226 -Author: Hannes Reinecke -Date: Fri Apr 4 13:43:25 2008 +0200 - - fwparam_ibft: print messages to stderr - - When no iBFT was found a message would be printed to stdout, - confusing any parsing scripts. - - Signed-off-by: Hannes Reinecke - -diff --git a/utils/fwparam_ibft/fwparam_ibft.c b/utils/fwparam_ibft/fwparam_ibft.c -index 3801028..dc0fc6b 100644 ---- a/utils/fwparam_ibft/fwparam_ibft.c -+++ b/utils/fwparam_ibft/fwparam_ibft.c -@@ -612,7 +612,7 @@ fwparam_ibft(struct boot_context *context, const char *filepath) - if (ibft_loc) - ret = dump_ibft(ibft_loc, context); - else { -- printf("Could not find iBFT.\n"); -+ fprintf(stderr, "Could not find iBFT.\n"); - ret = -1; - } - munmap(filebuf, end_search); diff --git a/open-iscsi-start-target-before-initiator b/open-iscsi-start-target-before-initiator new file mode 100644 index 0000000..7553148 --- /dev/null +++ b/open-iscsi-start-target-before-initiator @@ -0,0 +1,13 @@ +diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse +index 23bbac0..d8b91cc 100644 +--- a/etc/initd/initd.suse ++++ b/etc/initd/initd.suse +@@ -5,7 +5,7 @@ + ### BEGIN INIT INFO + # Provides: iscsi + # Required-Start: $network +-# Should-Start: ++# Should-Start: iscsitarget + # Required-Stop: + # Should-Stop: + # Default-Start: 3 5 diff --git a/open-iscsi-fix-suse-build b/open-iscsi-suse-build-fixes similarity index 80% rename from open-iscsi-fix-suse-build rename to open-iscsi-suse-build-fixes index 0a05e2e..55597b5 100644 --- a/open-iscsi-fix-suse-build +++ b/open-iscsi-suse-build-fixes @@ -1,27 +1,18 @@ diff --git a/Makefile b/Makefile -index 8eb812c..167138f 100644 +index e405c9c..58bb24d 100644 --- a/Makefile +++ b/Makefile -@@ -15,7 +15,7 @@ etcdir = /etc - initddir = $(etcdir)/init.d - - MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8 --PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery utils/iscsi-iname -+PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery utils/iscsi-iname utils/fwparam_ibft/fwparam_ibft - INSTALL = install - ETCFILES = etc/iscsid.conf - IFACEFILES = etc/iface.example @@ -54,7 +54,7 @@ clean: install_etc install_iface install_doc install_kernel install_iname install: install_kernel install_programs install_doc install_etc \ - install_initd install_iname install_iface -+ install_initd ++ install_initd install_iface install_usr: install_programs install_doc install_etc \ install_initd install_iname install_iface diff --git a/kernel/Makefile b/kernel/Makefile -index bf67fec..268758d 100644 +index 3dae671..65a291a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -32,11 +32,16 @@ V ?= 0 @@ -42,7 +33,7 @@ index bf67fec..268758d 100644 $(KBUILD_BASE) modules # ============ BEGIN code for kernel_check and source patching ================ -@@ -58,9 +63,11 @@ cur_patched=cur_patched +@@ -59,9 +64,11 @@ cur_patched=cur_patched # check to see if code is unpatched unpatch_code=$(shell test -e $(cur_patched) && echo do_unpatch_code ) @@ -55,7 +46,7 @@ index bf67fec..268758d 100644 KERNEL_TARGET=linux_2_6_$(KSUBLEVEL) kernel_check: $(KERNEL_TARGET) -@@ -114,7 +121,11 @@ has_20to21_patch: $(20to21_patch) +@@ -128,7 +135,11 @@ has_24_patch: $(24_patch) # ============ END code for kernel_check and source patching ================= @@ -68,7 +59,7 @@ index bf67fec..268758d 100644 $(KBUILD_BASE) clean rm -f Module.symvers -@@ -164,7 +164,9 @@ ko = $(patsubst %.o,%.ko,$(obj-m)) +@@ -167,7 +178,9 @@ ko = $(patsubst %.o,%.ko,$(obj-m)) $(ko): all # now the actual command @@ -78,9 +69,9 @@ index bf67fec..268758d 100644 +install_kernel_obj: $(ko) $(KBUILD_BASE) modules_install INSTALL_MOD_DIR=$(INSTALL_MOD_DIR) INSTALL_MOD_PATH=$(INSTALL_MOD_PATH) - # vim: ft=make tw=72 sw=4 ts=4: + dpkg_divert: diff --git a/usr/Makefile b/usr/Makefile -index bf67fec..268758d 100644 +index 7794831..840510d 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -7,9 +7,11 @@ OSNAME=$(shell uname -s) diff --git a/open-iscsi-teardown-device-stack b/open-iscsi-teardown-device-stack deleted file mode 100644 index 0fc3ef4..0000000 --- a/open-iscsi-teardown-device-stack +++ /dev/null @@ -1,205 +0,0 @@ -commit 6005e4dab173f021b35da79e763ab0dd40d94a08 -Author: Hannes Reinecke -Date: Thu Apr 10 16:37:02 2008 +0200 - - Tear down device stack before logging out - - We have to tear down the entire device stack before logging - out of any iSCSI portal, otherwise the system won't be - able to do a clean umount. - - Signed-off-by: Hannes Reinecke - -diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse -index d8b91cc..18bd889 100644 ---- a/etc/initd/initd.suse -+++ b/etc/initd/initd.suse -@@ -26,6 +26,148 @@ ARGS="-c $CONFIG_FILE -p $PID_FILE" - # Reset status of this service - rc_reset - -+dm_major() -+{ -+ local maj -+ -+ [ -f /proc/devices ] || echo 0 -+ maj=$(sed -n 's/\([0-9]*\) device-mapper/\1/p' /proc/devices) -+ echo $maj -+} -+ -+umount_lun() -+{ -+ local dev=$1 -+ local d m t l -+ -+ cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do -+ if [ "$m" = "/" ] ; then -+ continue; -+ fi -+ l=$d -+ [ -L "$d" ] && l=$(readlink -f $d) -+ -+ if [ "${l##/dev/mapper}" != "$l" ] ; then -+ t=$(dmsetup info -c --noheadings -o minor ${l##/dev/mapper/}) -+ l="/dev/dm-$t" -+ fi -+ -+ if [ "$l" != "$dev" ] ; then -+ continue; -+ fi -+ -+ umount $d -+ break; -+ done -+} -+ -+teardown_md() -+{ -+ local dev=${1##/dev/} -+ local md_minor -+ -+ md_minor=$(cat /proc/mdstat | sed -n "s/\(md[0-9]*\) : .* $dev.*/\1/p") -+ -+ if [ "$md_minor" ] ; then -+ teardown_iscsi /dev/${md_minor} -+ mdadm -S /dev/${md_minor} -+ return 0 -+ fi -+ -+ return 1 -+} -+ -+teardown_dm() -+{ -+ local dev=${1##/dev/} -+ local dm_maj=$(dm_major) -+ local dm_min -+ local retval=1 -+ local tblname -+ -+ read dev_t < /sys/block/$dev/dev -+ -+ dmsetup table | sed -n "s/\(.*\): .* $dev_t .*/\1/p" | while read tblname; do -+ tbluuid=$(dmsetup info -c --noheadings -o uuid $tblname) -+ [ -z "$tbluuid" ] && continue -+ dm_min=$(dmsetup info -c --noheadings -o minor $tblname) -+ if [ ${tbluuid##mpath-} != "$tbluuid" ] ; then -+ teardown_iscsi /dev/dm-$dm_min -+ dmsetup remove $tblname -+ retval=0 -+ elif [ ${tbluuid##part} != "$tbluuid" ] ; then -+ teardown_iscsi /dev/dm-$dm_min -+ dmsetup remove $tblname -+ retval=0 -+ elif [ ${tbluuid##LVM-} != "$tbluuid" ] ; then -+ # We don't support stacking atop of LVM devices -+ umount_lun /dev/dm-$dm_min -+ dmsetup remove $tblname -+ retval=0 -+ fi -+ done -+ return $retval -+} -+ -+teardown_block() -+{ -+ local dev=${1##/dev} -+ local s -+ local p -+ -+ if [ ${dev##/sd} == $dev ] ; then -+ return 1 -+ fi -+ -+ p="/sys/block${dev%%[0-9]*}" -+ -+ if [ ! -d ${p} ] && [ ! -d ${p}/device ] ; then -+ continue; -+ fi -+ -+ s=$(cd -P ${p}/device && echo $PWD) -+ -+ case "$s" in -+ */session[0-9]*/*) -+ # This is an iSCSI device -+ umount_lun $dev -+ return 0 -+ ;; -+ esac -+ return 1 -+} -+ -+teardown_iscsi() -+{ -+ local dev=$1 -+ -+ teardown_md $dev && return -+ -+ teardown_dm $dev && return -+ -+ teardown_block $dev && return -+ -+ umount_lun $dev -+} -+ -+iscsi_umount_all_luns() -+{ -+ local d m dev p s -+ -+ for target in /sys/class/iscsi_session/session*/device/target* ; do -+ [ -e $target ] || continue; -+ cd -P $target; -+ for lun in *:*:*:*; do -+ cd -P /sys/class/scsi_device/$lun/device -+ for l in block:* ; do -+ [ -e $l ] || continue -+ dev=${l##block:} -+ teardown_iscsi /dev/$dev -+ done -+ done -+ done -+} -+ - iscsi_login_all_nodes() - { - echo -n "Setting up iSCSI targets: " -@@ -55,39 +197,6 @@ iscsi_logout_all_nodes() - return ${RETVAL:-0} - } - --iscsi_umount_all_luns() --{ -- local d m dev p s -- -- cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do -- if [ "$m" = "/" ] ; then -- continue; -- fi -- if [ -L "$d" ] ; then -- d=$(readlink -f $d) -- fi -- dev=${d##/dev} -- -- if [ "${dev##/sd}" = "$dev" ] ; then -- continue; -- fi -- p="/sys/block${dev%%[0-9]*}" -- -- if [ ! -d ${p} ] && [ ! -d ${p}/device ] ; then -- continue; -- fi -- -- s=$(cd -P ${p}/device && echo $PWD) -- -- case "$s" in -- */session[0-9]*/*) -- # This is an iSCSI device -- umount "$m" -- ;; -- esac -- done --} -- - iscsi_list_all_nodes() - { - # Check for active sessions diff --git a/open-iscsi-update-init-script b/open-iscsi-update-init-script new file mode 100644 index 0000000..67f4c6b --- /dev/null +++ b/open-iscsi-update-init-script @@ -0,0 +1,72 @@ +diff --git a/etc/initd/boot.suse b/etc/initd/boot.suse +index df64e21..e4a54a6 100644 +--- a/etc/initd/boot.suse ++++ b/etc/initd/boot.suse +@@ -10,8 +10,10 @@ + # Should-Stop: + # Default-Start: B + # Default-Stop: +-# Short-Description: Starts the iSCSI initiator daemon +-# ++# Short-Description: iSCSI initiator daemon root-fs support ++# Description: Starts the iSCSI initiator daemon if the ++# root-filesystem is on an iSCSI device ++# + ### END INIT INFO + + ISCSIADM=/sbin/iscsiadm +@@ -56,7 +58,7 @@ case "$1" in + rc_status -v + iscsi_mark_root_nodes + ;; +- stop) ++ stop|restart|reload) + rc_failed 0 + ;; + status) +@@ -68,13 +70,8 @@ case "$1" in + rc_status -v + fi + ;; +- restart) +- $0 stop +- sleep 1 +- $0 start +- ;; + *) +- echo "Usage: $0 {start|stop|status|restart}" ++ echo "Usage: $0 {start|stop|status|restart|reload}" + exit 1 + ;; + esac +diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse +index d8b91cc..5c42b88 100644 +--- a/etc/initd/initd.suse ++++ b/etc/initd/initd.suse +@@ -10,7 +10,9 @@ + # Should-Stop: + # Default-Start: 3 5 + # Default-Stop: +-# Short-Description: Starts and stops the iSCSI client initiator ++# Short-Description: iSCSI initiator daemon ++# Description: The iSCSI initator is used to create and ++# manage iSCSI connections to a iSCSI Target. + # + ### END INIT INFO + +@@ -190,13 +192,13 @@ case "$1" in + rc_status -v + fi + ;; +- restart) ++ restart|reload) + $0 stop + sleep 1 + $0 start + ;; + *) +- echo "Usage: $0 {start|stop|status|restart}" ++ echo "Usage: $0 {start|stop|status|restart|reload}" + exit 1 + ;; + esac diff --git a/open-iscsi-update-nodes b/open-iscsi-update-nodes deleted file mode 100644 index 42244f0..0000000 --- a/open-iscsi-update-nodes +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c -index 591ae8c..9e3d47c 100644 ---- a/usr/iscsiadm.c -+++ b/usr/iscsiadm.c -@@ -1604,7 +1604,7 @@ do_sofware_sendtargets(idbm_t *db, discovery_rec_t *drec, - * ops for them - */ - if (!op) -- op = OP_NEW | OP_DELETE | OP_UPDATE; -+ op = OP_NEW | OP_DELETE; - - drec->type = DISCOVERY_TYPE_SENDTARGETS; - rc = discovery_sendtargets(db, drec, &new_rec_list); diff --git a/open-iscsi.changes b/open-iscsi.changes index 5e9570a..b468ebf 100644 --- a/open-iscsi.changes +++ b/open-iscsi.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Thu Jul 24 12:04:10 CEST 2008 - hare@suse.de + +- Update to 2.0-870-rc1 + * Fix login redirection + * Fix NOP timeout handling + * Various small bugfixes +- Include mkinitrd scriptlets. + ------------------------------------------------------------------- Tue Apr 29 09:06:07 CEST 2008 - hare@suse.de diff --git a/open-iscsi.spec b/open-iscsi.spec index 0568c36..c1db034 100644 --- a/open-iscsi.spec +++ b/open-iscsi.spec @@ -1,5 +1,5 @@ # -# spec file for package open-iscsi (Version 2.0.869) +# spec file for package open-iscsi (Version 2.0.870) # # Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany. # This file and all modifications and additions to the pristine @@ -18,27 +18,21 @@ License: GPL v2 or later Group: Productivity/Networking/Other PreReq: %fillup_prereq %insserv_prereq AutoReqProv: on -Version: 2.0.869 +Version: 2.0.870 Release: 1 Provides: linux-iscsi Obsoletes: linux-iscsi %define iscsi_release 865 Summary: Linux* Open-iSCSI Software Initiator -Source: %{name}-2.0-865.tar.bz2 +Source: %{name}-2.0-870-rc1.tar.bz2 +Source5: mkinitrd-setup.sh +Source6: mkinitrd-boot.sh +Source7: mkinitrd-stop.sh Source11: iscsi-gen-initiatorname.sh -Patch1: %{name}-866.diff -Patch2: %{name}-git-update -Patch5: %{name}-format-luns -Patch22: %{name}-fwparam-scan-in-blocks -Patch24: %{name}-update-nodes -Patch30: %{name}-backport-fwparam_ibft -Patch31: %{name}-fix-suse-build -Patch32: %{name}-ibft-fill-initiator-values -Patch33: %{name}-ibft-print-ifspec -Patch37: %{name}-print-ibft-error-to-stderr -Patch38: %{name}-teardown-device-stack -Patch39: %{name}-fwparam_ppc-string-overflow -Patch40: %{name}-ibft-scan-memory-area +Patch1: %{name}-start-target-before-initiator +Patch2: %{name}-suse-build-fixes +Patch3: %{name}-fwparam_ppc-string-overflow +Patch4: %{name}-update-init-script BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@ -67,21 +61,11 @@ Authors: open-iscsi@googlegroups.com %prep -%setup -n %{name}-2.0-865 -%patch1 +%setup -n %{name}-2.0-870-rc1 +%patch1 -p1 %patch2 -p1 -%patch5 -p1 -%patch22 -p1 -%patch24 -p1 -%patch30 -p1 -%patch31 -p1 -%patch32 -p1 -%patch33 -p1 -%patch37 -p1 -%patch38 -p1 -%patch39 -p1 -%patch40 -p1 -cp etc/iface.example . +%patch3 -p1 +%patch4 -p1 %build %{__make} OPTFLAGS="${RPM_OPT_FLAGS}" @@ -90,6 +74,10 @@ cp etc/iface.example . make DESTDIR=${RPM_BUILD_ROOT} install make DESTDIR=${RPM_BUILD_ROOT} install_initd_suse install -D -m 755 %{S:11} ${RPM_BUILD_ROOT}/sbin/iscsi-gen-initiatorname +install -d $RPM_BUILD_ROOT/lib/mkinitrd/scripts +install -m 755 %{S:5} $RPM_BUILD_ROOT/lib/mkinitrd/scripts/setup-iscsi.sh +install -m 755 %{S:6} $RPM_BUILD_ROOT/lib/mkinitrd/scripts/boot-iscsi.sh +install -m 755 %{S:7} $RPM_BUILD_ROOT/lib/mkinitrd/scripts/boot-killiscsi.sh (cd ${RPM_BUILD_ROOT}/sbin; ln -sf /etc/init.d/open-iscsi rcopen-iscsi) (cd ${RPM_BUILD_ROOT}/etc; ln -sf iscsi/iscsid.conf iscsid.conf) @@ -110,17 +98,29 @@ fi %files %defattr(-,root,root) +%dir /etc/iscsi %attr(0600,root,root) %config(noreplace) /etc/iscsi/iscsid.conf +%dir /etc/iscsi/ifaces +/etc/iscsi/ifaces/iface.example /etc/iscsid.conf %config /etc/init.d/open-iscsi %config /etc/init.d/boot.open-iscsi /sbin/* -%dir /etc/iscsi -%doc iface.example +%dir /lib/mkinitrd +%dir /lib/mkinitrd/scripts +/lib/mkinitrd/scripts/setup-iscsi.sh +/lib/mkinitrd/scripts/boot-iscsi.sh +/lib/mkinitrd/scripts/boot-killiscsi.sh %doc COPYING README %doc %{_mandir}/man8/* %changelog +* Thu Jul 24 2008 hare@suse.de +- Update to 2.0-870-rc1 + * Fix login redirection + * Fix NOP timeout handling + * Various small bugfixes +- Include mkinitrd scriptlets. * Tue Apr 29 2008 hare@suse.de - Pull in fixes from upstream git repository * Mon Apr 21 2008 hare@suse.de