From ddec0c491e287384d60d4cfac6172eef7e801a69de66b4a6a935f19bec9508b7 Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Thu, 9 Oct 2008 17:03:54 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/tgt?expand=0&rev=6 --- tgt-0.9.0-update | 1909 +++++++++++++++++++++++++++++++++++++ tgt-fix-build | 204 +++- tgt-git-update | 2339 +++++++++++++++++++++++++--------------------- tgt.changes | 9 + tgt.spec | 20 +- 5 files changed, 3389 insertions(+), 1092 deletions(-) create mode 100644 tgt-0.9.0-update diff --git a/tgt-0.9.0-update b/tgt-0.9.0-update new file mode 100644 index 0000000..f44ea04 --- /dev/null +++ b/tgt-0.9.0-update @@ -0,0 +1,1909 @@ +diff --git a/README b/README +index dd99e74..22785fc 100644 +--- a/README ++++ b/README +@@ -3,46 +3,53 @@ Introduction + Linux target framework (tgt) aims to simplify various SCSI target + driver (iSCSI, Fibre Channel, SRP, etc) creation and maintenance. + +-Tgt consists of kernel modules, user-space daemon, and user-space +-tools. Some target drivers uses all of them and some use only +-user-space daemon and tools (i.e. they completely runs in user space). ++Currently, tgt supports the following target drivers: + +-Currently, tgt supports three target drivers: ++- iSCSI software target driver for Ethernet NICs + +-- IBM VIO server (ibmvstgt) +-- iSCSI +-- Xen vscsifront/back ++- iSER software target driver for Infiniband and RDMA NICs + +-Note that tgt is under active development. Don't play with important +-data. ++- IBM System p VIO server + +-The code is under the GNU General Public License version 2. ++- FCoE software target driver for Ethernet NICs (in progress) + ++- Qlogic qla2xxx FC target driver (in progress) + +-Preparation +-------------- +-The iSCSI target driver can works with the 2.6.X kernels. It requires +-OpenSSL library (libssl-dev for debian, openssl-devel for Fedora). ++Tgt consists of kernel modules, user-space daemon, and user-space ++tools. iSCSI, iSER, and FCoE target drivers use only user-space daemon ++and tools (i.e. they are just user-space applications. They don't need ++any kernel support). ++ ++tgt can emulate the following device types: + +-host:~/tgt/usr$ make ISCSI=1 ++- SBC: a virtual disk drive that can use a file to store the content. + +-If you want IBM VIO target driver, get kernel version 2.6.20, rebuild +-the kernel, and reboot with the new kernel. Note you need to enable +-SCSI_TGT, SCSI_SRP, and SCSI_IBMVSCSIS kernel options. ++- SMC: a virtual media jukebox that can be controlled by the "mtx" ++tool (partially functional). + +-host:~/tgt/usr$ make KERNELSRC= IBMVIO=1 ++- MMC: a virtual DVD drive that can read DVD-ROM iso files and create ++burnable DVD+R. It can be combined with SMC to provide a fully ++operational DVD jukebox. + +-Make sure that everything is built successfully. ++- SSC: a virtual tape device (aka VTL) that can use a file to store ++the content (in progress). + +-Now you can run tgt. Target drivers have their own ways for +-configuration. So find an appropriate documentation in the doc +-directory. ++- OSD: a virtual object-based storage device that can use a file to ++store the content (in progress). ++ ++The code is under the GNU General Public License version 2. ++ ++ ++Preparation ++------------- ++Target drivers have their own ways to build, configure, etc. So find ++an appropriate documentation in the doc directory. + + + Developer Notes + ------------- + The central resource for tgt development is the mailing list +-(stgt-devel@lists.berlios.de). ++(stgt@vger.kernel.org). + + First, please read the following documents (in short, follow Linux + kernel development rules): +diff --git a/doc/README.ibmvstgt b/doc/README.ibmvstgt +index 9a9dbd8..d77f932 100644 +--- a/doc/README.ibmvstgt ++++ b/doc/README.ibmvstgt +@@ -1,5 +1,13 @@ + Starting + ------------- ++If you want IBM VIO target driver, get kernel version 2.6.20, rebuild ++the kernel, and reboot with the new kernel. Note you need to enable ++SCSI_TGT, SCSI_SRP, and SCSI_IBMVSCSIS kernel options. ++ ++host:~/tgt/usr$ make KERNELSRC= IBMVIO=1 ++ ++Make sure that everything is built successfully. ++ + Try the following commands: + + host:~/tgt$ su +diff --git a/doc/README.iscsi b/doc/README.iscsi +index 5aec190..e6b4d09 100644 +--- a/doc/README.iscsi ++++ b/doc/README.iscsi +@@ -5,6 +5,13 @@ This show a simple example to set up some targets. + + Starting the daemon + ------------- ++The iSCSI target driver works with the 2.6.X kernels. It requires ++OpenSSL library (libssl-dev for debian, openssl-devel for Fedora). ++ ++First, you need to compile the source code: ++ ++host:~/tgt/usr$ make ISCSI=1 ++ + Try the following commands: + + host:~/tgt$ su +diff --git a/doc/targets.conf.example b/doc/targets.conf.example +new file mode 100644 +index 0000000..46be8fe +--- /dev/null ++++ b/doc/targets.conf.example +@@ -0,0 +1,168 @@ ++# This is a sample config file for tgt-admin. ++# By default, tgt-admin looks for its config file in /etc/tgt/targets.conf ++ ++# This one includes other config files: ++ ++include /etc/tgt/temp/*.conf ++ ++ ++# Set the driver. If not specified, defaults to "iscsi". ++ ++default-driver iscsi ++ ++ ++# Sample target with one LUN only. Defaults to allow access for all initiators: ++ ++ ++ backing-store /dev/LVM/somedevice ++ ++ ++ ++# Similar, but we use "direct-store" instead of "backing-store". ++# "direct-store" reads drive parameters with sg_inq command and sets them to ++# the target. ++# Parameters fatched with sg_inq are: ++# - Vendor identification ++# - Product identification ++# - Product revision level ++# - Unit serial number (if present) ++# We also specify "incominguser". ++ ++ ++ direct-store /dev/sdd ++ incominguser someuser secretpass12 ++ ++ ++ ++# An example with multiple LUNs, disabled write-cache (tgtd enables write-cache ++# by default) and vendor identification set to "MyVendor" ++ ++ ++ backing-store /dev/LVM/somedevice1 # Becomes LUN 1 ++ backing-store /dev/LVM/somedevice2 # Becomes LUN 2 ++ backing-store /dev/LVM/somedevice3 # Becomes LUN 3 ++ write-cache off ++ vendor_id MyCompany Inc. ++ ++ ++ ++# Similar to the one above, but we fetch vendor_id, product_id, product_rev and ++# scsi_sn from the disks. ++# Vendor identification (vendor_id) is replaced in all disks by "MyVendor" ++ ++ ++ direct-store /dev/sdb # Becomes LUN 1 ++ direct-store /dev/sdc # Becomes LUN 2 ++ direct-store /dev/sdd # Becomes LUN 3 ++ write-cache off ++ vendor_id MyCompany Inc. ++ ++ ++ ++# Note that "first-device-first-lun numbering" will work only for simple ++# scenarios above, where _only_ direct-store _or_ backing-store is used. ++# If you mix backing-store and direct-store, then all backing-store entries ++# are processed before direct-store-entries. ++ ++ ++ direct-store /dev/sdb # Becomes LUN 3 ++ backing-store /dev/sdc # Becomes LUN 1 ++ direct-store /dev/sdd # Becomes LUN 4 ++ backing-store /dev/sde # Becomes LUN 2 ++ ++ ++ ++# Even more complicated example - each device has different parameters. ++# You can use indentation to make the config file more readable. ++# Note that LUNs will be assigned more or less randomly here (and still ++# backing-store get LUNs assigned before drect-store). ++# You can specify multiple mode_page parameters (they are commented out ++# in this example). ++# Note that some parameters (write-cache, scsi_sn) were specified "globally". ++# "Global" parameters will be applied to all LUNs; they can be overwritten ++# "locally", per LUN. ++ ++ ++ ++ ++ vendor_id VENDOR1 ++ removable 1 ++ device-type cd ++ # lun 1 # Not yet supported ++ ++ ++ ++ vendor_id VENDOR2 ++ # lun 2 # Not yet supported ++ ++ ++ ++ vendor_id back1 ++ scsi_sn SERIAL ++ write-cache on ++ # lun 3 # Not yet supported ++ ++ ++ ++ vendor_id back2 ++ #mode_page 8:0:18:0x10:0:0xff.... ++ #mode_page 8:0:18:0x10:0:0xff.... ++ # lun 4 # Not yet supported ++ ++ ++ # Some more parameters which can be specified locally or globally: ++ #scsi_id ... ++ #scsi_sn ... ++ #vendor_id ... ++ #product_id ... ++ #product_rev ... ++ #sense_format ... ++ #removable ... ++ #online ... ++ #path ... ++ #mode_page 8:0:18:0x10:0:0xff.... ++ #mode_page 8:0:18:0x10:0:0xff.... ++ #device-type ++ ++ write-cache off ++ scsi_sn multipath-10 ++ ++ # Parameters below are global. They can't be configured per LUN. ++ # Only allow connections from 192.168.100.1 and 192.168.200.5 ++ initiator-address 192.168.100.1 ++ initiator-address 192.168.200.5 ++ ++ # Allowed incoming users ++ incominguser user1 secretpass12 ++ incominguser user2 secretpass23 ++ ++ # Outgoing user ++ outgoinguser userA secretpassA ++ ++ ++ ++ ++ ++# Not supported configurations, and therefore, commented out: ++ ++# ++# ++# vendor_id VENDOR1 ++# ++# ++# direct-store /dev/sdc ++# ++ ++# This one will break the parser: ++ ++# ++# ++# vendor_id VENDOR1 ++# ++# ++# direct-store /dev/sdc ++# ++# ++# vendor_id VENDOR1 ++# ++# +diff --git a/scripts/tgt-admin b/scripts/tgt-admin +index fe95723..e4be373 100755 +--- a/scripts/tgt-admin ++++ b/scripts/tgt-admin +@@ -11,7 +11,6 @@ + + use strict; + use Config::General qw(ParseConfig); +-use Data::Dumper; + use Getopt::Long; + + # Our config file +@@ -30,12 +29,15 @@ This tool configures tgt targets. + (see "--offline help" for more info) + --ready put all or selected targets in ready state + (see "--ready help" for more info) ++ --update update configuration for all or selected targets ++ (see "--update help" for more info) + -s, --show show all the targets + -c, --conf specify an alternative configuration file + --ignore-errors continue even if tgtadm exits with non-zero code + -f, --force force some operations even if the target is in use + -p, --pretend only print tgtadm options +- --dump dump current tgtd configuration ++ --dump dump current tgtd configuration (note: does not ++ include detailed parameters, like write caching) + -v, --verbose increase verbosity (show tgtadm commands) + -h, --help show this help + +@@ -49,6 +51,7 @@ my $execute = 0; + my $delete = 0; + my $offline = 0; + my $ready = 0; ++my $update = 0; + my $show = 0; + my $alternate_conf="0"; + my $ignore_errors = 0; +@@ -62,6 +65,7 @@ my $result = GetOptions ( + "delete=s" => \$delete, + "offline=s" => \$offline, + "ready=s" => \$ready, ++ "update=s" => \$update, + "s|show" => \$show, + "c|conf=s" => \$alternate_conf, + "ignore-errors" => \$ignore_errors, +@@ -73,7 +77,7 @@ my $result = GetOptions ( + ); + + if (($help == 1) || ($param eq undef)) { +- &usage ++ usage; + } + + # Show all the targets and exit +@@ -99,7 +103,6 @@ sub process_targets { + my @show_target = `tgtadm --op show --mode target`; + my $tid; + my $targetname; +- + # Here, we create hashes of target names (all target data) and target tids + foreach my $show_target_line (@show_target) { + if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) { +@@ -128,7 +131,7 @@ sub parse_configs { + %conf = ParseConfig(-ConfigFile => "$alternate_conf", -UseApacheInclude => 1, -IncludeGlob => 1,); + } + else { +- die("file $alternate_conf not found. Exiting...\n"); ++ die("Config file $alternate_conf not found. Exiting...\n"); + } + } else { + # Parse the config file with Config::General +@@ -145,13 +148,17 @@ my $default_driver; + my $target; + my $option; + my $value; ++my $lun; + + sub add_targets { +- ++ my $single_target = $_[0]; ++ my $configured = $_[1]; ++ my $connected = $_[2]; ++ my $in_configfile = $_[3]; + foreach my $k (sort keys %conf) { + +- if ( $k eq "default-driver" ) { +- if ( not length ref($conf{$k}) ) { ++ if ($k eq "default-driver") { ++ if (not length ref($conf{$k})) { + $default_driver = $conf{$k}; + } else { + print "Multiple default-driver definitions are not allowed!\n"; +@@ -162,134 +169,344 @@ sub add_targets { + } + + # If $default_driver is empty, default to iscsi +- if ( not defined $default_driver ) { ++ if (not defined $default_driver) { + execute("# default-driver not defined, defaulting to iscsi.\n"); + $default_driver = "iscsi"; + } + + foreach my $k (sort keys %conf) { +- if ( $k eq "target" ) { ++ if ($k eq "target") { + foreach my $k2 (sort keys %{$conf{$k}}) { +- $target = $k2; +- my $allowall = 1; +- if ( not defined $tgtadm_output{$k2} ) { +- # We have to find available tid +- $next_tid = $next_tid + 1; ++ # Do we run update or execute? ++ if (length $single_target) { ++ if ($single_target ne $k2) { ++ next; ++ } else { ++ $target = $single_target; ++ } ++ } else { ++ $target = $k2; + } +- else { +- execute("# Target $target already exist!"); +- execute("# Updating Target $target"); +- execute("tgtadm --op update --mode target --tid=$next_tid -n state -v offline"); +- execute("tgtadm --mode target --op delete --tid=$next_tid"); ++ ++ my $in_use = 0; ++ if (length $single_target) { ++ $in_use = main_delete($target); + } ++ my $allowall = 1; ++ if ((not defined $tgtadm_output{$k2}) || ++ ($update ne 0 && $in_use == 0) || ++ ($update ne 0 && $in_use == 1 && $pretend == 1 && $force == 1)) ++ { ++ # We have to find available tid ++ if ($in_configfile == 1 && $configured == 0 && $pretend == 0) { ++ my $maxtid = find_max_tid(); ++ $next_tid = $maxtid + 1; ++ } elsif (length $single_target && $configured == 1) { ++ $next_tid = $tgtadm_output_tid{$target}; ++ } else { ++ $next_tid = $next_tid + 1; ++ } ++ ++ # Before we add a target, we need to know its type ++ # and other parameters which can be specified globally ++ my %target_options; ++ my $target_options_ref; ++ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { ++ $lun = 1; ++ $option = $k3; ++ $value = $conf{$k}{$k2}{$k3}; ++ check_value($value); ++ $target_options{$option} = $value; ++ $target_options_ref = \%target_options; ++ } + +- # Before we add a target, we need to know its type +- my $driver; +- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { +- $option = $k3; +- $value = $conf{$k}{$k2}{$k3}; +- &check($value); +- if ( $option eq "driver" ) { +- if (ref($value) eq "ARRAY") { +- print "Multiple driver definitions not allowed!\n"; +- print "Check your config file for errors (target: $target).\n"; +- exit 1; ++ if (not defined $target_options{"driver"}) { ++ $target_options{"driver"} = $default_driver; ++ } ++ my $driver = $target_options{"driver"}; ++ execute("# Adding target: $target"); ++ execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target"); ++ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { ++ $option = $k3; ++ $value = $conf{$k}{$k2}{$k3}; ++ check_value($value); ++ process_options($target_options_ref); ++ # If there was no option called "initiator-address", it means ++ # we want to allow ALL initiators for this target ++ if ($option eq "initiator-address") { ++ $allowall = 0; + } +- $driver = $value; ++ } ++ ++ if ($allowall == 1) { ++ execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL"); ++ } ++ ++ } else { ++ if (not length $configured || $in_use eq 1) { ++ execute("# Target $target already exists!"); + } + } ++ } ++ if (length $single_target && $in_configfile == 0 && $configured == 0) { ++ print "Target $single_target is currently not configured\n"; ++ print "and does not exist in the config file - can't continue!\n"; ++ exit 1; ++ } ++ execute(); ++ } ++ } ++} + +- if ( not defined $driver ) { +- $driver = $default_driver; ++# Some options can be specified only once ++sub check_if_hash_array { ++ my $check = $_[0]; ++ my $definition = $_[1]; ++ if (ref($check) eq 'ARRAY' || ref($check) eq "HASH") { ++ print "Multiple '$definition' definitions in '$option' not allowed!\n"; ++ print "Check your config file for errors (target: $target).\n"; ++ exit 1; ++ } ++} ++ ++# Force an array if we just have one command ++sub force_array { ++ unless (ref($value) eq 'ARRAY') { ++ $value = [ $value ]; ++ } ++} ++ ++# If we start any external command, we want to know if it exists ++sub check_exe { ++ my $command = $_[0]; ++ my $option = $_[1]; ++ my @path = split(":", $ENV{PATH}); ++ my $exists = 0; ++ foreach my $path (@path) { ++ if ( -x "$path/$command" && -f "$path/$command" ) { $exists = 1 } ++ } ++ if ( $exists == 0 ) { ++ print "Command $command (needed by $option option in your config file) is not in your path - can't continue!\n"; ++ exit 1; ++ } ++} ++ ++# Apply additional parameters ++sub add_params { ++ my $param = shift; ++ my $param_value = shift; ++ my $lun = shift; ++ my $driver = shift; ++ ++ if ($param eq "write-cache") { ++ if ($param_value eq "off") { ++ return("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun=$lun --params mode_page=8:0:18:0x10:0:0xff:0xff:0:0:0xff:0xff:0xff:0xff:0x80:0x14:0:0:0:0:0:0"); ++ } elsif ($param_value eq "on" || not length $param_value) { ++ return("# Write cache is enabled (default) for lun $lun."); ++ } else { ++ return("# WARNING! Unknown value ($param_value) to write-cache! Accepted values are \"on\" and \"off\"."); ++ } ++ } ++ ++ if ($param eq "scsi_id" || $param eq "scsi_sn" || $param eq "vendor_id" || $param eq "product_id" || ++ $param eq "product_rev" || $param eq "sense_format" || $param eq "removable" || $param eq "online" || ++ $param eq "path" || $param eq "mode_page") { ++ return("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun=$lun --params $param=\"$param_value\""); ++ } ++} ++ ++# Add backing or direct store ++sub add_backing_direct { ++ my $backing_store = $_[0]; ++ my $target_options_ref = $_[1]; ++ my $lun = $_[2]; ++ my $direct_store = $_[3]; ++ my $driver = $$target_options_ref{"driver"}; ++ ++ # Is the device in use? ++ (my $can_alloc, my $dev) = check_device($backing_store); ++ ++ # Needed if the config file has mixed definitions ++ if (ref($backing_store) eq "HASH") { ++ foreach my $backing_store (sort keys %$value) { ++ add_backing_direct($backing_store,$target_options_ref,$lun,$direct_store); ++ $lun += 1; ++ } ++ return $lun; ++ } elsif (-e $backing_store && $can_alloc == 1) { ++ my @exec_commands; ++ my $device_type; ++ # Process parameters for each lun / backing store ++ if (ref $value eq "HASH") { ++ my %params_added; ++ my @mode_page; ++ foreach my $store (keys %$value) { ++ if (ref $$value{$store} eq "HASH" && $store eq $backing_store) { ++ foreach my $store_option (keys %{$$value{$store}}) { ++ my $result = $$value{$store}{$store_option}; ++ check_value($result); ++ if ($store_option ne "mode_page") { check_if_hash_array($result,$store_option) } ++ # write-cache can be set globally per target and overridden per lun, ++ # so we treat it differently ++ if ($store_option ne "mode_page" && $store_option ne "write-cache") { ++ my $exec_command = add_params($store_option, $result, $lun, $driver); ++ push(@exec_commands, $exec_command); ++ $params_added{$store_option} = 1; ++ } ++ if ($store_option eq "write-cache") { ++ my $exec_command = add_params($store_option, $result, $lun, $driver); ++ $params_added{write_cache} = 1; ++ push(@exec_commands, $exec_command); ++ } ++ if ($store_option eq "device-type") { ++ $device_type = $result; ++ $params_added{$store_option} = 1; ++ } ++ if ($store_option eq "mode_page") { ++ @mode_page = @$result; ++ foreach my $mode_page (@mode_page) { ++ my $exec_command = add_params("mode_page", $mode_page, $lun, $driver); ++ push(@exec_commands, $exec_command); ++ } ++ } ++ } + } +- execute("# Adding target: $target"); +- execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target"); +- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { +- $option = $k3; +- $value = $conf{$k}{$k2}{$k3}; +- &check($value); +- &process_options($driver); +- # If there was no option called "initiator-address", it means +- # we want to allow ALL initiators for this target +- if ( $option eq "initiator-address" ) { +- $allowall = 0; ++ } ++ # Used only if lun is a direct-store ++ my $sg_inq; ++ my %direct_params; ++ if ($direct_store == 1) { ++ $sg_inq=`sg_inq $backing_store`; ++ if ($sg_inq=~m { ++ Vendor\ identification:\s+?(.*?)\n ++ \s+Product\ identification:\s+(.*?)\n ++ \s+Product\ revision\ level:\s+(.*?)\n ++ (?:\s+Unit\ serial\ number:\s+(.*?)\n)? ++ }xs ) { ++ # If they were not defined globally for a target, ++ # add them now ++ if (not length $$target_options_ref{vendor_id}) { ++ $direct_params{vendor_id} = $1; ++ } ++ if (not length $$target_options_ref{product_id}) { ++ $direct_params{product_id} = $2; ++ } ++ if (not length $$target_options_ref{product_rev}) { ++ $direct_params{product_rev} = $3; ++ } ++ if (not length $$target_options_ref{scsi_sn}) { ++ $direct_params{scsi_sn} = $4; + } + } ++ } + +- if ( $allowall == 1 ) { +- execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL"); ++ # Add these parameters if they were not overwritten in the config file ++ my @opts = ("scsi_id", "sense_format", "removable", "online", "path"); ++ foreach my $single_opt (@opts) { ++ check_if_hash_array($$target_options_ref{$single_opt},$single_opt); ++ if ($params_added{$single_opt} ne 1 && length $$target_options_ref{$single_opt}) { ++ my $exec_command = add_params($single_opt, $$target_options_ref{$single_opt}, $lun, $driver); ++ push(@exec_commands, $exec_command); ++ $params_added{$single_opt} = 1; + } +- execute(); + } ++ # These options can be fetched by sg_inq for direct-store ++ my @opts = ("vendor_id", "product_id", "product_rev", "scsi_sn"); ++ foreach my $single_opt (@opts) { ++ check_if_hash_array($$target_options_ref{$single_opt},$single_opt); ++ my $this_opt; ++ if (length $$target_options_ref{$single_opt}) { ++ $this_opt = $$target_options_ref{$single_opt}; ++ } elsif (length $direct_params{$single_opt}) { ++ $this_opt = $direct_params{$single_opt}; ++ } ++ if ($params_added{$single_opt} ne 1 && length $this_opt) { ++ my $exec_command = add_params($single_opt, $this_opt, $lun, $driver); ++ push(@exec_commands, $exec_command); ++ $params_added{$single_opt} = 1; ++ } ++ } ++ # write-cache ++ if ($params_added{write_cache} ne 1) { ++ my $exec_command = add_params("write-cache", $$target_options_ref{"write-cache"}, $lun, $driver); ++ push(@exec_commands, $exec_command); ++ $params_added{write_cache} = 1; ++ } ++ # mode_page ++ unless (ref($$target_options_ref{mode_page}) eq 'ARRAY') { ++ $$target_options_ref{mode_page} = [ $$target_options_ref{mode_page} ]; ++ } ++ foreach my $mode_page (@{$$target_options_ref{"mode_page"}}) { ++ if (length $mode_page) { ++ my $exec_command = add_params("mode_page", $mode_page, $lun, $driver); ++ push(@exec_commands, $exec_command); ++ } ++ } ++ # device-type ++ if ($params_added{"device-type"} ne 1) { ++ check_if_hash_array($$target_options_ref{"device-type"}, "device-type"); ++ $device_type = $$target_options_ref{"device-type"}; ++ } ++ } else { ++ print "If you got here, this means your config file is not supported.\n"; ++ print "Please report it to stgt mailing list and attach your config files.\n"; ++ exit 1; ++ } ++ # Execute commands for a given LUN ++ if (length $device_type) { $device_type = "--device-type $device_type" }; ++ execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun $lun -b $backing_store $device_type"); ++ foreach my $exec_command (@exec_commands) { ++ if (length $exec_command) { execute($exec_command) } + } ++ $lun += 1; ++ return $lun; ++ } elsif ($can_alloc == 0) { ++ execute("# Skipping device $backing_store ($dev is mounted / in use)"); ++ } else { ++ execute("# Skipping device: $backing_store"); ++ execute("# $backing_store does not exist - please check the configuration file"); + } + } + + # Process options from the config file + sub process_options { +- my $driver = $_[0]; +- if ( $option eq "backing-store" ) { +- # if we have one command, force it to be an array anyway +- unless (ref($value) eq 'ARRAY') { +- $value = [ $value ]; ++ my $target_options_ref = $_[0]; ++ my $driver = $$target_options_ref{"driver"}; ++ if ($option eq "backing-store" || $option eq "direct-store") { ++ my $direct_store = 0; ++ if ($option eq "direct-store") { ++ check_exe("sg_inq", "direct-store"); ++ $direct_store = 1; + } +- my @value_arr = @$value; +- my $i = 1; + +- foreach my $backing_store (@value_arr) { +- # Check if device exists +- if ( -e $backing_store) { +- execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun $i -b $backing_store"); +- $i += 1; +- } +- else { +- print("skipping device $backing_store\n"); +- print("$backing_store does not exist - please check the configuration file\n"); +- } ++ # We want to make everything a hash to use it ++ # in the same way later on ++ unless (ref($value)) { ++ $value = { $value } + } +- } + +- if ( $option eq "direct-store" ) { +- my $inq; +- my $vendor_id=""; +- my $prod_id=""; +- my $prod_rev=""; +- my $scsi_serial=""; +- # if we have one command, force it to be an array anyway +- unless (ref($value) eq 'ARRAY') { +- $value = [ $value ]; +- } +- my @value_arr = @$value; +- my $i = 1; +- foreach my $direct_store (@value_arr) { +- $inq=`sg_inq $direct_store`; +- if ($inq=~/Vendor identification:\s*(\w+)\s*\n*Product identification:\s*([\w\s\/\-]+)\n\s*\n*Product revision level:\s*(\w*)\s*\n*Unit serial number:\s*(\w+)/) +- { +- $vendor_id="$1"; +- $prod_id="$2"; +- $prod_rev="$3"; +- $scsi_serial="$4"; ++ my %arrvalue; ++ if (ref($value) eq "ARRAY") { ++ foreach my $backing_store (@$value) { ++ $arrvalue{$backing_store} = 1; + } +- $vendor_id =~ s/\s+$//; +- $prod_id =~ s/\s+$//; +- $prod_rev =~ s/\s+$//; +- $scsi_serial =~ s/\s+$//; ++ $value = \%arrvalue; ++ } + +- execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun 1 -b $direct_store"); +- execute("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun 1 --params vendor_id=\"$vendor_id\",product_id=\"$prod_id\",product_rev=\"$prod_rev\",scsi_sn=\"$scsi_serial\""); +- $i += 1; ++ if (ref($value) eq "HASH") { ++ foreach my $backing_store (sort keys %$value) { ++ $lun = add_backing_direct($backing_store,$target_options_ref,$lun,$direct_store); ++ } + } + } + + if ( $option eq "incominguser" ) { + # if we have one command, force it to be an array anyway +- unless (ref($value) eq 'ARRAY') { +- $value = [ $value ]; +- } ++ force_array(); + my @value_arr = @$value; + foreach my $incominguser (@value_arr) { + my @userpass = split(/ /, $incominguser); +- &check($userpass[1]); ++ check_value($userpass[1]); + execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]"); + execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]"); + execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0]"); +@@ -298,12 +515,10 @@ sub process_options { + + if ( $option eq "outgoinguser" ) { + # if we have one command, force it to be an array anyway +- unless (ref($value) eq 'ARRAY') { +- $value = [ $value ]; +- } ++ force_array(); + execute("# Warning: only one outgoinguser is allowed. Will only use the first one."); + my @userpass = split(/ /, @$value[0]); +- &check($userpass[1]); ++ check_value($userpass[1]); + execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]"); + execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]"); + execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing"); +@@ -311,21 +526,20 @@ sub process_options { + + if ( $option eq "initiator-address" ) { + # if we have one command, force it to be an array anyway +- unless (ref($value) eq 'ARRAY') { +- $value = [ $value ]; +- } ++ force_array(); + my @value_arr = @$value; + foreach my $initiator_address (@value_arr) { ++ check_value($initiator_address); + execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I $initiator_address"); + } + } + } + + # If the target is configured, but not present in the config file, +-# offline it and try to remove it ++# try to remove it + sub remove_targets { + +- &process_targets; ++ process_targets; + my @all_targets = keys %tgtadm_output_tid; + + foreach my $existing_target (@all_targets) { +@@ -340,13 +554,8 @@ sub remove_targets { + } + + if ( $dontremove == 0 ) { +- # Right now, it is not possible to remove a target if any initiators +- # are connected to it. We'll do our best - offline the target first +- # (so it won't accept any new connections), and remove. +- # Note that remove will only work if no initiator is connected. +- execute("# Removing target: $existing_target"); +- execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v offline"); +- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}"); ++ # Remove the target ++ main_delete($existing_target); + } + } + } +@@ -356,61 +565,54 @@ sub remove_targets { + # Dump current tgtd configuration + sub dump_config { + +- &process_targets; ++ process_targets; + + my @all_targets = keys %tgtadm_output_tid; + +- foreach my $target (@all_targets) { +- foreach my $show_target_line ($tgtadm_output{$target}) { +- if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) { +- print "\n"; +- } ++ # If all targets use the same driver, us it only once in the config ++ my $skip_driver = 0; ++ my @drivers_combined; ++ foreach my $current_target (@all_targets) { ++ my $driver = show_target_info($current_target, "driver"); ++ push (@drivers_combined, $driver); ++ } + +- if ( $show_target_line =~ m/\s+Driver: (.+)/ ) { +- print "\tdriver $1\n"; +- } ++ my %drivers_uniq; ++ @drivers_uniq{@drivers_combined} = (); ++ my @drivers_combined_uniq = sort keys %drivers_uniq; + +- if ( $show_target_line =~ m/\s+Backing store: (?!No backing store)(.+)/ ) { +- print "\tbacking-store $1\n"; +- } +- } ++ if (scalar @drivers_combined_uniq == 1) { ++ print "default-driver $drivers_combined_uniq[0]\n\n"; ++ } + +- # Process account and ACL information +- my $account_acl; ++ # Print everything else in the config ++ foreach my $current_target (@all_targets) { ++ my $target_name = show_target_info($current_target, "target_name"); ++ print "\n"; + +- foreach my $show_target_line ($tgtadm_output{$target}) { +- $account_acl .= $show_target_line ++ if (scalar @drivers_combined_uniq gt 1) { ++ my $driver = show_target_info($current_target, "driver"); ++ print "\tdriver $driver\n"; + } + +- # start with account information... +- while ($account_acl =~ m{ +- \s+Account\ information:\n(.*)ACL\ information: +- }xmgs +- ) { +- +- my @account = split(/\n/, $1); +- +- foreach my $user (@account) { +- my @var = split(/^\s+/, $user); +- @var = split(/\s/, $var[1]); ++ my @backing_stores = show_target_info($current_target, "backing_stores"); ++ foreach my $backing_store (@backing_stores) { ++ print "\tbacking-store $backing_store\n"; ++ } + +- if ( $var[1] eq "(outgoing)" ) { +- print "\toutgoinguser $var[0] PLEASE_CORRECT_THE_PASSWORD\n"; +- } elsif ( ($var[0] ne "") && ($var[1] eq "") ) { +- print "\tincominguser $var[0] PLEASE_CORRECT_THE_PASSWORD\n"; +- } ++ my @account_information = show_target_info($current_target, "account_information"); ++ foreach my $account (@account_information) { ++ if ($account =~ /(.+)\ \(outgoing\)/) { ++ print "\toutgoinguser $1 PLEASE_CORRECT_THE_PASSWORD\n"; ++ } elsif (length $account) { ++ print "\tincominguser $account PLEASE_CORRECT_THE_PASSWORD\n"; + } + } + +- #...and finish with ACL information +- while ($account_acl =~ m{ +- \s+ACL\ information:\n(.*) +- }xmgs +- ) { +- my @ini_addresses = split(/\n/, $1); +- foreach my $ini_address (@ini_addresses) { +- my @var = split(/^\s+/, $ini_address); +- print "\tinitiator-address $var[1]\n"; ++ my @acl_information = show_target_info($current_target, "acl_information"); ++ if (scalar(@acl_information) != 1 || $acl_information[0] ne "ALL") { ++ foreach my $ini_address (@acl_information) { ++ print "\tinitiator-address $ini_address\n"; + } + } + print "\n\n"; +@@ -441,17 +643,17 @@ Example usage: + + EOF + } elsif ($off_ready eq "ALL") { +- &process_targets; ++ process_targets; + # Run over all targets and offline/ready them + my @all_targets = keys %tgtadm_output_tid; + foreach my $existing_target (@all_targets) { + execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v $var"); + } + } elsif ($off_ready =~ m/tid=(.+)/) { +- &process_targets; ++ process_targets; + execute("tgtadm --op update --mode target --tid=$1 -n state -v $var"); + } else { +- &process_targets; ++ process_targets; + if (length $tgtadm_output_tid{$off_ready}) { + execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$off_ready} --name=\"$off_ready\" -n state -v $var"); + } else { +@@ -465,17 +667,42 @@ EOF + sub show_target_info { + my $existing_target = $_[0]; + my $task = $_[1]; ++ # Returns target information ++ if ($task eq "target_name") { ++ if ($tgtadm_output{$existing_target} =~ m/^Target (\d*): (.+)/ ) { ++ return $2; ++ } + # Returns driver information +- if ($task eq "driver") { +- if ( $tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) { +- print $1; ++ } elsif ($task eq "driver") { ++ if ($tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) { + return $1; + } ++ # Returns backing store ++ } elsif ($task eq "backing_stores") { ++ if ($tgtadm_output{$existing_target} =~ m/\s+Backing store: (?!No backing store)(.+)/ ) { ++ my @backing_stores = $tgtadm_output{$existing_target} =~ m{\s+Backing store: (?!No backing store\n)(.+)}g; ++ return @backing_stores; ++ } ++ return; ++ # Returns account information: ++ } elsif ($task eq "account_information") { ++ if ($tgtadm_output{$existing_target} =~ m{ ++ \s+Account\ information:\n(.*)\n\s+ACL\ information: ++ }xs ++ ) { ++ my @accounts = split(/\n/, $1); ++ my @account_information; ++ foreach my $user (@accounts) { ++ my @var = split(/^\s+/, $user); ++ push(@account_information, $var[1]); ++ } ++ return @account_information; ++ } + # Returns ACL information + } elsif ($task eq "acl_information") { +- while ($tgtadm_output{$existing_target} =~ m{ ++ if ($tgtadm_output{$existing_target} =~ m{ + \s+ACL\ information:\n(.*) +- }xmgs ++ }xs + ) { + my @ini_addresses = split(/\n/, $1); + my @acls; +@@ -487,51 +714,80 @@ sub show_target_info { + } + # Returns sessions + } elsif ($task eq "sessions") { +- my @var = split(/\n/, $tgtadm_output{$existing_target}); +- my @sids; +- foreach my $sid (@var) { +- if ( $sid =~ m/\s+I_T nexus: (.+)/ ) { +- push(@sids, $1); ++ my %sessions; ++ if ($tgtadm_output{$existing_target} =~ m{ ++ \s+I_T\ nexus\ information:\n(.*)LUN\ information: ++ }xs ++ ) { ++ my @var = split(/\n/, $1); ++ my $sid; ++ my $cid; ++ ++ foreach my $line (@var) { ++ if ($line =~ m/\s+I_T nexus:\ (.+)/) { ++ $sid = $1; ++ } else { ++ if ($line =~ m/\s+Connection:\ (.+)/) { ++ $cid = $1; ++ $sessions{$sid} = $cid; ++ } ++ } + } + } +- return @sids; ++ return %sessions; + } + } + +-# Delete the targets which are not in use +-sub delete_targets { +- +- # Check if the target is used by an initiator +- sub check_in_use { +- my $existing_target = $_[0]; +- my $cur_option = $_[1]; +- my $cur_tid = $_[2]; +- if ($tgtadm_output{$existing_target} =~ m/\s+Connection:/) { +- if ($force == 1) { +- # Remove ACLs first +- my @acl_info = &show_target_info($existing_target, "acl_information"); +- foreach my $acl (@acl_info) { +- execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$existing_target} -I $acl"); +- } +- # Now, remove all sessions / connections from that tid +- my @sessions = &show_target_info($existing_target, "sessions"); +- foreach my $session (@sessions) { +- execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$existing_target} --sid $session --cid 0"); ++# Main subroutine for deleting targets ++sub main_delete { ++ my $current_target = $_[0]; ++ my $current_tid = $_[1]; ++ my $configured = check_configured($current_target); ++ my $del_upd_text; ++ # Check if the target has initiators connected ++ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) { ++ if ($force == 1) { ++ execute("# Removing target: $current_target"); ++ # Remove ACLs first ++ my @acl_info = show_target_info($current_target, "acl_information"); ++ foreach my $acl (@acl_info) { ++ execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$current_target} -I $acl"); ++ } ++ # Now, remove all sessions / connections from that tid ++ my %sessions = show_target_info($current_target, "sessions"); ++ foreach my $sid (keys %sessions) { ++ foreach my $cid ($sessions{$sid}) { ++ execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$current_target} --sid $sid --cid $cid"); + } +- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}"); +- } else { +- execute("# Target with tid $tgtadm_output_tid{$existing_target} ($existing_target) is in use, it won't be deleted."); + } +- } elsif (length $tgtadm_output_tid{$existing_target}) { +- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}"); ++ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}"); + } else { +- if ($cur_option eq "tid") { +- execute("# Target with tid $cur_tid does not exist!"); ++ if ($update ne 0) { ++ $del_upd_text = "updated"; + } else { +- execute("# Target $existing_target does not exist!"); ++ $del_upd_text = "deleted"; + } ++ execute("# Target with tid $tgtadm_output_tid{$current_target} ($current_target) is in use, it won't be $del_upd_text."); ++ return 1; ++ } ++ } elsif (length $tgtadm_output_tid{$current_target}) { ++ execute("# Removing target: $current_target"); ++ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}"); ++ } else { ++ if (length $current_tid) { ++ execute("# Target with tid $current_tid does not exist!"); ++ } else { ++ execute("# Target with name $current_target does not exist!"); + } + } ++ if ($configured ne 0) { ++ execute(); ++ } ++ return 0; ++} ++ ++# Delete the targets ++sub delete_targets { + + if ($delete eq "help") { + print < update all or selected targets ++ The target will be updated only if it's not used ++ (no initiator is connected to it). ++ If you want to update targets which are in use, ++ you have to add "--force" flag. ++ ++Example usage: ++ --update help - display this help ++ --update ALL - update all targets ++ --update tid=4 - update target 4 (target with tid 4) ++ --update iqn.2008-08.com.example:some.target - update this target ++ ++EOF ++ exit; ++ } elsif ($update eq "ALL") { ++ # Run over all targets and delete them if they are not in use ++ parse_configs; ++ process_targets; ++ my @targets_combined = combine_targets(); ++ foreach my $current_target (@targets_combined) { ++ my $configured = check_configured($current_target); ++ my $connected = check_connected($current_target); ++ my $in_configfile = check_in_configfile($current_target); ++ combine_targets(); ++ if (($in_configfile == 0) && ($configured == 1)) { ++ # Delete the target if it's not in the config file ++ main_delete($current_target); ++ } else { ++ add_targets($current_target, $configured, $connected, $in_configfile); ++ } ++ ++ } ++ } elsif ($update =~ m/^tid=(.+)/) { ++ # Update by tid ++ parse_configs; ++ process_targets; ++ my $current_target = $tgtadm_output_name{$1}; ++ my $configured = check_configured($current_target); ++ my $connected = check_connected($current_target); ++ my $in_configfile = check_in_configfile($current_target); ++ if (($in_configfile == 0) && ($configured == 1)) { ++ # Delete the target if it's not in the config file ++ main_delete($current_target); ++ } elsif ($configured == 1) { ++ add_targets($current_target, $configured, $connected, $in_configfile); ++ } else { ++ print "There is no target with tid $1, can't continue!\n"; ++ exit 1; ++ } ++ } else { ++ # Update by name ++ parse_configs; ++ process_targets; ++ my $current_target = $update; ++ my $configured = check_configured($current_target); ++ my $connected = check_connected($current_target); ++ my $in_configfile = check_in_configfile($current_target); ++ if ($in_configfile == 0 && $configured == 1) { ++ # Delete the target if it's not in the config file ++ main_delete($current_target); ++ } else { ++ add_targets($current_target, $configured, $connected, $in_configfile); ++ } ++ } ++} ++ ++# Find the biggest tid ++sub find_max_tid { ++ process_targets; ++ my @all_targets = keys %tgtadm_output_tid; ++ my $maxtid = 0; ++ foreach my $var (@all_targets) { ++ if ($tgtadm_output_tid{$var} > $maxtid) { ++ $maxtid = $tgtadm_output_tid{$var}; ++ } ++ } ++ return $maxtid; ++} ++ ++# Combine targets from the config file and currently configured targets ++sub combine_targets { ++ my @targets_in_configfile; ++ my @all_targets = keys %tgtadm_output_tid; ++ my @targets_combined; ++ # Make an array of targets in the config file ++ foreach my $k (sort keys %conf) { ++ if ( $k eq "target" ) { ++ foreach my $k2 (sort keys %{$conf{$k}}) { ++ push(@targets_in_configfile, $k2) ++ } ++ } ++ } ++ # Use only unique elements from both arrays ++ foreach my $current_target (@all_targets) { ++ push (@targets_combined, $current_target) unless grep { $_ eq $current_target } @targets_in_configfile; ++ } ++ @targets_combined = (@targets_combined, @targets_in_configfile); ++ return @targets_combined; ++} ++ ++# Check if a value is correct ++sub check_value { + if ( not defined $_[0] or not length $_[0] ) { + print "\nOption $option has a missing value!\n"; + print "Check your config file for errors (target: $target)\n"; +@@ -578,6 +938,102 @@ sub check { + } + } + ++# Check if the target is in the config file ++sub check_in_configfile { ++ my $current_target = $_[0]; ++ my $result; ++ foreach my $k (sort keys %conf) { ++ if ( $k eq "target" ) { ++ foreach my $k2 (sort keys %{$conf{$k}}) { ++ if ($k2 eq $current_target) { ++ return 1; ++ } ++ } ++ # If we're here, we didn't find a match ++ return 0; ++ } ++ } ++} ++ ++# Check if the target is configured in tgtd ++sub check_configured { ++ my $current_target = $_[0]; ++ if (length $tgtadm_output_tid{$current_target}) { ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++# Check if any initiators are connected to the target ++sub check_connected { ++ my $current_target = $_[0]; ++ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) { ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++# Check if a device can be allocated ++my @rootfs_dev; ++sub check_device { ++ my $tmp_dev = $_[0]; ++ ++ # Check if force flag is set ++ if ( $force == 0) { ++ # Check for rootfs devices ++ &find_rootfs_device(); ++ $tmp_dev =~ s/\d//g; ++ # Check if device is on the same disk as rootfs ++ if (grep {$_ eq $tmp_dev} @rootfs_dev) { ++ return (0,$tmp_dev); ++ } ++ } ++ return 1; ++} ++ ++# finds all the devices that rootfs is mounted on ++sub find_rootfs_device { ++ my @files=("/etc/mtab","/proc/mounts"); ++ my @lines; ++ # read files ++ foreach my $file (@files){ ++ if (open(FH,"$file")) { ++ @lines=(@lines,); ++ close (FH); ++ } ++ } ++ ++ # parse files and finds all the device which mounted on / ++ foreach my $line (@lines){ ++ chomp $line; ++ if (($line=~/^\/dev\//) && ($line=~/ \/ /)){ ++ my @ln=split(' ',$line); ++ $ln[0]=~s/\d//g; ++ push(@rootfs_dev,$ln[0]); ++ } ++ } ++ ++ # read swap file ++ my $swap_file="/proc/swap"; ++ if (open(FH,"$swap_file")) { ++ @lines=; ++ close (FH); ++ } ++ # parse swap file and finds all the swap devices ++ foreach my $line (@lines){ ++ chomp $line; ++ if ($line=~/^\/dev\//) { ++ my @ln=split(' ',$line); ++ $ln[0]=~s/\d//g; ++ push(@rootfs_dev,$ln[0]); ++ } ++ } ++ # remove duplicate entries from @rootfs_dev ++ my %seen = (); ++ @rootfs_dev = grep { ! $seen{ $_ }++ } @rootfs_dev; ++} + + # Execute or just print (or both) everything we start or would start + sub execute { +@@ -589,7 +1045,7 @@ sub execute { + } + # Don't try to execute if it's a comment + my @execargs = split(/#/, $args); +- if ( $execargs[0] ne undef ) { ++ if ($execargs[0] ne undef) { + system($args); + + # If non-zero exit code was return, exit +@@ -600,24 +1056,26 @@ sub execute { + } + } + +- } elsif ( $pretend == 1 ) { ++ } elsif ($pretend == 1) { + print "@_\n"; + } + } + + if ($execute == 1) { +- &process_targets; +- &parse_configs; +- &add_targets; +- &remove_targets; ++ process_targets; ++ parse_configs; ++ add_targets; ++ remove_targets; + } elsif ($delete ne 0) { +- &delete_targets; ++ delete_targets; ++} elsif ($update ne 0) { ++ update_targets; + } elsif ($dump == 1) { +- &dump_config; ++ dump_config; + } elsif ($offline ne 0) { +- &ready_offline_targets("offline"); ++ ready_offline_targets("offline"); + } elsif ($ready ne 0) { +- &ready_offline_targets("ready"); ++ ready_offline_targets("ready"); + } else { + print "No action specified.\n"; + } +diff --git a/usr/Makefile b/usr/Makefile +index 4245709..82ddf07 100644 +--- a/usr/Makefile ++++ b/usr/Makefile +@@ -55,10 +55,12 @@ CFLAGS += -g -O2 -Wall -Wstrict-prototypes -fPIC + LIBS += -lpthread + + PROGRAMS += tgtd tgtadm +-SCRIPTS += ../scripts/tgt-setup-lun ++SCRIPTS += ../scripts/tgt-setup-lun ../scripts/tgt-admin + TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \ +- parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o bs.o +-MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-setup-lun.8 ++ parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o \ ++ bs.o ++MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-admin.8 \ ++ ../doc/manpages/tgt-setup-lun.8 + + TGTD_DEP = $(TGTD_OBJS:.o=.d) + +diff --git a/usr/be_byteshift.h b/usr/be_byteshift.h +new file mode 100644 +index 0000000..5c6a619 +--- /dev/null ++++ b/usr/be_byteshift.h +@@ -0,0 +1,68 @@ ++#ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H ++#define _LINUX_UNALIGNED_BE_BYTESHIFT_H ++ ++static inline uint16_t __get_unaligned_be16(const uint8_t *p) ++{ ++ return p[0] << 8 | p[1]; ++} ++ ++static inline uint32_t __get_unaligned_be32(const uint8_t *p) ++{ ++ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; ++} ++ ++static inline uint64_t __get_unaligned_be64(const uint8_t *p) ++{ ++ return (uint64_t)__get_unaligned_be32(p) << 32 | ++ __get_unaligned_be32(p + 4); ++} ++ ++static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) ++{ ++ *p++ = val >> 8; ++ *p++ = val; ++} ++ ++static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) ++{ ++ __put_unaligned_be16(val >> 16, p); ++ __put_unaligned_be16(val, p + 2); ++} ++ ++static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) ++{ ++ __put_unaligned_be32(val >> 32, p); ++ __put_unaligned_be32(val, p + 4); ++} ++ ++static inline uint16_t get_unaligned_be16(const void *p) ++{ ++ return __get_unaligned_be16((const uint8_t *)p); ++} ++ ++static inline uint32_t get_unaligned_be32(const void *p) ++{ ++ return __get_unaligned_be32((const uint8_t *)p); ++} ++ ++static inline uint64_t get_unaligned_be64(const void *p) ++{ ++ return __get_unaligned_be64((const uint8_t *)p); ++} ++ ++static inline void put_unaligned_be16(uint16_t val, void *p) ++{ ++ __put_unaligned_be16(val, p); ++} ++ ++static inline void put_unaligned_be32(uint32_t val, void *p) ++{ ++ __put_unaligned_be32(val, p); ++} ++ ++static inline void put_unaligned_be64(uint64_t val, void *p) ++{ ++ __put_unaligned_be64(val, p); ++} ++ ++#endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */ +diff --git a/usr/bs.c b/usr/bs.c +index f100e2c..cef7b19 100644 +--- a/usr/bs.c ++++ b/usr/bs.c +@@ -125,7 +125,15 @@ static void bs_thread_request_done(int fd, int events, void *data) + cmd->scsi_cmd_done(cmd, scsi_get_result(cmd)); + } + +- write(info->command_fd[1], &nr_events, sizeof(nr_events)); ++rewrite: ++ ret = write(info->command_fd[1], &nr_events, sizeof(nr_events)); ++ if (ret < 0) { ++ eprintf("can't write done, %m\n"); ++ if (errno == EAGAIN || errno == EINTR) ++ goto rewrite; ++ ++ return; ++ } + } + + static void *bs_thread_worker_fn(void *arg) +@@ -140,7 +148,7 @@ static void *bs_thread_worker_fn(void *arg) + pthread_cond_wait(&info->pending_cond, &info->pending_lock); + if (info->stop) { + pthread_mutex_unlock(&info->pending_lock); +- break; ++ pthread_exit(NULL); + } + goto retest; + } +@@ -200,11 +208,32 @@ int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn) + for (i = 0; i < ARRAY_SIZE(info->worker_thread); i++) { + ret = pthread_create(&info->worker_thread[i], NULL, + bs_thread_worker_fn, info); ++ if (ret) ++ goto destroy_threads; ++ } ++rewrite: ++ ret = write(info->command_fd[1], &ret, sizeof(ret)); ++ if (ret < 0) { ++ eprintf("can't write done, %m\n"); ++ if (errno == EAGAIN || errno == EINTR) ++ goto rewrite; + } + ++ return 0; ++destroy_threads: + write(info->command_fd[1], &ret, sizeof(ret)); ++ pthread_cancel(info->ack_thread); ++ pthread_cond_signal(&info->finished_cond); ++ pthread_join(info->ack_thread, NULL); + +- return 0; ++ info->stop = 1; ++ for (i = 0; info->worker_thread[i]; i++) { ++ pthread_cancel(info->worker_thread[i]); ++ pthread_cond_signal(&info->pending_cond); ++ } ++ ++ for (i = 0; info->worker_thread[i]; i++) ++ pthread_join(info->worker_thread[i], NULL); + event_del: + tgt_event_del(info->done_fd[0]); + close_done_fd: +diff --git a/usr/bs_aio.h b/usr/bs_aio.h +index ad1cc3a..f62e99c 100644 +--- a/usr/bs_aio.h ++++ b/usr/bs_aio.h +@@ -44,6 +44,10 @@ enum { + #define __NR_eventfd 284 + #elif defined(__i386__) + #define __NR_eventfd 323 ++#elif defined(__powerpc__) ++#define __NR_eventfd 307 ++#elif defined(__powerpc64__) ++#define __NR_eventfd 307 + #elif defined(__ia64__) + #define __NR_eventfd 1309 + #elif defined(__sparc__) || defined(__sparc64__) +diff --git a/usr/log.c b/usr/log.c +index 4b71216..076c770 100644 +--- a/usr/log.c ++++ b/usr/log.c +@@ -108,9 +108,6 @@ static int logarea_init (int size) + return 1; + } + +- la->ops[0].sem_num = 0; +- la->ops[0].sem_flg = 0; +- + return 0; + } + +@@ -237,21 +234,24 @@ static void log_syslog (void * buff) + static void dolog(int prio, const char *fmt, va_list ap) + { + struct timespec ts; ++ struct sembuf ops; + + if (la) { + ts.tv_sec = 0; + ts.tv_nsec = 10000; + +- la->ops[0].sem_op = -1; +- if (semtimedop(la->semid, la->ops, 1, &ts) < 0) { ++ ops.sem_num = 0; ++ ops.sem_flg = 0; ++ ops.sem_op = -1; ++ if (semtimedop(la->semid, &ops, 1, &ts) < 0) { + syslog(LOG_ERR, "semop up failed"); + return; + } + + log_enqueue(prio, fmt, ap); + +- la->ops[0].sem_op = 1; +- if (semop(la->semid, la->ops, 1) < 0) { ++ ops.sem_op = 1; ++ if (semop(la->semid, &ops, 1) < 0) { + syslog(LOG_ERR, "semop down failed"); + return; + } +@@ -291,15 +291,21 @@ void log_debug(const char *fmt, ...) + + static void log_flush(void) + { ++ struct sembuf ops; ++ + while (!la->empty) { +- la->ops[0].sem_op = -1; +- if (semop(la->semid, la->ops, 1) < 0) { ++ ops.sem_num = 0; ++ ops.sem_flg = 0; ++ ops.sem_op = -1; ++ if (semop(la->semid, &ops, 1) < 0) { + syslog(LOG_ERR, "semop up failed"); + exit(1); + } ++ + log_dequeue(la->buff); +- la->ops[0].sem_op = 1; +- if (semop(la->semid, la->ops, 1) < 0) { ++ ++ ops.sem_op = 1; ++ if (semop(la->semid, &ops, 1) < 0) { + syslog(LOG_ERR, "semop down failed"); + exit(1); + } +diff --git a/usr/log.h b/usr/log.h +index 6993235..b84f6d6 100644 +--- a/usr/log.h ++++ b/usr/log.h +@@ -55,7 +55,6 @@ struct logarea { + void *start; + void *end; + char *buff; +- struct sembuf ops[1]; + int semid; + union semun semarg; + }; +diff --git a/usr/parser.c b/usr/parser.c +index 7b892a5..0b244e4 100644 +--- a/usr/parser.c ++++ b/usr/parser.c +@@ -30,6 +30,7 @@ static int match_one(char *s, char *p, substring_t args[]) + { + char *meta; + int argc = 0; ++ unsigned long long ret; + + if (!p) + return 1; +@@ -68,16 +69,16 @@ static int match_one(char *s, char *p, substring_t args[]) + args[argc].to = s + len; + break; + case 'd': +- strtol(s, &args[argc].to, 0); ++ ret = strtol(s, &args[argc].to, 0); + goto num; + case 'u': +- strtoul(s, &args[argc].to, 0); ++ ret = strtoul(s, &args[argc].to, 0); + goto num; + case 'o': +- strtoul(s, &args[argc].to, 8); ++ ret = strtoul(s, &args[argc].to, 8); + goto num; + case 'x': +- strtoul(s, &args[argc].to, 16); ++ ret = strtoul(s, &args[argc].to, 16); + num: + if (args[argc].to == args[argc].from) + return 0; +diff --git a/usr/smc.c b/usr/smc.c +index 9d7f681..ab36e9c 100644 +--- a/usr/smc.c ++++ b/usr/smc.c +@@ -225,6 +225,41 @@ static int build_element_descriptors(uint8_t *data, struct list_head *head, + } + + /** ++ * smc_initialize_element_status with range ++ * - INITIALIZE ELEMENT STATUS WITH RANGE op code ++ * ++ * Support the SCSI op code INITIALIZE_ELEMENT_STATUS_WITH_RANGE ++ * Ref: smc3r11, 6.5 ++ */ ++static int smc_initialize_element_status_range(int host_no, struct scsi_cmd *cmd) ++{ ++ scsi_set_in_resid_by_actual(cmd, 0); ++ ++ if (device_reserved(cmd)) ++ return SAM_STAT_RESERVATION_CONFLICT; ++ else ++ return SAM_STAT_GOOD; ++} ++ ++/** ++ * smc_initialize_element_status - INITIALIZE ELEMENT STATUS op code ++ * ++ * Some backup libraries seem to require this. ++ * ++ * Support the SCSI op code INITIALIZE_ELEMENT_STATUS ++ * Ref: smc3r10a, 6.2 ++ */ ++static int smc_initialize_element_status(int host_no, struct scsi_cmd *cmd) ++{ ++ scsi_set_in_resid_by_actual(cmd, 0); ++ ++ if (device_reserved(cmd)) ++ return SAM_STAT_RESERVATION_CONFLICT; ++ else ++ return SAM_STAT_GOOD; ++} ++ ++/** + * smc_read_element_status - READ ELEMENT STATUS op code + * + * Support the SCSI op code READ ELEMENT STATUS +@@ -748,7 +783,7 @@ struct device_type_template smc_template = { + {spc_illegal_op,}, + {spc_illegal_op,}, + {spc_illegal_op,}, +- {spc_illegal_op,}, ++ {smc_initialize_element_status,}, + + {spc_illegal_op,}, + {spc_illegal_op,}, +@@ -778,7 +813,28 @@ struct device_type_template smc_template = { + {spc_illegal_op,}, + {spc_illegal_op,}, + +- [0x20 ... 0x4f] = {spc_illegal_op,}, ++ [0x20 ... 0x2f] = {spc_illegal_op,}, ++ ++ /* 0x30 */ ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {smc_initialize_element_status_range,}, ++ ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ {spc_illegal_op,}, ++ ++ [0x40 ... 0x4f] = {spc_illegal_op,}, + + /* 0x50 */ + {spc_illegal_op,}, +diff --git a/usr/spc.c b/usr/spc.c +index bd2c975..60fd7d7 100644 +--- a/usr/spc.c ++++ b/usr/spc.c +@@ -318,6 +318,16 @@ int spc_test_unit(int host_no, struct scsi_cmd *cmd) + return SAM_STAT_CHECK_CONDITION; + } + ++int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd) ++{ ++ /* TODO: implement properly */ ++ ++ if (device_reserved(cmd)) ++ return SAM_STAT_RESERVATION_CONFLICT; ++ else ++ return SAM_STAT_GOOD; ++} ++ + int spc_mode_select(int host_no, struct scsi_cmd *cmd, + int (*update)(struct scsi_cmd *, uint8_t *, int *)) + { +diff --git a/usr/spc.h b/usr/spc.h +index 8fe3e3c..cfc9cf3 100644 +--- a/usr/spc.h ++++ b/usr/spc.h +@@ -8,6 +8,7 @@ extern int spc_report_luns(int host_no, struct scsi_cmd *cmd); + extern int spc_start_stop(int host_no, struct scsi_cmd *cmd); + extern int spc_test_unit(int host_no, struct scsi_cmd *cmd); + extern int spc_request_sense(int host_no, struct scsi_cmd *cmd); ++extern int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd); + extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd); + extern int spc_lu_init(struct scsi_lu *lu); + +diff --git a/usr/ssc.c b/usr/ssc.c +index 2630a6a..96c3242 100644 +--- a/usr/ssc.c ++++ b/usr/ssc.c +@@ -192,7 +192,7 @@ static struct device_type_template ssc_template = { + {spc_start_stop,}, + {spc_illegal_op,}, + {spc_illegal_op,}, +- {spc_illegal_op,}, ++ {spc_prevent_allow_media_removal,}, + {spc_illegal_op,}, + + /* 0x20 */ +@@ -298,7 +298,7 @@ static struct device_type_template ssc_template = { + {spc_report_luns,}, + {spc_illegal_op,}, + {spc_illegal_op,}, +- {spc_illegal_op,}, ++ {spc_maint_in, maint_in_service_actions,}, + {spc_illegal_op,}, + {spc_illegal_op,}, + {spc_illegal_op,}, +diff --git a/usr/target.c b/usr/target.c +index 70bf72a..32812d9 100644 +--- a/usr/target.c ++++ b/usr/target.c +@@ -491,7 +491,7 @@ int tgt_device_create(int tid, int dev_type, uint64_t lun, char *params, + if (lu->bst->bs_init) { + ret = lu->bst->bs_init(lu); + if (ret) +- goto fail_bs_init; ++ goto fail_lu_init; + } + + if (backing && !path && !lu->attrs.removable) { +diff --git a/usr/util.h b/usr/util.h +index ac4b380..794c70b 100644 +--- a/usr/util.h ++++ b/usr/util.h +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include "be_byteshift.h" + + #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) diff --git a/tgt-fix-build b/tgt-fix-build index b594847..e2fcff7 100644 --- a/tgt-fix-build +++ b/tgt-fix-build @@ -18,7 +18,7 @@ index 1e214ee..2ae9193 100755 { name=$(basename $0) diff --git a/usr/Makefile b/usr/Makefile -index 4245709..1720d89 100644 +index a59364b..bf264f4 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -50,7 +50,7 @@ INCLUDES += -I. -I../include -I$(KERNELSRC)/include @@ -43,6 +43,208 @@ index febb1e9..59f7573 100644 #include #include +diff --git a/usr/iscsi/iscsi_rdma.c b/usr/iscsi/iscsi_rdma.c +index d3b5147..c155b48 100644 +--- a/usr/iscsi/iscsi_rdma.c ++++ b/usr/iscsi/iscsi_rdma.c +@@ -144,7 +144,7 @@ struct conn_info { + /* but count so we can drain CQ on close */ + int recvl_posted; + +- struct tgt_event tx_sched; ++ struct tgtd_event tx_sched; + + /* login phase resources, freed at full-feature */ + void *srbuf_login; +@@ -196,7 +196,7 @@ struct iser_device { + void *mempool_listbuf; + struct ibv_mr *mempool_mr; + +- struct tgt_event poll_sched; ++ struct tgtd_event poll_sched; + + /* free and allocated mempool entries */ + struct list_head mempool_free, mempool_alloc; +@@ -281,9 +281,9 @@ static void iscsi_rdma_release(struct iscsi_connection *conn); + static int iscsi_rdma_show(struct iscsi_connection *conn, char *buf, + int rest); + static void iscsi_rdma_event_modify(struct iscsi_connection *conn, int events); +-static void iser_sched_poll_cq(struct tgt_event *tev); +-static void iser_sched_consume_cq(struct tgt_event *tev); +-static void iser_sched_tx(struct tgt_event *evt); ++static void iser_sched_poll_cq(struct tgtd_event *tev); ++static void iser_sched_consume_cq(struct tgtd_event *tev); ++static void iser_sched_tx(struct tgtd_event *evt); + + /* + * Called when ready for full feature, builds resources. +@@ -1058,7 +1058,7 @@ static void iser_poll_cq_armable(struct iser_device *dev) + /* Scheduled to poll cq after a completion event has been + received and acknowledged, if no more completions are found + the interrupts are re-armed */ +-static void iser_sched_poll_cq(struct tgt_event *tev) ++static void iser_sched_poll_cq(struct tgtd_event *tev) + { + struct iser_device *dev = tev->data; + iser_poll_cq_armable(dev); +@@ -1069,7 +1069,7 @@ static void iser_sched_poll_cq(struct tgt_event *tev) + the notification interrupts were re-armed. + Intended to consume those remaining completions only, + this function does not re-arm interrupts. */ +-static void iser_sched_consume_cq(struct tgt_event *tev) ++static void iser_sched_consume_cq(struct tgtd_event *tev) + { + struct iser_device *dev = tev->data; + int ret; +@@ -1112,7 +1112,7 @@ static void iser_cqe_handler(int fd __attribute__((unused)), + * tries to push tx on a connection, until nothing + * is ready anymore. No progress limit here. + */ +-static void iser_sched_tx(struct tgt_event *evt) ++static void iser_sched_tx(struct tgtd_event *evt) + { + struct conn_info *ci = evt->data; + struct iscsi_connection *conn = &ci->iscsi_conn; +diff --git a/usr/tgtd.c b/usr/tgtd.c +index 62aaa04..a226906 100644 +--- a/usr/tgtd.c ++++ b/usr/tgtd.c +@@ -99,7 +99,7 @@ static int oom_adjust(void) + int tgt_event_add(int fd, int events, event_handler_t handler, void *data) + { + struct epoll_event ev; +- struct tgt_event *tev; ++ struct tgtd_event *tev; + int err; + + tev = zalloc(sizeof(*tev)); +@@ -123,9 +123,9 @@ int tgt_event_add(int fd, int events, event_handler_t handler, void *data) + return err; + } + +-static struct tgt_event *tgt_event_lookup(int fd) ++static struct tgtd_event *tgt_event_lookup(int fd) + { +- struct tgt_event *tev; ++ struct tgtd_event *tev; + + list_for_each_entry(tev, &tgt_events_list, e_list) { + if (tev->fd == fd) +@@ -136,7 +136,7 @@ static struct tgt_event *tgt_event_lookup(int fd) + + void tgt_event_del(int fd) + { +- struct tgt_event *tev; ++ struct tgtd_event *tev; + + tev = tgt_event_lookup(fd); + if (!tev) { +@@ -152,7 +152,7 @@ void tgt_event_del(int fd) + int tgt_event_modify(int fd, int events) + { + struct epoll_event ev; +- struct tgt_event *tev; ++ struct tgtd_event *tev; + + tev = tgt_event_lookup(fd); + if (!tev) { +@@ -167,7 +167,7 @@ int tgt_event_modify(int fd, int events) + return epoll_ctl(ep_fd, EPOLL_CTL_MOD, fd, &ev); + } + +-void tgt_init_sched_event(struct tgt_event *evt, ++void tgt_init_sched_event(struct tgtd_event *evt, + sched_event_handler_t sched_handler, void *data) + { + evt->sched_handler = sched_handler; +@@ -176,7 +176,7 @@ void tgt_init_sched_event(struct tgt_event *evt, + INIT_LIST_HEAD(&evt->e_list); + } + +-void tgt_add_sched_event(struct tgt_event *evt) ++void tgt_add_sched_event(struct tgtd_event *evt) + { + if (!evt->scheduled) { + evt->scheduled = 1; +@@ -184,7 +184,7 @@ void tgt_add_sched_event(struct tgt_event *evt) + } + } + +-void tgt_remove_sched_event(struct tgt_event *evt) ++void tgt_remove_sched_event(struct tgtd_event *evt) + { + if (evt->scheduled) { + evt->scheduled = 0; +@@ -195,7 +195,7 @@ void tgt_remove_sched_event(struct tgt_event *evt) + static int tgt_exec_scheduled(void) + { + struct list_head *last_sched; +- struct tgt_event *tev, *tevn; ++ struct tgtd_event *tev, *tevn; + int work_remains = 0; + + if (!list_empty(&tgt_sched_events_list)) { +@@ -218,7 +218,7 @@ static void event_loop(void) + { + int nevent, i, sched_remains, timeout; + struct epoll_event events[1024]; +- struct tgt_event *tev; ++ struct tgtd_event *tev; + + retry: + sched_remains = tgt_exec_scheduled(); +@@ -232,7 +232,7 @@ retry: + } + } else if (nevent) { + for (i = 0; i < nevent; i++) { +- tev = (struct tgt_event *) events[i].data.ptr; ++ tev = (struct tgtd_event *) events[i].data.ptr; + tev->handler(tev->fd, events[i].events, tev->data); + } + } else +diff --git a/usr/tgtd.h b/usr/tgtd.h +index da751c8..207c167 100644 +--- a/usr/tgtd.h ++++ b/usr/tgtd.h +@@ -206,21 +206,21 @@ extern int tgt_bind_host_to_target(int tid, int host_no); + extern int tgt_unbind_host_to_target(int tid, int host_no); + extern int tgt_bound_target_lookup(int host_no); + +-struct tgt_event; +-typedef void (*sched_event_handler_t)(struct tgt_event *tev); ++struct tgtd_event; ++typedef void (*sched_event_handler_t)(struct tgtd_event *tev); + +-extern void tgt_init_sched_event(struct tgt_event *evt, ++extern void tgt_init_sched_event(struct tgtd_event *evt, + sched_event_handler_t sched_handler, void *data); + + typedef void (*event_handler_t)(int fd, int events, void *data); + +-extern int tgt_event_add(int fd, int events, event_handler_t handler, void *data); +-extern void tgt_event_del(int fd); ++extern int tgtd_event_add(int fd, int events, event_handler_t handler, void *data); ++extern void tgtd_event_del(int fd); + +-extern void tgt_add_sched_event(struct tgt_event *evt); +-extern void tgt_remove_sched_event(struct tgt_event *evt); ++extern void tgt_add_sched_event(struct tgtd_event *evt); ++extern void tgt_remove_sched_event(struct tgtd_event *evt); + +-extern int tgt_event_modify(int fd, int events); ++extern int tgtd_event_modify(int fd, int events); + extern int target_cmd_queue(int tid, struct scsi_cmd *cmd); + extern void target_cmd_done(struct scsi_cmd *cmd); + struct scsi_cmd *target_cmd_lookup(int tid, uint64_t itn_id, uint64_t tag); +@@ -269,7 +269,7 @@ extern int dtd_load_unload(int tid, uint64_t lun, int load, char *file); + extern int register_backingstore_template(struct backingstore_template *bst); + extern struct backingstore_template *get_backingstore_template(const char *name); + +-struct tgt_event { ++struct tgtd_event { + union { + event_handler_t handler; + sched_event_handler_t sched_handler; diff --git a/usr/tgtif.c b/usr/tgtif.c index fd5ad5b..9b87b13 100644 --- a/usr/tgtif.c diff --git a/tgt-git-update b/tgt-git-update index 6020382..84da72d 100644 --- a/tgt-git-update +++ b/tgt-git-update @@ -1,1157 +1,1326 @@ -diff --git a/README b/README -index dd99e74..22785fc 100644 ---- a/README -+++ b/README -@@ -3,46 +3,53 @@ Introduction - Linux target framework (tgt) aims to simplify various SCSI target - driver (iSCSI, Fibre Channel, SRP, etc) creation and maintenance. +diff --git a/doc/targets.conf.example b/doc/targets.conf.example +index 46be8fe..ac8cf69 100644 +--- a/doc/targets.conf.example ++++ b/doc/targets.conf.example +@@ -81,6 +81,7 @@ default-driver iscsi + # Note that some parameters (write-cache, scsi_sn) were specified "globally". + # "Global" parameters will be applied to all LUNs; they can be overwritten + # "locally", per LUN. ++# If lun is not specified, it will be allocated automatically (first available). --Tgt consists of kernel modules, user-space daemon, and user-space --tools. Some target drivers uses all of them and some use only --user-space daemon and tools (i.e. they completely runs in user space). -+Currently, tgt supports the following target drivers: + --Currently, tgt supports three target drivers: -+- iSCSI software target driver for Ethernet NICs +@@ -88,26 +89,26 @@ default-driver iscsi + vendor_id VENDOR1 + removable 1 + device-type cd +- # lun 1 # Not yet supported ++ lun 1 + --- IBM VIO server (ibmvstgt) --- iSCSI --- Xen vscsifront/back -+- iSER software target driver for Infiniband and RDMA NICs + + vendor_id VENDOR2 +- # lun 2 # Not yet supported ++ lun 2 + --Note that tgt is under active development. Don't play with important --data. -+- IBM System p VIO server + + vendor_id back1 + scsi_sn SERIAL + write-cache on +- # lun 3 # Not yet supported ++ # lun 3 # lun is commented out - will be allocated automatically + --The code is under the GNU General Public License version 2. -+- FCoE software target driver for Ethernet NICs (in progress) + + vendor_id back2 + #mode_page 8:0:18:0x10:0:0xff.... + #mode_page 8:0:18:0x10:0:0xff.... +- # lun 4 # Not yet supported ++ lun 15 + -+- Qlogic qla2xxx FC target driver (in progress) + # Some more parameters which can be specified locally or globally: +@@ -123,11 +124,12 @@ default-driver iscsi + #mode_page 8:0:18:0x10:0:0xff.... + #mode_page 8:0:18:0x10:0:0xff.... + #device-type ++ #allow-in-use # if specified globally, can't be overwritten locally --Preparation --------------- --The iSCSI target driver can works with the 2.6.X kernels. It requires --OpenSSL library (libssl-dev for debian, openssl-devel for Fedora). -+Tgt consists of kernel modules, user-space daemon, and user-space -+tools. iSCSI, iSER, and FCoE target drivers use only user-space daemon -+and tools (i.e. they are just user-space applications. They don't need -+any kernel support). -+ -+tgt can emulate the following device types: + write-cache off + scsi_sn multipath-10 --host:~/tgt/usr$ make ISCSI=1 -+- SBC: a virtual disk drive that can use a file to store the content. +- # Parameters below are global. They can't be configured per LUN. ++ # Parameters below are only global. They can't be configured per LUN. + # Only allow connections from 192.168.100.1 and 192.168.200.5 + initiator-address 192.168.100.1 + initiator-address 192.168.200.5 +@@ -142,10 +144,47 @@ default-driver iscsi + --If you want IBM VIO target driver, get kernel version 2.6.20, rebuild --the kernel, and reboot with the new kernel. Note you need to enable --SCSI_TGT, SCSI_SRP, and SCSI_IBMVSCSIS kernel options. -+- SMC: a virtual media jukebox that can be controlled by the "mtx" -+tool (partially functional). --host:~/tgt/usr$ make KERNELSRC= IBMVIO=1 -+- MMC: a virtual DVD drive that can read DVD-ROM iso files and create -+burnable DVD+R. It can be combined with SMC to provide a fully -+operational DVD jukebox. - --Make sure that everything is built successfully. -+- SSC: a virtual tape device (aka VTL) that can use a file to store -+the content (in progress). - --Now you can run tgt. Target drivers have their own ways for --configuration. So find an appropriate documentation in the doc --directory. -+- OSD: a virtual object-based storage device that can use a file to -+store the content (in progress). -+ -+The code is under the GNU General Public License version 2. ++# The device will have lun 1 unless you specify something else ++ ++ backing-store /dev/LVM/somedevice ++ lun 10 ++ + + -+Preparation -+------------- -+Target drivers have their own ways to build, configure, etc. So find -+an appropriate documentation in the doc directory. - - - Developer Notes - ------------- - The central resource for tgt development is the mailing list --(stgt-devel@lists.berlios.de). -+(stgt@vger.kernel.org). - - First, please read the following documents (in short, follow Linux - kernel development rules): -diff --git a/doc/README.ibmvstgt b/doc/README.ibmvstgt -index 9a9dbd8..d77f932 100644 ---- a/doc/README.ibmvstgt -+++ b/doc/README.ibmvstgt -@@ -1,5 +1,13 @@ - Starting - ------------- -+If you want IBM VIO target driver, get kernel version 2.6.20, rebuild -+the kernel, and reboot with the new kernel. Note you need to enable -+SCSI_TGT, SCSI_SRP, and SCSI_IBMVSCSIS kernel options. ++# Devices which are in use (by system: mounted, for swap, part of RAID, or by ++# userspace: dd, by tgtd for another target etc.) can't be used, unless you use ++# --force flag or add 'allow-in-use yes' option ++ ++ backing-store /dev/LVM/somedevice ++ allow-in-use yes ++ ++ ++ ++ ++ scsi_sn serial1 ++ ++ ++ ++ scsi_sn serial2 ++ ++ ++ allow-in-use yes ++ ++ + -+host:~/tgt/usr$ make KERNELSRC= IBMVIO=1 + -+Make sure that everything is built successfully. + - Try the following commands: - host:~/tgt$ su -diff --git a/doc/README.iscsi b/doc/README.iscsi -index 5aec190..e6b4d09 100644 ---- a/doc/README.iscsi -+++ b/doc/README.iscsi -@@ -5,6 +5,13 @@ This show a simple example to set up some targets. + # Not supported configurations, and therefore, commented out: - Starting the daemon - ------------- -+The iSCSI target driver works with the 2.6.X kernels. It requires -+OpenSSL library (libssl-dev for debian, openssl-devel for Fedora). +-# ++# ++# backing-store /dev/LVM/somedevice1 ++# backing-store /dev/LVM/somedevice2 ++# lun 10 ++# lun 11 ++# + -+First, you need to compile the source code: -+ -+host:~/tgt/usr$ make ISCSI=1 -+ - Try the following commands: ++# + # + # vendor_id VENDOR1 + # +@@ -155,7 +194,7 @@ default-driver iscsi - host:~/tgt$ su + # This one will break the parser: + +-# ++# + # + # vendor_id VENDOR1 + # diff --git a/scripts/tgt-admin b/scripts/tgt-admin -index fe95723..22c4725 100755 +index e4be373..c352952 100755 --- a/scripts/tgt-admin +++ b/scripts/tgt-admin -@@ -30,6 +30,8 @@ This tool configures tgt targets. - (see "--offline help" for more info) - --ready put all or selected targets in ready state - (see "--ready help" for more info) -+ --update update configuration for all or selected targets -+ (see "--update help" for more info) - -s, --show show all the targets - -c, --conf specify an alternative configuration file - --ignore-errors continue even if tgtadm exits with non-zero code -@@ -49,6 +51,7 @@ my $execute = 0; - my $delete = 0; - my $offline = 0; - my $ready = 0; -+my $update = 0; - my $show = 0; - my $alternate_conf="0"; - my $ignore_errors = 0; -@@ -62,6 +65,7 @@ my $result = GetOptions ( - "delete=s" => \$delete, - "offline=s" => \$offline, - "ready=s" => \$ready, -+ "update=s" => \$update, - "s|show" => \$show, - "c|conf=s" => \$alternate_conf, - "ignore-errors" => \$ignore_errors, -@@ -147,7 +151,10 @@ my $option; - my $value; - - sub add_targets { -- -+ my $single_target = $_[0]; -+ my $configured = $_[1]; -+ my $connected = $_[2]; -+ my $in_configfile = $_[3]; - foreach my $k (sort keys %conf) { - - if ( $k eq "default-driver" ) { -@@ -170,57 +177,85 @@ sub add_targets { - foreach my $k (sort keys %conf) { - if ( $k eq "target" ) { - foreach my $k2 (sort keys %{$conf{$k}}) { -- $target = $k2; -- my $allowall = 1; -- if ( not defined $tgtadm_output{$k2} ) { -- # We have to find available tid -- $next_tid = $next_tid + 1; -+ # Do we run update or execute? -+ if (length $single_target) { -+ if ($single_target ne $k2) { -+ next; -+ } else { -+ $target = $single_target; -+ } -+ } else { -+ $target = $k2; - } -- else { -- execute("# Target $target already exist!"); -- execute("# Updating Target $target"); -- execute("tgtadm --op update --mode target --tid=$next_tid -n state -v offline"); -- execute("tgtadm --mode target --op delete --tid=$next_tid"); -+ -+ my $in_use = 0; -+ if (length $single_target) { -+ $in_use = &main_delete($target); - } -+ my $allowall = 1; -+ if ((not defined $tgtadm_output{$k2}) || -+ ($update ne 0 && $in_use == 0) || -+ ($update ne 0 && $in_use == 1 && $pretend == 1 && $force == 1)) -+ { -+ # We have to find available tid -+ if (($in_configfile == 1) && ($configured == 0)) { -+ my $maxtid = &find_max_tid; -+ $next_tid = $maxtid + 1; -+ } elsif (length $single_target) { -+ $next_tid = $tgtadm_output_tid{$target}; -+ } else { -+ $next_tid = $next_tid + 1; -+ } -+ # Before we add a target, we need to know its type -+ my $driver; -+ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { -+ $option = $k3; -+ $value = $conf{$k}{$k2}{$k3}; -+ &check_value($value); -+ if ($option eq "driver") { -+ if (ref($value) eq "ARRAY") { -+ print "Multiple driver definitions not allowed!\n"; -+ print "Check your config file for errors (target: $target).\n"; -+ exit 1; -+ } -+ $driver = $value; -+ } -+ } -+ -+ if (not defined $driver) { -+ $driver = $default_driver; -+ } - -- # Before we add a target, we need to know its type -- my $driver; -- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { -- $option = $k3; -- $value = $conf{$k}{$k2}{$k3}; -- &check($value); -- if ( $option eq "driver" ) { -- if (ref($value) eq "ARRAY") { -- print "Multiple driver definitions not allowed!\n"; -- print "Check your config file for errors (target: $target).\n"; -- exit 1; -+ execute("# Adding target: $target"); -+ execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target"); -+ foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { -+ $option = $k3; -+ $value = $conf{$k}{$k2}{$k3}; -+ &check_value($value); -+ &process_options($driver); -+ # If there was no option called "initiator-address", it means -+ # we want to allow ALL initiators for this target -+ if ($option eq "initiator-address") { -+ $allowall = 0; - } -- $driver = $value; +@@ -125,7 +125,7 @@ sub process_targets { + sub parse_configs { + # Parse the config + if ($alternate_conf ne 0) { +- # Check if alternative configuration file exist ++ # Check if alternative configuration file exists + if (-e "$alternate_conf") { + execute("# Using $alternate_conf as configuration file\n"); + %conf = ParseConfig(-ConfigFile => "$alternate_conf", -UseApacheInclude => 1, -IncludeGlob => 1,); +@@ -211,6 +211,7 @@ sub add_targets { + # and other parameters which can be specified globally + my %target_options; + my $target_options_ref; ++ my $data_key; + foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { + $lun = 1; + $option = $k3; +@@ -218,6 +219,7 @@ sub add_targets { + check_value($value); + $target_options{$option} = $value; + $target_options_ref = \%target_options; ++ $data_key = make_key($target_options_ref, "lun", "allow-in-use"); } -- } -- if ( not defined $driver ) { -- $driver = $default_driver; -- } -- execute("# Adding target: $target"); -- execute("tgtadm --lld $driver --op new --mode target --tid $next_tid -T $target"); -- foreach my $k3 (sort keys %{$conf{$k}{$k2}}) { -- $option = $k3; -- $value = $conf{$k}{$k2}{$k3}; -- &check($value); -- &process_options($driver); -- # If there was no option called "initiator-address", it means -- # we want to allow ALL initiators for this target -- if ( $option eq "initiator-address" ) { -- $allowall = 0; -+ if ($allowall == 1) { -+ execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL"); - } -- } - -- if ( $allowall == 1 ) { -- execute("tgtadm --lld $driver --op bind --mode target --tid $next_tid -I ALL"); -+ } else { -+ if (not length $configured || $in_use eq 1) { -+ execute("# Target $target already exists!"); -+ } - } -- execute(); - } -+ if (length $single_target && $in_configfile == 0 && $configured == 0) { -+ print "Target $single_target is $connected currently not configured\n"; -+ print "and does not exist in the config file - can't continue!\n"; -+ exit 1; -+ } -+ execute(); - } + if (not defined $target_options{"driver"}) { +@@ -230,7 +232,7 @@ sub add_targets { + $option = $k3; + $value = $conf{$k}{$k2}{$k3}; + check_value($value); +- process_options($target_options_ref); ++ process_options($target_options_ref,$data_key); + # If there was no option called "initiator-address", it means + # we want to allow ALL initiators for this target + if ($option eq "initiator-address") { +@@ -258,6 +260,27 @@ sub add_targets { } } -@@ -262,22 +297,29 @@ sub process_options { - my @value_arr = @$value; - my $i = 1; - foreach my $direct_store (@value_arr) { -- $inq=`sg_inq $direct_store`; -- if ($inq=~/Vendor identification:\s*(\w+)\s*\n*Product identification:\s*([\w\s\/\-]+)\n\s*\n*Product revision level:\s*(\w*)\s*\n*Unit serial number:\s*(\w+)/) -- { -- $vendor_id="$1"; -- $prod_id="$2"; -- $prod_rev="$3"; -- $scsi_serial="$4"; -- } -- $vendor_id =~ s/\s+$//; -- $prod_id =~ s/\s+$//; -- $prod_rev =~ s/\s+$//; -- $scsi_serial =~ s/\s+$//; -+ # Check if device exists -+ if ( -e $direct_store) { -+ $inq=`sg_inq $direct_store`; -+ if ($inq=~/Vendor identification:\s*(\w+)\s*\n*Product identification:\s*([\w\s\/\-]+)\n\s*\n*Product revision level:\s*(\w*)\s*\n*Unit serial number:\s*(\w+)/) -+ { -+ $vendor_id="$1"; -+ $prod_id="$2"; -+ $prod_rev="$3"; -+ $scsi_serial="$4"; + ++# Pre-parse the config and get some values we need ++sub make_key { ++ my $target_options_ref = shift; ++ my @actions = @_; ++ my %data_key; ++ ++ foreach my $action (@actions) { ++ if (ref $$target_options_ref{'backing-store'} eq "HASH") { ++ foreach my $testlun (keys %{$$target_options_ref{'backing-store'}}) { ++ $data_key{$testlun}{$action} = $$target_options_ref{'backing-store'}{$testlun}{$action}; ++ } ++ } ++ if (ref $$target_options_ref{'direct-store'} eq "HASH") { ++ foreach my $testlun (keys %{$$target_options_ref{'direct-store'}}) { ++ $data_key{$testlun}{$action} = $$target_options_ref{'direct-store'}{$testlun}{$action}; ++ } ++ } ++ } ++ return \%data_key; ++} ++ + # Some options can be specified only once + sub check_if_hash_array { + my $check = $_[0]; +@@ -285,9 +308,15 @@ sub check_exe { + foreach my $path (@path) { + if ( -x "$path/$command" && -f "$path/$command" ) { $exists = 1 } + } +- if ( $exists == 0 ) { +- print "Command $command (needed by $option option in your config file) is not in your path - can't continue!\n"; +- exit 1; ++ if ($exists == 0) { ++ if ($command eq "sg_inq") { ++ print "Command '$command' (needed by '$option') is not in your path - can't continue!\n"; ++ exit 1; ++ } elsif ($command eq "lsof") { ++ execute("# Command '$command' is not in your path."); ++ execute("# Can't reliably check if device is not in use."); ++ return 1; ++ } + } + } + +@@ -315,27 +344,61 @@ sub add_params { + } + } + ++# Find next available LUN ++sub find_next_lun { ++ my $backing_store = $_[0]; ++ my $data_key_ref = $_[1]; ++ my $lun_collision = 0; ++ my $lun_is_free = 0; ++ my $found_lun = 1; ++ while ($lun_is_free == 0) { ++ foreach my $testlun (keys %$data_key_ref) { ++ foreach my $testlun2 (values %{$$data_key_ref{$testlun}}) { ++ if ($found_lun eq $testlun2) { ++ $lun_collision = 1; + } -+ $vendor_id =~ s/\s+$//; -+ $prod_id =~ s/\s+$//; -+ $prod_rev =~ s/\s+$//; -+ $scsi_serial =~ s/\s+$//; - -- execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun 1 -b $direct_store"); -- execute("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun 1 --params vendor_id=\"$vendor_id\",product_id=\"$prod_id\",product_rev=\"$prod_rev\",scsi_sn=\"$scsi_serial\""); -- $i += 1; -+ execute("tgtadm --lld $driver --op new --mode logicalunit --tid $next_tid --lun 1 -b $direct_store"); -+ execute("tgtadm --lld $driver --op update --mode logicalunit --tid $next_tid --lun 1 --params vendor_id=\"$vendor_id\",product_id=\"$prod_id\",product_rev=\"$prod_rev\",scsi_sn=\"$scsi_serial\""); -+ $i += 1; + } -+ else { -+ print("skipping device $direct_store\n"); -+ print("$direct_store does not exist - please check the configuration file\n"); ++ } ++ if ($lun_collision == 0) { ++ $lun_is_free = 1; ++ } else { ++ $found_lun += 1; ++ } ++ $lun_collision = 0; ++ } ++ $$data_key_ref{$backing_store}{'lun'} = $found_lun; ++ return $found_lun; ++} ++ + # Add backing or direct store + sub add_backing_direct { + my $backing_store = $_[0]; + my $target_options_ref = $_[1]; +- my $lun = $_[2]; ++ my $lun; ++ my $data_key_ref = $_[2]; + my $direct_store = $_[3]; + my $driver = $$target_options_ref{"driver"}; + + # Is the device in use? +- (my $can_alloc, my $dev) = check_device($backing_store); ++ my $can_alloc = 1; ++ if ($force != 1 && $$target_options_ref{'allow-in-use'} ne "yes") { ++ $can_alloc = check_device($backing_store,$data_key_ref); ++ } + +- # Needed if the config file has mixed definitions +- if (ref($backing_store) eq "HASH") { +- foreach my $backing_store (sort keys %$value) { +- add_backing_direct($backing_store,$target_options_ref,$lun,$direct_store); +- $lun += 1; +- } +- return $lun; +- } elsif (-e $backing_store && $can_alloc == 1) { ++ if (-e $backing_store && ! -d $backing_store && $can_alloc == 1) { + my @exec_commands; + my $device_type; ++ my %luns; ++ my @added_luns; ++ # Find out LUNs which are "reserved" in the config file ++ if (ref $value eq "HASH") { ++ if (length $$data_key_ref{$backing_store}{'lun'}) { ++ $lun = $$data_key_ref{$backing_store}{'lun'}; ++ } else { ++ # Find an available lun if it wasn't specified ++ $lun = find_next_lun($backing_store,$data_key_ref); + } ++ } + # Process parameters for each lun / backing store + if (ref $value eq "HASH") { + my %params_added; +@@ -447,6 +510,11 @@ sub add_backing_direct { + check_if_hash_array($$target_options_ref{"device-type"}, "device-type"); + $device_type = $$target_options_ref{"device-type"}; + } ++ # lun ++ if (length $$target_options_ref{"lun"}) { ++ check_if_hash_array($$target_options_ref{"lun"}, "lun"); ++ $lun = $$target_options_ref{"lun"}; ++ } + } else { + print "If you got here, this means your config file is not supported.\n"; + print "Please report it to stgt mailing list and attach your config files.\n"; +@@ -461,7 +529,9 @@ sub add_backing_direct { + $lun += 1; + return $lun; + } elsif ($can_alloc == 0) { +- execute("# Skipping device $backing_store ($dev is mounted / in use)"); ++ execute("# Skipping device $backing_store - it is in use."); ++ execute("# You can override it with --force or 'allow-in-use yes' config option."); ++ execute("# Note - do so only if you know what you're doing, you may damage your data."); + } else { + execute("# Skipping device: $backing_store"); + execute("# $backing_store does not exist - please check the configuration file"); +@@ -471,11 +541,12 @@ sub add_backing_direct { + # Process options from the config file + sub process_options { + my $target_options_ref = $_[0]; ++ my $data_key_ref = $_[1]; + my $driver = $$target_options_ref{"driver"}; + if ($option eq "backing-store" || $option eq "direct-store") { + my $direct_store = 0; + if ($option eq "direct-store") { +- check_exe("sg_inq", "direct-store"); ++ check_exe("sg_inq", "option direct-store"); + $direct_store = 1; } - } -@@ -289,7 +331,7 @@ sub process_options { - my @value_arr = @$value; - foreach my $incominguser (@value_arr) { - my @userpass = split(/ /, $incominguser); -- &check($userpass[1]); -+ &check_value($userpass[1]); - execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]"); - execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]"); - execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0]"); -@@ -303,7 +345,7 @@ sub process_options { - } - execute("# Warning: only one outgoinguser is allowed. Will only use the first one."); - my @userpass = split(/ /, @$value[0]); -- &check($userpass[1]); -+ &check_value($userpass[1]); - execute("tgtadm --lld $driver --mode account --op delete --user=$userpass[0]"); - execute("tgtadm --lld $driver --mode account --op new --user=$userpass[0] --password=$userpass[1]"); - execute("tgtadm --lld $driver --mode account --op bind --tid=$next_tid --user=$userpass[0] --outgoing"); -@@ -322,7 +364,7 @@ sub process_options { - } +@@ -495,7 +566,13 @@ sub process_options { - # If the target is configured, but not present in the config file, --# offline it and try to remove it -+# try to remove it - sub remove_targets { - - &process_targets; -@@ -340,13 +382,8 @@ sub remove_targets { - } - - if ( $dontremove == 0 ) { -- # Right now, it is not possible to remove a target if any initiators -- # are connected to it. We'll do our best - offline the target first -- # (so it won't accept any new connections), and remove. -- # Note that remove will only work if no initiator is connected. -- execute("# Removing target: $existing_target"); -- execute("tgtadm --op update --mode target --tid=$tgtadm_output_tid{$existing_target} -n state -v offline"); -- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}"); -+ # Remove the target -+ &main_delete($existing_target); - } + if (ref($value) eq "HASH") { + foreach my $backing_store (sort keys %$value) { +- $lun = add_backing_direct($backing_store,$target_options_ref,$lun,$direct_store); ++ if ($backing_store =~ m/HASH/) { ++ print "\nYour config file is not supported. See targets.conf.example for details.\n"; ++ exit 1; ++ } ++ } ++ foreach my $backing_store (sort keys %$value) { ++ add_backing_direct($backing_store,$target_options_ref,$data_key_ref,$direct_store); } } -@@ -360,57 +397,50 @@ sub dump_config { + } +@@ -569,7 +646,7 @@ sub dump_config { my @all_targets = keys %tgtadm_output_tid; -- foreach my $target (@all_targets) { -- foreach my $show_target_line ($tgtadm_output{$target}) { -- if ( $show_target_line =~ m/^Target (\d*): (.+)/ ) { -- print "\n"; -- } -+ # If all targets use the same driver, us it only once in the config -+ my $skip_driver = 0; -+ my @drivers_combined; -+ foreach my $current_target (@all_targets) { -+ my $driver = &show_target_info($current_target, "driver"); -+ push (@drivers_combined, $driver); -+ } - -- if ( $show_target_line =~ m/\s+Driver: (.+)/ ) { -- print "\tdriver $1\n"; -- } -+ my %drivers_uniq; -+ @drivers_uniq{@drivers_combined} = (); -+ my @drivers_combined_uniq = sort keys %drivers_uniq; - -- if ( $show_target_line =~ m/\s+Backing store: (?!No backing store)(.+)/ ) { -- print "\tbacking-store $1\n"; -- } -- } -+ if (scalar @drivers_combined_uniq == 1) { -+ print "default-driver $drivers_combined_uniq[0]\n\n"; -+ } - -- # Process account and ACL information -- my $account_acl; -+ # Print everything else in the config -+ foreach my $current_target (@all_targets) { -+ my $target_name = &show_target_info($current_target, "target_name"); -+ print "\n"; - -- foreach my $show_target_line ($tgtadm_output{$target}) { -- $account_acl .= $show_target_line -+ if (scalar @drivers_combined_uniq gt 1) { -+ my $driver = &show_target_info($current_target, "driver"); -+ print "\tdriver $driver\n"; - } - -- # start with account information... -- while ($account_acl =~ m{ -- \s+Account\ information:\n(.*)ACL\ information: -- }xmgs -- ) { -- -- my @account = split(/\n/, $1); -- -- foreach my $user (@account) { -- my @var = split(/^\s+/, $user); -- @var = split(/\s/, $var[1]); -+ my @backing_stores = &show_target_info($current_target, "backing_stores"); -+ foreach my $backing_store (@backing_stores) { -+ print "\tbacking-store $backing_store\n"; -+ } - -- if ( $var[1] eq "(outgoing)" ) { -- print "\toutgoinguser $var[0] PLEASE_CORRECT_THE_PASSWORD\n"; -- } elsif ( ($var[0] ne "") && ($var[1] eq "") ) { -- print "\tincominguser $var[0] PLEASE_CORRECT_THE_PASSWORD\n"; -- } -+ my @account_information = &show_target_info($current_target, "account_information"); -+ foreach my $account (@account_information) { -+ if ($account =~ /(.+)\ \(outgoing\)/) { -+ print "\toutgoinguser $1 PLEASE_CORRECT_THE_PASSWORD\n"; -+ } elsif (length $account) { -+ print "\tincominguser $account PLEASE_CORRECT_THE_PASSWORD\n"; - } - } - -- #...and finish with ACL information -- while ($account_acl =~ m{ -- \s+ACL\ information:\n(.*) -- }xmgs -- ) { -- my @ini_addresses = split(/\n/, $1); -- foreach my $ini_address (@ini_addresses) { -- my @var = split(/^\s+/, $ini_address); -- print "\tinitiator-address $var[1]\n"; -+ my @acl_information = &show_target_info($current_target, "acl_information"); -+ if (scalar(@acl_information) != 1 || $acl_information[0] ne "ALL") { -+ foreach my $ini_address (@acl_information) { -+ print "\tinitiator-address $ini_address\n"; - } - } - print "\n\n"; -@@ -465,17 +495,42 @@ EOF - sub show_target_info { - my $existing_target = $_[0]; - my $task = $_[1]; -+ # Returns target information -+ if ($task eq "target_name") { -+ if ($tgtadm_output{$existing_target} =~ m/^Target (\d*): (.+)/ ) { -+ return $2; -+ } - # Returns driver information -- if ($task eq "driver") { -- if ( $tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) { -- print $1; -+ } elsif ($task eq "driver") { -+ if ($tgtadm_output{$existing_target} =~ m/\s+Driver: (.+)/ ) { - return $1; - } -+ # Returns backing store -+ } elsif ($task eq "backing_stores") { -+ if ($tgtadm_output{$existing_target} =~ m/\s+Backing store: (?!No backing store)(.+)/ ) { -+ my @backing_stores = $tgtadm_output{$existing_target} =~ m{\s+Backing store: (?!No backing store\n)(.+)}g; -+ return @backing_stores; -+ } -+ return; -+ # Returns account information: -+ } elsif ($task eq "account_information") { -+ if ($tgtadm_output{$existing_target} =~ m{ -+ \s+Account\ information:\n(.*)\n\s+ACL\ information: -+ }xs -+ ) { -+ my @accounts = split(/\n/, $1); -+ my @account_information; -+ foreach my $user (@accounts) { -+ my @var = split(/^\s+/, $user); -+ push(@account_information, $var[1]); -+ } -+ return @account_information; -+ } - # Returns ACL information - } elsif ($task eq "acl_information") { -- while ($tgtadm_output{$existing_target} =~ m{ -+ if ($tgtadm_output{$existing_target} =~ m{ - \s+ACL\ information:\n(.*) -- }xmgs -+ }xs - ) { - my @ini_addresses = split(/\n/, $1); - my @acls; -@@ -487,51 +542,80 @@ sub show_target_info { - } - # Returns sessions - } elsif ($task eq "sessions") { -- my @var = split(/\n/, $tgtadm_output{$existing_target}); -- my @sids; -- foreach my $sid (@var) { -- if ( $sid =~ m/\s+I_T nexus: (.+)/ ) { -- push(@sids, $1); -+ my %sessions; -+ if ($tgtadm_output{$existing_target} =~ m{ -+ \s+I_T\ nexus\ information:\n(.*)LUN\ information: -+ }xs -+ ) { -+ my @var = split(/\n/, $1); -+ my $sid; -+ my $cid; -+ -+ foreach my $line (@var) { -+ if ($line =~ m/\s+I_T nexus:\ (.+)/) { -+ $sid = $1; -+ } else { -+ if ($line =~ m/\s+Connection:\ (.+)/) { -+ $cid = $1; -+ $sessions{$sid} = $cid; -+ } -+ } - } - } -- return @sids; -+ return %sessions; - } +- # If all targets use the same driver, us it only once in the config ++ # If all targets use the same driver, use it only once in the config + my $skip_driver = 0; + my @drivers_combined; + foreach my $current_target (@all_targets) { +@@ -976,63 +1053,43 @@ sub check_connected { } --# Delete the targets which are not in use --sub delete_targets { + # Check if a device can be allocated +-my @rootfs_dev; ++# Device can be used "by system" (i.e. mounted, used as swap, as a part of ++# a RAID array etc.) or "by user" - i.e., already by tgtd, or someone doing: ++# dd if=/dev/1st_device of=/dev/2nd_device ++# We shouldn't allow a device to be used more than one time, as it could ++# cause corruption when written several times. Unless the user really wants to. + sub check_device { +- my $tmp_dev = $_[0]; - -- # Check if the target is used by an initiator -- sub check_in_use { -- my $existing_target = $_[0]; -- my $cur_option = $_[1]; -- my $cur_tid = $_[2]; -- if ($tgtadm_output{$existing_target} =~ m/\s+Connection:/) { -- if ($force == 1) { -- # Remove ACLs first -- my @acl_info = &show_target_info($existing_target, "acl_information"); -- foreach my $acl (@acl_info) { -- execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$existing_target} -I $acl"); -- } -- # Now, remove all sessions / connections from that tid -- my @sessions = &show_target_info($existing_target, "sessions"); -- foreach my $session (@sessions) { -- execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$existing_target} --sid $session --cid 0"); -+# Main subroutine for deleting targets -+sub main_delete { -+ my $current_target = $_[0]; -+ my $current_tid = $_[1]; -+ my $configured = &check_configured($current_target); -+ my $del_upd_text; -+ # Check if the target has initiators connected -+ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) { -+ if ($force == 1) { -+ execute("# Removing target: $current_target"); -+ # Remove ACLs first -+ my @acl_info = &show_target_info($current_target, "acl_information"); -+ foreach my $acl (@acl_info) { -+ execute("tgtadm --op unbind --mode target --tid $tgtadm_output_tid{$current_target} -I $acl"); -+ } -+ # Now, remove all sessions / connections from that tid -+ my %sessions = &show_target_info($current_target, "sessions"); -+ foreach my $sid (keys %sessions) { -+ foreach my $cid ($sessions{$sid}) { -+ execute("tgtadm --op delete --mode conn --tid $tgtadm_output_tid{$current_target} --sid $sid --cid $cid"); - } -- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}"); -- } else { -- execute("# Target with tid $tgtadm_output_tid{$existing_target} ($existing_target) is in use, it won't be deleted."); - } -- } elsif (length $tgtadm_output_tid{$existing_target}) { -- execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$existing_target}"); -+ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}"); - } else { -- if ($cur_option eq "tid") { -- execute("# Target with tid $cur_tid does not exist!"); -+ if ($update ne 0) { -+ $del_upd_text = "updated"; - } else { -- execute("# Target $existing_target does not exist!"); -+ $del_upd_text = "deleted"; - } -+ execute("# Target with tid $tgtadm_output_tid{$current_target} ($current_target) is in use, it won't be $del_upd_text."); -+ return 1; - } -+ } elsif (length $tgtadm_output_tid{$current_target}) { -+ execute("# Removing target: $current_target"); -+ execute("tgtadm --mode target --op delete --tid=$tgtadm_output_tid{$current_target}"); -+ } else { -+ if (length $current_tid) { -+ execute("# Target with tid $current_tid does not exist!"); -+ } else { -+ execute("# Target with name $current_target does not exist!"); -+ } -+ } -+ if ($configured ne 0) { -+ execute(); +- # Check if force flag is set +- if ( $force == 0) { +- # Check for rootfs devices +- &find_rootfs_device(); +- $tmp_dev =~ s/\d//g; +- # Check if device is on the same disk as rootfs +- if (grep {$_ eq $tmp_dev} @rootfs_dev) { +- return (0,$tmp_dev); +- } +- } +- return 1; +-} +- +-# finds all the devices that rootfs is mounted on +-sub find_rootfs_device { +- my @files=("/etc/mtab","/proc/mounts"); +- my @lines; +- # read files +- foreach my $file (@files){ +- if (open(FH,"$file")) { +- @lines=(@lines,); +- close (FH); +- } +- } ++ my $backing_store = $_[0]; ++ my $data_key_ref = $_[1]; + +- # parse files and finds all the device which mounted on / +- foreach my $line (@lines){ +- chomp $line; +- if (($line=~/^\/dev\//) && ($line=~/ \/ /)){ +- my @ln=split(' ',$line); +- $ln[0]=~s/\d//g; +- push(@rootfs_dev,$ln[0]); +- } ++ # If allow-in-use is "yes", there is no need to do ++ # farther tests ++ if ($$data_key_ref{$backing_store}{'allow-in-use'} eq "yes") { ++ return 1; } + +- # read swap file +- my $swap_file="/proc/swap"; +- if (open(FH,"$swap_file")) { +- @lines=; +- close (FH); ++ # Check if the system uses this device ++ use Fcntl qw(O_RDONLY O_EXCL); ++ use Errno; ++ sysopen(FH, $backing_store, O_RDONLY | O_EXCL); ++ if ($!{EBUSY}) { ++ execute("# Device $backing_store is used by the system (mounted, used by swap?)."); ++ return 0; + } +- # parse swap file and finds all the swap devices +- foreach my $line (@lines){ +- chomp $line; +- if ($line=~/^\/dev\//) { +- my @ln=split(' ',$line); +- $ln[0]=~s/\d//g; +- push(@rootfs_dev,$ln[0]); ++ close(FH); ++ ++ # Check if userspace uses this device ++ my $lsof_check = check_exe("lsof"); ++ if ($lsof_check ne 1) { ++ system("lsof $backing_store &>/dev/null"); ++ my $exit_value = $? >> 8; ++ if ($exit_value eq 0) { ++ execute("# Device $backing_store is used (already tgtd target?)."); ++ execute("# Run 'lsof $backing_store' to see the details."); ++ return 0; + } + } +- # remove duplicate entries from @rootfs_dev +- my %seen = (); +- @rootfs_dev = grep { ! $seen{ $_ }++ } @rootfs_dev; ++ return 1; + } + + # Execute or just print (or both) everything we start or would start +diff --git a/usr/Makefile b/usr/Makefile +index 82ddf07..a59364b 100644 +--- a/usr/Makefile ++++ b/usr/Makefile +@@ -58,7 +58,7 @@ PROGRAMS += tgtd tgtadm + SCRIPTS += ../scripts/tgt-setup-lun ../scripts/tgt-admin + TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \ + parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o \ +- bs.o ++ bs_null.o bs.o + MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-admin.8 \ + ../doc/manpages/tgt-setup-lun.8 + +diff --git a/usr/be_byteshift.h b/usr/be_byteshift.h +index 5c6a619..82b7da6 100644 +--- a/usr/be_byteshift.h ++++ b/usr/be_byteshift.h +@@ -40,6 +40,11 @@ static inline uint16_t get_unaligned_be16(const void *p) + return __get_unaligned_be16((const uint8_t *)p); + } + ++static inline uint32_t get_unaligned_be24(const uint8_t *p) ++{ ++ return p[0] << 16 | p[1] << 8 | p[2]; ++} ++ + static inline uint32_t get_unaligned_be32(const void *p) + { + return __get_unaligned_be32((const uint8_t *)p); +@@ -55,6 +60,13 @@ static inline void put_unaligned_be16(uint16_t val, void *p) + __put_unaligned_be16(val, p); + } + ++static inline void put_unaligned_be24(uint32_t val, void *p) ++{ ++ ((uint8_t *)p)[0] = (val >> 16) & 0xff; ++ ((uint8_t *)p)[1] = (val >> 8) & 0xff; ++ ((uint8_t *)p)[2] = val & 0xff; ++} ++ + static inline void put_unaligned_be32(uint32_t val, void *p) + { + __put_unaligned_be32(val, p); +diff --git a/usr/bs.c b/usr/bs.c +index cef7b19..542ef55 100644 +--- a/usr/bs.c ++++ b/usr/bs.c +@@ -173,7 +173,8 @@ static void *bs_thread_worker_fn(void *arg) + return NULL; + } + +-int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn) ++int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn, ++ int nr_threads) + { + int i, ret; + +@@ -205,12 +206,18 @@ int bs_thread_open(struct bs_thread_info *info, request_func_t *rfn) + if (ret) + goto event_del; + +- for (i = 0; i < ARRAY_SIZE(info->worker_thread); i++) { ++ if (nr_threads > ARRAY_SIZE(info->worker_thread)) { ++ eprintf("too many threads %d\n", nr_threads); ++ nr_threads = ARRAY_SIZE(info->worker_thread); ++ } ++ ++ for (i = 0; i < nr_threads; i++) { + ret = pthread_create(&info->worker_thread[i], NULL, + bs_thread_worker_fn, info); + if (ret) + goto destroy_threads; + } ++ + rewrite: + ret = write(info->command_fd[1], &ret, sizeof(ret)); + if (ret < 0) { +@@ -261,7 +268,8 @@ void bs_thread_close(struct bs_thread_info *info) + info->stop = 1; + pthread_cond_broadcast(&info->pending_cond); + +- for (i = 0; i < ARRAY_SIZE(info->worker_thread); i++) ++ for (i = 0; info->worker_thread[i] && ++ i < ARRAY_SIZE(info->worker_thread); i++) + pthread_join(info->worker_thread[i], NULL); + + pthread_cond_destroy(&info->finished_cond); +diff --git a/usr/bs_mmap.c b/usr/bs_mmap.c +index fff19d3..bb24f5e 100644 +--- a/usr/bs_mmap.c ++++ b/usr/bs_mmap.c +@@ -96,7 +96,7 @@ static void bs_mmap_close(struct scsi_lu *lu) + static int bs_mmap_init(struct scsi_lu *lu) + { + struct bs_thread_info *info = BS_THREAD_I(lu); +- return bs_thread_open(info, bs_mmap_request); ++ return bs_thread_open(info, bs_mmap_request, NR_WORKER_THREADS); + } + + static void bs_mmap_exit(struct scsi_lu *lu) +diff --git a/usr/bs_null.c b/usr/bs_null.c +new file mode 100644 +index 0000000..00137ff +--- /dev/null ++++ b/usr/bs_null.c +@@ -0,0 +1,68 @@ ++/* ++ * NULL I/O backing store routine ++ * ++ * Copyright (C) 2008 Alexander Nezhinsky ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * 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 St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "list.h" ++#include "tgtd.h" ++#include "scsi.h" ++ ++#define NULL_BS_DEV_SIZE (1ULL << 40) ++ ++int bs_null_cmd_submit(struct scsi_cmd *cmd) ++{ ++ scsi_set_result(cmd, SAM_STAT_GOOD); + return 0; +} + -+# Delete the targets -+sub delete_targets { ++static int bs_null_open(struct scsi_lu *lu, char *path, ++ int *fd, uint64_t *size) ++{ ++ *size = NULL_BS_DEV_SIZE; ++ dprintf("NULL backing store open, size: %" PRIu64 "\n", *size); ++ return 0; ++} ++ ++static void bs_null_close(struct scsi_lu *lu) ++{ ++} ++ ++static int bs_null_cmd_done(struct scsi_cmd *cmd) ++{ ++ return 0; ++} ++ ++static struct backingstore_template null_bst = { ++ .bs_name = "null", ++ .bs_datasize = 0, ++ .bs_open = bs_null_open, ++ .bs_close = bs_null_close, ++ .bs_cmd_submit = bs_null_cmd_submit, ++ .bs_cmd_done = bs_null_cmd_done, ++}; ++ ++__attribute__((constructor)) static void bs_null_constructor(void) ++{ ++ register_backingstore_template(&null_bst); ++} +diff --git a/usr/bs_rdwr.c b/usr/bs_rdwr.c +index e2ece4a..65a6136 100644 +--- a/usr/bs_rdwr.c ++++ b/usr/bs_rdwr.c +@@ -147,7 +147,7 @@ static int bs_rdwr_init(struct scsi_lu *lu) + { + struct bs_thread_info *info = BS_THREAD_I(lu); - if ($delete eq "help") { - print <poll_sched, iser_sched_poll_cq, dev); ++ + ret = ibv_req_notify_cq(dev->cq, 0); + if (ret) { + eprintf("ibv_req_notify failed: %s\n", strerror(ret)); +@@ -691,6 +700,9 @@ static void iser_accept_connection(struct rdma_cm_event *event) + ci->login_phase = LOGIN_PHASE_START; + INIT_LIST_HEAD(&ci->conn_tx_ready); + list_add(&ci->iser_conn_list, &temp_conn); ++ ++ tgt_init_sched_event(&ci->tx_sched, iser_sched_tx, ci); ++ + /* initiator sits at dst, we are src */ + memcpy(&ci->peer_addr, &event->id->route.addr.dst_addr, + sizeof(ci->peer_addr)); +@@ -940,7 +952,7 @@ static void handle_wc(struct ibv_wc *wc) + list_add(&rdmal->list, &ci->rdmal); + if (waiting_rdma_slot) { + waiting_rdma_slot = 0; +- num_tx_ready = 1; ++ tgt_add_sched_event(&ci->tx_sched); } -- } elsif ($delete =~ m/tid=(.+)/) { -+ } elsif ($delete =~ m/^tid=(.+)/) { - # Delete by tid - &process_targets; -- my $existing_target = $1; -- &check_in_use($tgtadm_output_name{$existing_target}, "tid", $existing_target); -+ my $current_target = $tgtadm_output_name{$1}; -+ &main_delete($current_target, $1); + break; + +@@ -957,7 +969,7 @@ static void handle_wc(struct ibv_wc *wc) + list_add(&rdmal->list, &ci->rdmal); + if (waiting_rdma_slot) { + waiting_rdma_slot = 0; +- num_tx_ready = 1; ++ tgt_add_sched_event(&ci->tx_sched); + } + break; + +@@ -974,85 +986,14 @@ close_err: + } + + /* +- * Called directly from main event loop when a CQ notification is +- * available. +- */ +-static void iser_cqe_handler(int fd __attribute__((unused)), +- int events __attribute__((unused)), +- void *data) +-{ +- int ret; +- void *cq_context; +- struct iser_device *dev = data; +- +- ret = ibv_get_cq_event(dev->cq_channel, &dev->cq, &cq_context); +- if (ret != 0) { +- eprintf("notification, but no CQ event\n"); +- exit(1); +- } +- +- ibv_ack_cq_events(dev->cq, 1); +- +- ret = ibv_req_notify_cq(dev->cq, 0); +- if (ret) { +- eprintf("ibv_req_notify_cq: %s\n", strerror(ret)); +- exit(1); +- } +- +- iser_rx_progress(NULL, dev); +-} +- +-/* +- * Called from tgtd when num_tx_ready (counter) non-zero. Walks the +- * list of active connections and tries to push tx on each, until nothing +- * is ready anymore. No progress limit here. +- */ +-static void iser_tx_progress(int *counter __attribute__((unused)), +- void *data __attribute__((unused))) +-{ +- int reloop, ret; +- struct conn_info *ci, *cin; +- struct iscsi_connection *conn; +- +- dprintf("entry\n"); +- num_tx_ready = 0; +- +- do { +- reloop = 0; +- list_for_each_entry_safe(ci, cin, &conn_tx_ready, +- conn_tx_ready) { +- conn = &ci->iscsi_conn; +- if (conn->state == STATE_CLOSE) { +- dprintf("ignoring tx for closed conn\n"); +- } else { +- dprintf("trying tx\n"); +- ret = iscsi_tx_handler(conn); +- if (conn->state == STATE_CLOSE) { +- conn_close(conn); +- dprintf("connection %p closed\n", ci); +- } else { +- if (ret == 0) { +- reloop = 1; +- } else { +- /* but leave on tx ready list */ +- waiting_rdma_slot = 1; +- } +- } +- } +- } +- } while (reloop); +-} +- +-/* + * Could read as many entries as possible without blocking, but + * that just fills up a list of tasks. Instead pop out of here + * so that tx progress, like issuing rdma reads and writes, can + * happen periodically. + */ +-#define MAX_RX_PROGRESS 8 +-static void iser_rx_progress_one(struct iser_device *dev) ++static int iser_poll_cq(struct iser_device *dev, int max_wc) + { +- int ret, numwc = 0; ++ int ret = 0, numwc = 0; + struct ibv_wc wc; + struct conn_info *ci; + struct recvlist *recvl; +@@ -1069,8 +1010,8 @@ static void iser_rx_progress_one(struct iser_device *dev) + VALGRIND_MAKE_MEM_DEFINED(&wc, sizeof(wc)); + if (wc.status == IBV_WC_SUCCESS) { + handle_wc(&wc); +- if (++numwc == MAX_RX_PROGRESS) { +- num_rx_ready = 1; ++ if (++numwc == max_wc) { ++ ret = 1; + break; + } + } else if (wc.status == IBV_WC_WR_FLUSH_ERR) { +@@ -1089,23 +1030,114 @@ static void iser_rx_progress_one(struct iser_device *dev) + wc.status, (unsigned long long) wc.wr_id); + } + } ++ return ret; ++} ++ ++static void iser_poll_cq_armable(struct iser_device *dev) ++{ ++ int ret; ++ ++ ret = iser_poll_cq(dev, MAX_POLL_WC); ++ if (ret < 0) ++ exit(1); ++ ++ if (ret == 0) { ++ /* no more completions on cq, arm the completion interrupts */ ++ ret = ibv_req_notify_cq(dev->cq, 0); ++ if (ret) { ++ eprintf("ibv_req_notify_cq: %s\n", strerror(ret)); ++ exit(1); ++ } ++ dev->poll_sched.sched_handler = iser_sched_consume_cq; ++ } else ++ dev->poll_sched.sched_handler = iser_sched_poll_cq; ++ ++ tgt_add_sched_event(&dev->poll_sched); ++} ++ ++/* Scheduled to poll cq after a completion event has been ++ received and acknowledged, if no more completions are found ++ the interrupts are re-armed */ ++static void iser_sched_poll_cq(struct tgt_event *tev) ++{ ++ struct iser_device *dev = tev->data; ++ iser_poll_cq_armable(dev); ++} ++ ++/* Scheduled to consume completion events that could arrive ++ after the cq had been seen empty but just before ++ the notification interrupts were re-armed. ++ Intended to consume those remaining completions only, ++ this function does not re-arm interrupts. */ ++static void iser_sched_consume_cq(struct tgt_event *tev) ++{ ++ struct iser_device *dev = tev->data; ++ int ret; ++ ++ ret = iser_poll_cq(dev, MAX_POLL_WC); ++ if (ret < 0) ++ exit(1); ++} ++ ++/* ++ * Called directly from main event loop when a CQ notification is ++ * available. ++ */ ++static void iser_cqe_handler(int fd __attribute__((unused)), ++ int events __attribute__((unused)), ++ void *data) ++{ ++ struct iser_device *dev = data; ++ void *cq_context; ++ int ret; ++ ++ ret = ibv_get_cq_event(dev->cq_channel, &dev->cq, &cq_context); ++ if (ret != 0) { ++ eprintf("notification, but no CQ event\n"); ++ exit(1); ++ } ++ ++ ibv_ack_cq_events(dev->cq, 1); ++ ++ /* if a poll was previosuly scheduled, remove it, ++ as it will be scheduled when necessary */ ++ if (dev->poll_sched.scheduled) ++ tgt_remove_sched_event(&dev->poll_sched); ++ ++ iser_poll_cq_armable(dev); + } + + /* +- * Only one progress counter, must look across all devs. ++ * Called from tgtd as a scheduled event ++ * tries to push tx on a connection, until nothing ++ * is ready anymore. No progress limit here. + */ +-static void iser_rx_progress(int *counter __attribute__((unused)), void *data) ++static void iser_sched_tx(struct tgt_event *evt) + { +- struct iser_device *dev; ++ struct conn_info *ci = evt->data; ++ struct iscsi_connection *conn = &ci->iscsi_conn; ++ int ret; + + dprintf("entry\n"); +- num_rx_ready = 0; +- if (data == NULL) { +- list_for_each_entry(dev, &iser_dev_list, list) +- iser_rx_progress_one(dev); +- } else { +- dev = data; +- iser_rx_progress_one(dev); ++ ++ if (conn->state == STATE_CLOSE) { ++ dprintf("ignoring tx for closed conn\n"); ++ return; ++ } ++ ++ for (;;) { ++ dprintf("trying tx\n"); ++ ret = iscsi_tx_handler(conn); ++ if (conn->state == STATE_CLOSE) { ++ conn_close(conn); ++ dprintf("connection %p closed\n", ci); ++ break; ++ } ++ if (ret != 0) { ++ /* but leave on tx ready list */ ++ waiting_rdma_slot = 1; ++ break; ++ } + } + } + +@@ -1165,10 +1197,7 @@ static int iscsi_rdma_init(void) + INIT_LIST_HEAD(&iser_dev_list); + INIT_LIST_HEAD(&iser_conn_list); + INIT_LIST_HEAD(&temp_conn); +- num_tx_ready = 0; +- num_rx_ready = 0; +- ret = tgt_counter_event_add(&num_tx_ready, iser_tx_progress, NULL); +- ret = tgt_counter_event_add(&num_rx_ready, iser_rx_progress, NULL); ++ + return ret; + } + +@@ -1397,10 +1426,6 @@ static void iscsi_iser_write_end(struct iscsi_connection *conn) + + ci->writeb = 0; /* reset count */ + ci->send_comm_event = NULL; +- +- /* wake up the progress engine to do the done */ +- dprintf("inc progress to finish cmd\n"); +- num_tx_ready = 1; + } + + /* +@@ -1505,7 +1530,7 @@ static int iscsi_rdma_rdma_write(struct iscsi_connection *conn) + iscsi_rdma_event_modify(conn, EPOLLIN); } else { - # Delete by name - &process_targets; -- my $existing_target = $delete; -- &check_in_use($existing_target); -+ my $current_target = $delete; -+ &main_delete($current_target); + /* poke ourselves to do the next rdma */ +- num_tx_ready = 1; ++ tgt_add_sched_event(&ci->tx_sched); } - } --# Some checks --sub check { -+# Update targets -+sub update_targets { -+ if ($update eq "help") { -+ print < update all or selected targets -+ The target will be update only if it's not used -+ (no initiator is connected to it). -+ If you want to update targets which are in use, -+ you have to add "--force" flag. -+ -+Example usage: -+ --update help - display this help -+ --update ALL - update all targets -+ --update tid=4 - update target 4 (target with tid 4) -+ --update iqn.2008-08.com.example:some.target - update this target -+ -+EOF -+ exit; -+ } elsif ($update eq "ALL") { -+ # Run over all targets and delete them if they are not in use -+ &parse_configs; -+ &process_targets; -+# my @all_targets = keys %tgtadm_output_tid; -+ my @targets_combined = &combine_targets; -+ foreach my $current_target (@targets_combined) { -+ my $configured = &check_configured($current_target); -+ my $connected = &check_connected($current_target); -+ my $in_configfile = &check_in_configfile($current_target); -+ &combine_targets; -+ if (($in_configfile == 0) && ($configured == 1)) { -+ # Delete the target if it's not in the config file -+ &main_delete($current_target); -+ } else { -+ &add_targets($current_target, $configured, $connected, $in_configfile); -+ } -+ -+ } -+ } elsif ($update =~ m/^tid=(.+)/) { -+ # Update by tid -+ &parse_configs; -+ &process_targets; -+ my $current_target = $tgtadm_output_name{$1}; -+ my $configured = &check_configured($current_target); -+ my $connected = &check_connected($current_target); -+ my $in_configfile = &check_in_configfile($current_target); -+ if (($in_configfile == 0) && ($configured == 1)) { -+ # Delete the target if it's not in the config file -+ &main_delete($current_target); -+ } elsif ($configured == 1) { -+ &add_targets($current_target, $configured, $connected); -+ } else { -+ print "There is no target with tid $1, can't continue!\n"; -+ exit 1; -+ } -+ } else { -+ # Update by name -+ &parse_configs; -+ &process_targets; -+ my $current_target = $update; -+ my $configured = &check_configured($current_target); -+ my $connected = &check_connected($current_target); -+ my $in_configfile = &check_in_configfile($current_target); -+ if (($in_configfile == 0) && ($configured == 1)) { -+ # Delete the target if it's not in the config file -+ &main_delete($current_target); -+ } else { -+ &add_targets($current_target, $configured, $connected); -+ } -+ } -+} -+ -+# Find the biggest tid -+sub find_max_tid { -+ my @all_targets = keys %tgtadm_output_tid; -+ my $maxtid = 0; -+ foreach my $var (@all_targets) { -+ if ($tgtadm_output_tid{$var} > $maxtid) { -+ $maxtid = $tgtadm_output_tid{$var}; -+ } -+ return $maxtid; -+ } -+} -+ -+# Combine targets from the config file and currently configured targets -+sub combine_targets { -+ my @targets_in_configfile; -+ my @all_targets = keys %tgtadm_output_tid; -+ my @targets_combined; -+ # Make an array of targets in the config file -+ foreach my $k (sort keys %conf) { -+ if ( $k eq "target" ) { -+ foreach my $k2 (sort keys %{$conf{$k}}) { -+ push(@targets_in_configfile, $k2) -+ } -+ } -+ } -+ # Use only unique elements from both arrays -+ foreach my $current_target (@all_targets) { -+ push (@targets_combined, $current_target) unless grep { $_ eq $current_target } @targets_in_configfile; -+ } -+ @targets_combined = (@targets_combined, @targets_in_configfile); -+ return @targets_combined; -+} -+ -+# Check if a value is correct -+sub check_value { - if ( not defined $_[0] or not length $_[0] ) { - print "\nOption $option has a missing value!\n"; - print "Check your config file for errors (target: $target)\n"; -@@ -578,6 +766,42 @@ sub check { - } - } - -+# Check if the target is in the config file -+sub check_in_configfile { -+ my $current_target = $_[0]; -+ my $result; -+ foreach my $k (sort keys %conf) { -+ if ( $k eq "target" ) { -+ foreach my $k2 (sort keys %{$conf{$k}}) { -+ if ($k2 eq $current_target) { -+ return 1; -+ } -+ } -+ # If we're here, we didn't find a match -+ return 0; -+ } -+ } -+} -+ -+# Check if the target is configured in tgtd -+sub check_configured { -+ my $current_target = $_[0]; -+ if (length $tgtadm_output_tid{$current_target}) { -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+# Check if any initiators are connected to the target -+sub check_connected { -+ my $current_target = $_[0]; -+ if ($tgtadm_output{$current_target} =~ m/\s+Connection:/) { -+ return 1; -+ } else { -+ return 0; -+ } -+} - - # Execute or just print (or both) everything we start or would start - sub execute { -@@ -612,6 +836,8 @@ if ($execute == 1) { - &remove_targets; - } elsif ($delete ne 0) { - &delete_targets; -+} elsif ($update ne 0) { -+ &update_targets; - } elsif ($dump == 1) { - &dump_config; - } elsif ($offline ne 0) { -diff --git a/usr/Makefile b/usr/Makefile -index 4245709..82ddf07 100644 ---- a/usr/Makefile -+++ b/usr/Makefile -@@ -55,10 +55,12 @@ CFLAGS += -g -O2 -Wall -Wstrict-prototypes -fPIC - LIBS += -lpthread - - PROGRAMS += tgtd tgtadm --SCRIPTS += ../scripts/tgt-setup-lun -+SCRIPTS += ../scripts/tgt-setup-lun ../scripts/tgt-admin - TGTD_OBJS += tgtd.o mgmt.o target.o scsi.o log.o driver.o util.o work.o \ -- parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o bs.o --MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-setup-lun.8 -+ parser.o spc.o sbc.o mmc.o osd.o scc.o smc.o ssc.o bs_ssc.o \ -+ bs.o -+MANPAGES = ../doc/manpages/tgtadm.8 ../doc/manpages/tgt-admin.8 \ -+ ../doc/manpages/tgt-setup-lun.8 - - TGTD_DEP = $(TGTD_OBJS:.o=.d) - -diff --git a/usr/be_byteshift.h b/usr/be_byteshift.h -new file mode 100644 -index 0000000..5c6a619 ---- /dev/null -+++ b/usr/be_byteshift.h -@@ -0,0 +1,68 @@ -+#ifndef _LINUX_UNALIGNED_BE_BYTESHIFT_H -+#define _LINUX_UNALIGNED_BE_BYTESHIFT_H -+ -+static inline uint16_t __get_unaligned_be16(const uint8_t *p) -+{ -+ return p[0] << 8 | p[1]; -+} -+ -+static inline uint32_t __get_unaligned_be32(const uint8_t *p) -+{ -+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -+} -+ -+static inline uint64_t __get_unaligned_be64(const uint8_t *p) -+{ -+ return (uint64_t)__get_unaligned_be32(p) << 32 | -+ __get_unaligned_be32(p + 4); -+} -+ -+static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) -+{ -+ *p++ = val >> 8; -+ *p++ = val; -+} -+ -+static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) -+{ -+ __put_unaligned_be16(val >> 16, p); -+ __put_unaligned_be16(val, p + 2); -+} -+ -+static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) -+{ -+ __put_unaligned_be32(val >> 32, p); -+ __put_unaligned_be32(val, p + 4); -+} -+ -+static inline uint16_t get_unaligned_be16(const void *p) -+{ -+ return __get_unaligned_be16((const uint8_t *)p); -+} -+ -+static inline uint32_t get_unaligned_be32(const void *p) -+{ -+ return __get_unaligned_be32((const uint8_t *)p); -+} -+ -+static inline uint64_t get_unaligned_be64(const void *p) -+{ -+ return __get_unaligned_be64((const uint8_t *)p); -+} -+ -+static inline void put_unaligned_be16(uint16_t val, void *p) -+{ -+ __put_unaligned_be16(val, p); -+} -+ -+static inline void put_unaligned_be32(uint32_t val, void *p) -+{ -+ __put_unaligned_be32(val, p); -+} -+ -+static inline void put_unaligned_be64(uint64_t val, void *p) -+{ -+ __put_unaligned_be64(val, p); -+} -+ -+#endif /* _LINUX_UNALIGNED_BE_BYTESHIFT_H */ + return ret; +@@ -1628,7 +1653,7 @@ static void iscsi_rdma_event_modify(struct iscsi_connection *conn, int events) + dprintf("tx ready adding %p\n", ci); + list_add(&ci->conn_tx_ready, &conn_tx_ready); + } +- num_tx_ready = 1; ++ tgt_add_sched_event(&ci->tx_sched); + } else { + dprintf("tx ready removing %p\n", ci); + list_del_init(&ci->conn_tx_ready); diff --git a/usr/log.c b/usr/log.c -index 4b71216..076c770 100644 +index 076c770..056314a 100644 --- a/usr/log.c +++ b/usr/log.c -@@ -108,9 +108,6 @@ static int logarea_init (int size) +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -52,29 +53,39 @@ static int logarea_init (int size) + logdbg(stderr,"enter logarea_init\n"); + + if ((shmid = shmget(IPC_PRIVATE, sizeof(struct logarea), +- 0644 | IPC_CREAT | IPC_EXCL)) == -1) ++ 0644 | IPC_CREAT | IPC_EXCL)) == -1) { ++ syslog(LOG_ERR, "shmget logarea failed %d", errno); + return 1; ++ } + + la = shmat(shmid, NULL, 0); +- if (!la) ++ if (!la) { ++ syslog(LOG_ERR, "shmat logarea failed %d", errno); + return 1; ++ } ++ ++ shmctl(shmid, IPC_RMID, NULL); + + if (size < MAX_MSG_SIZE) + size = LOG_SPACE_SIZE; + + if ((shmid = shmget(IPC_PRIVATE, size, + 0644 | IPC_CREAT | IPC_EXCL)) == -1) { ++ syslog(LOG_ERR, "shmget msg failed %d", errno); + shmdt(la); return 1; } -- la->ops[0].sem_num = 0; -- la->ops[0].sem_flg = 0; -- - return 0; - } + la->start = shmat(shmid, NULL, 0); + if (!la->start) { ++ syslog(LOG_ERR, "shmat msg failed %d", errno); + shmdt(la); + return 1; + } + memset(la->start, 0, size); -@@ -237,21 +234,24 @@ static void log_syslog (void * buff) - static void dolog(int prio, const char *fmt, va_list ap) - { - struct timespec ts; -+ struct sembuf ops; ++ shmctl(shmid, IPC_RMID, NULL); ++ + la->empty = 1; + la->end = la->start + size; + la->head = la->start; +@@ -82,18 +93,23 @@ static int logarea_init (int size) - if (la) { - ts.tv_sec = 0; - ts.tv_nsec = 10000; + if ((shmid = shmget(IPC_PRIVATE, MAX_MSG_SIZE + sizeof(struct logmsg), + 0644 | IPC_CREAT | IPC_EXCL)) == -1) { ++ syslog(LOG_ERR, "shmget logmsg failed %d", errno); + shmdt(la->start); + shmdt(la); + return 1; + } + la->buff = shmat(shmid, NULL, 0); + if (!la->buff) { ++ syslog(LOG_ERR, "shmat logmsgfailed %d", errno); + shmdt(la->start); + shmdt(la); + return 1; + } -- la->ops[0].sem_op = -1; -- if (semtimedop(la->semid, la->ops, 1, &ts) < 0) { -+ ops.sem_num = 0; -+ ops.sem_flg = 0; -+ ops.sem_op = -1; -+ if (semtimedop(la->semid, &ops, 1, &ts) < 0) { - syslog(LOG_ERR, "semop up failed"); - return; - } ++ shmctl(shmid, IPC_RMID, NULL); ++ + if ((la->semid = semget(SEMKEY, 1, 0666 | IPC_CREAT)) < 0) { ++ syslog(LOG_ERR, "semget failed %d", errno); + shmdt(la->buff); + shmdt(la->start); + shmdt(la); +@@ -102,6 +118,7 @@ static int logarea_init (int size) - log_enqueue(prio, fmt, ap); - -- la->ops[0].sem_op = 1; -- if (semop(la->semid, la->ops, 1) < 0) { -+ ops.sem_op = 1; -+ if (semop(la->semid, &ops, 1) < 0) { - syslog(LOG_ERR, "semop down failed"); - return; - } -@@ -291,15 +291,21 @@ void log_debug(const char *fmt, ...) - - static void log_flush(void) - { -+ struct sembuf ops; -+ - while (!la->empty) { -- la->ops[0].sem_op = -1; -- if (semop(la->semid, la->ops, 1) < 0) { -+ ops.sem_num = 0; -+ ops.sem_flg = 0; -+ ops.sem_op = -1; -+ if (semop(la->semid, &ops, 1) < 0) { - syslog(LOG_ERR, "semop up failed"); - exit(1); - } -+ - log_dequeue(la->buff); -- la->ops[0].sem_op = 1; -- if (semop(la->semid, la->ops, 1) < 0) { -+ -+ ops.sem_op = 1; -+ if (semop(la->semid, &ops, 1) < 0) { - syslog(LOG_ERR, "semop down failed"); - exit(1); - } -diff --git a/usr/log.h b/usr/log.h -index 6993235..b84f6d6 100644 ---- a/usr/log.h -+++ b/usr/log.h -@@ -55,7 +55,6 @@ struct logarea { - void *start; - void *end; - char *buff; -- struct sembuf ops[1]; - int semid; - union semun semarg; - }; -diff --git a/usr/smc.c b/usr/smc.c -index 9d7f681..ab36e9c 100644 ---- a/usr/smc.c -+++ b/usr/smc.c -@@ -225,6 +225,41 @@ static int build_element_descriptors(uint8_t *data, struct list_head *head, - } - - /** -+ * smc_initialize_element_status with range -+ * - INITIALIZE ELEMENT STATUS WITH RANGE op code -+ * -+ * Support the SCSI op code INITIALIZE_ELEMENT_STATUS_WITH_RANGE -+ * Ref: smc3r11, 6.5 -+ */ -+static int smc_initialize_element_status_range(int host_no, struct scsi_cmd *cmd) -+{ -+ scsi_set_in_resid_by_actual(cmd, 0); -+ -+ if (device_reserved(cmd)) -+ return SAM_STAT_RESERVATION_CONFLICT; -+ else -+ return SAM_STAT_GOOD; -+} -+ -+/** -+ * smc_initialize_element_status - INITIALIZE ELEMENT STATUS op code -+ * -+ * Some backup libraries seem to require this. -+ * -+ * Support the SCSI op code INITIALIZE_ELEMENT_STATUS -+ * Ref: smc3r10a, 6.2 -+ */ -+static int smc_initialize_element_status(int host_no, struct scsi_cmd *cmd) -+{ -+ scsi_set_in_resid_by_actual(cmd, 0); -+ -+ if (device_reserved(cmd)) -+ return SAM_STAT_RESERVATION_CONFLICT; -+ else -+ return SAM_STAT_GOOD; -+} -+ -+/** - * smc_read_element_status - READ ELEMENT STATUS op code - * - * Support the SCSI op code READ ELEMENT STATUS -@@ -748,7 +783,7 @@ struct device_type_template smc_template = { - {spc_illegal_op,}, - {spc_illegal_op,}, - {spc_illegal_op,}, -- {spc_illegal_op,}, -+ {smc_initialize_element_status,}, - - {spc_illegal_op,}, - {spc_illegal_op,}, -@@ -778,7 +813,28 @@ struct device_type_template smc_template = { - {spc_illegal_op,}, - {spc_illegal_op,}, - -- [0x20 ... 0x4f] = {spc_illegal_op,}, -+ [0x20 ... 0x2f] = {spc_illegal_op,}, -+ -+ /* 0x30 */ -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {smc_initialize_element_status_range,}, -+ -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ {spc_illegal_op,}, -+ -+ [0x40 ... 0x4f] = {spc_illegal_op,}, - - /* 0x50 */ - {spc_illegal_op,}, + la->semarg.val=1; + if (semctl(la->semid, 0, SETVAL, la->semarg) < 0) { ++ syslog(LOG_ERR, "semctl failed %d", errno); + shmdt(la->buff); + shmdt(la->start); + shmdt(la); diff --git a/usr/spc.c b/usr/spc.c -index bd2c975..60fd7d7 100644 +index 60fd7d7..ac5c3de 100644 --- a/usr/spc.c +++ b/usr/spc.c -@@ -318,6 +318,16 @@ int spc_test_unit(int host_no, struct scsi_cmd *cmd) - return SAM_STAT_CHECK_CONDITION; +@@ -383,6 +383,9 @@ int spc_mode_select(int host_no, struct scsi_cmd *cmd, + if (block_descriptor_len != BLOCK_DESCRIPTOR_LEN) + goto sense; + ++ memcpy(cmd->dev->mode_block_descriptor, data + offset, ++ BLOCK_DESCRIPTOR_LEN); ++ + offset += 8; + } + +diff --git a/usr/tgtd.c b/usr/tgtd.c +index 0b1cb4c..62aaa04 100644 +--- a/usr/tgtd.c ++++ b/usr/tgtd.c +@@ -38,26 +38,13 @@ + #include "work.h" + #include "util.h" + +-struct tgt_event { +- union { +- event_handler_t *handler; +- counter_event_handler_t *counter_handler; +- }; +- union { +- int fd; +- int *counter; +- }; +- void *data; +- struct list_head e_list; +-}; +- + unsigned long pagesize, pageshift, pagemask; + + int system_active = 1; + static int ep_fd; + static char program_name[] = "tgtd"; + static LIST_HEAD(tgt_events_list); +-static LIST_HEAD(tgt_counter_events_list); ++static LIST_HEAD(tgt_sched_events_list); + + static struct option const long_options[] = + { +@@ -136,22 +123,6 @@ int tgt_event_add(int fd, int events, event_handler_t handler, void *data) + return err; } -+int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd) +-int tgt_counter_event_add(int *counter, counter_event_handler_t handler, +- void *data) +-{ +- struct tgt_event *tev; +- +- tev = zalloc(sizeof(*tev)); +- if (!tev) +- return -ENOMEM; +- +- tev->data = data; +- tev->counter_handler = handler; +- tev->counter = counter; +- list_add(&tev->e_list, &tgt_counter_events_list); +- return 0; +-} +- + static struct tgt_event *tgt_event_lookup(int fd) + { + struct tgt_event *tev; +@@ -163,17 +134,6 @@ static struct tgt_event *tgt_event_lookup(int fd) + return NULL; + } + +-static struct tgt_event *tgt_counter_event_lookup(int *counter) +-{ +- struct tgt_event *tev; +- +- list_for_each_entry(tev, &tgt_counter_events_list, e_list) { +- if (tev->counter == counter) +- return tev; +- } +- return NULL; +-} +- + void tgt_event_del(int fd) + { + struct tgt_event *tev; +@@ -189,20 +149,6 @@ void tgt_event_del(int fd) + free(tev); + } + +-void tgt_counter_event_del(int *counter) +-{ +- struct tgt_event *tev; +- +- tev = tgt_counter_event_lookup(counter); +- if (!tev) { +- eprintf("Cannot find counter event %p\n", counter); +- return; +- } +- +- list_del(&tev->e_list); +- free(tev); +-} +- + int tgt_event_modify(int fd, int events) + { + struct epoll_event ev; +@@ -221,26 +167,62 @@ int tgt_event_modify(int fd, int events) + return epoll_ctl(ep_fd, EPOLL_CTL_MOD, fd, &ev); + } + ++void tgt_init_sched_event(struct tgt_event *evt, ++ sched_event_handler_t sched_handler, void *data) +{ -+ /* TODO: implement properly */ -+ -+ if (device_reserved(cmd)) -+ return SAM_STAT_RESERVATION_CONFLICT; -+ else -+ return SAM_STAT_GOOD; ++ evt->sched_handler = sched_handler; ++ evt->scheduled = 0; ++ evt->data = data; ++ INIT_LIST_HEAD(&evt->e_list); +} + - int spc_mode_select(int host_no, struct scsi_cmd *cmd, - int (*update)(struct scsi_cmd *, uint8_t *, int *)) ++void tgt_add_sched_event(struct tgt_event *evt) ++{ ++ if (!evt->scheduled) { ++ evt->scheduled = 1; ++ list_add_tail(&evt->e_list, &tgt_sched_events_list); ++ } ++} ++ ++void tgt_remove_sched_event(struct tgt_event *evt) ++{ ++ if (evt->scheduled) { ++ evt->scheduled = 0; ++ list_del_init(&evt->e_list); ++ } ++} ++ ++static int tgt_exec_scheduled(void) ++{ ++ struct list_head *last_sched; ++ struct tgt_event *tev, *tevn; ++ int work_remains = 0; ++ ++ if (!list_empty(&tgt_sched_events_list)) { ++ /* execute only work scheduled till now */ ++ last_sched = tgt_sched_events_list.prev; ++ list_for_each_entry_safe(tev, tevn, &tgt_sched_events_list, ++ e_list) { ++ tgt_remove_sched_event(tev); ++ tev->sched_handler(tev); ++ if (&tev->e_list == last_sched) ++ break; ++ } ++ if (!list_empty(&tgt_sched_events_list)) ++ work_remains = 1; ++ } ++ return work_remains; ++} ++ + static void event_loop(void) { -diff --git a/usr/spc.h b/usr/spc.h -index 8fe3e3c..cfc9cf3 100644 ---- a/usr/spc.h -+++ b/usr/spc.h -@@ -8,6 +8,7 @@ extern int spc_report_luns(int host_no, struct scsi_cmd *cmd); - extern int spc_start_stop(int host_no, struct scsi_cmd *cmd); - extern int spc_test_unit(int host_no, struct scsi_cmd *cmd); - extern int spc_request_sense(int host_no, struct scsi_cmd *cmd); -+extern int spc_prevent_allow_media_removal(int host_no, struct scsi_cmd *cmd); - extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd); - extern int spc_lu_init(struct scsi_lu *lu); +- int nevent, i, done, timeout = TGTD_TICK_PERIOD * 1000; ++ int nevent, i, sched_remains, timeout; + struct epoll_event events[1024]; +- struct tgt_event *tev, *tevn; ++ struct tgt_event *tev; -diff --git a/usr/ssc.c b/usr/ssc.c -index 2630a6a..96c3242 100644 ---- a/usr/ssc.c -+++ b/usr/ssc.c -@@ -192,7 +192,7 @@ static struct device_type_template ssc_template = { - {spc_start_stop,}, - {spc_illegal_op,}, - {spc_illegal_op,}, -- {spc_illegal_op,}, -+ {spc_prevent_allow_media_removal,}, - {spc_illegal_op,}, + retry: +- /* +- * Check the counter events to see if they have any work to run. +- */ +- do { +- done = 1; +- list_for_each_entry_safe(tev, tevn, &tgt_counter_events_list, +- e_list) { +- if (*tev->counter) { +- done = 0; +- tev->counter_handler(tev->counter, tev->data); +- } +- } +- } while (!done); ++ sched_remains = tgt_exec_scheduled(); ++ timeout = sched_remains ? 0 : TGTD_TICK_PERIOD * 1000; - /* 0x20 */ -@@ -298,7 +298,7 @@ static struct device_type_template ssc_template = { - {spc_report_luns,}, - {spc_illegal_op,}, - {spc_illegal_op,}, -- {spc_illegal_op,}, -+ {spc_maint_in, maint_in_service_actions,}, - {spc_illegal_op,}, - {spc_illegal_op,}, - {spc_illegal_op,}, -diff --git a/usr/util.h b/usr/util.h -index ac4b380..794c70b 100644 ---- a/usr/util.h -+++ b/usr/util.h -@@ -6,6 +6,7 @@ - #include - #include - #include -+#include "be_byteshift.h" + nevent = epoll_wait(ep_fd, events, ARRAY_SIZE(events), timeout); + if (nevent < 0) { +diff --git a/usr/tgtd.h b/usr/tgtd.h +index 4febcd3..da751c8 100644 +--- a/usr/tgtd.h ++++ b/usr/tgtd.h +@@ -206,13 +206,20 @@ extern int tgt_bind_host_to_target(int tid, int host_no); + extern int tgt_unbind_host_to_target(int tid, int host_no); + extern int tgt_bound_target_lookup(int host_no); - #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) - #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +-typedef void (event_handler_t)(int fd, int events, void *data); +-typedef void (counter_event_handler_t)(int *counter, void *data); ++struct tgt_event; ++typedef void (*sched_event_handler_t)(struct tgt_event *tev); ++ ++extern void tgt_init_sched_event(struct tgt_event *evt, ++ sched_event_handler_t sched_handler, void *data); ++ ++typedef void (*event_handler_t)(int fd, int events, void *data); ++ + extern int tgt_event_add(int fd, int events, event_handler_t handler, void *data); +-extern int tgt_counter_event_add(int *counter, counter_event_handler_t handler, +- void *data); + extern void tgt_event_del(int fd); +-extern void tgt_counter_event_del(int *counter); ++ ++extern void tgt_add_sched_event(struct tgt_event *evt); ++extern void tgt_remove_sched_event(struct tgt_event *evt); ++ + extern int tgt_event_modify(int fd, int events); + extern int target_cmd_queue(int tid, struct scsi_cmd *cmd); + extern void target_cmd_done(struct scsi_cmd *cmd); +@@ -262,4 +269,17 @@ extern int dtd_load_unload(int tid, uint64_t lun, int load, char *file); + extern int register_backingstore_template(struct backingstore_template *bst); + extern struct backingstore_template *get_backingstore_template(const char *name); + ++struct tgt_event { ++ union { ++ event_handler_t handler; ++ sched_event_handler_t sched_handler; ++ }; ++ union { ++ int fd; ++ int scheduled; ++ }; ++ void *data; ++ struct list_head e_list; ++}; ++ + #endif diff --git a/tgt.changes b/tgt.changes index 13511e9..4f784a7 100644 --- a/tgt.changes +++ b/tgt.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Thu Oct 9 13:53:39 CEST 2008 - hare@suse.de + +- Update to latest upstream release 0.9.0 + * tgt-admin fixes + * add null backing store + * Improve iSER scheduling + * Various small fixes + ------------------------------------------------------------------- Mon Sep 15 08:20:24 CEST 2008 - hare@suse.de diff --git a/tgt.spec b/tgt.spec index d1dd0fa..2677dc5 100644 --- a/tgt.spec +++ b/tgt.spec @@ -1,5 +1,5 @@ # -# spec file for package tgt (Version 20080805) +# spec file for package tgt (Version 0.9.0) # # Copyright (c) 2008 SUSE LINUX Products GmbH, Nuernberg, Germany. # @@ -27,14 +27,15 @@ License: GPL v2 or later Group: System/Daemons PreReq: %fillup_prereq %insserv_prereq AutoReqProv: on -Version: 20080805 -Release: 11 +Version: 0.9.0 +Release: 1 Summary: Generic Linux target framework (tgt) -Source: %{name}-%{version}.tar.bz2 +Source: %{name}-20080805.tar.bz2 Source1: %{name}.init Source2: scsi_tgt_if.h Source3: %{name}.services -Patch1: %{name}-git-update +Patch1: %{name}-0.9.0-update +Patch2: %{name}-git-update Patch11: %{name}-fix-build BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -54,8 +55,9 @@ Authors: Mike Christie %prep -%setup -n %{name}-%{version} +%setup -n %{name}-20080805 %patch1 -p1 +%patch2 -p1 %patch11 -p1 cp %{S:2} usr @@ -98,6 +100,12 @@ rm -f filelist %doc %{_mandir}/man8/* %changelog +* Thu Oct 09 2008 hare@suse.de +- Update to latest upstream release 0.9.0 + * tgt-admin fixes + * add null backing store + * Improve iSER scheduling + * Various small fixes * Mon Sep 15 2008 hare@suse.de - Include fixes from upstream: * Add support to update targets