Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:11.4
tgt
tgt-0.9.0-update
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File tgt-0.9.0-update of Package tgt
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=<kernel-src-directory> 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=<kernel-src-directory> 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: + +<target iqn.2008-09.com.example:server.target1> + backing-store /dev/LVM/somedevice +</target> + + +# 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". + +<target iqn.2008-09.com.example:server.target2> + direct-store /dev/sdd + incominguser someuser secretpass12 +</target> + + +# An example with multiple LUNs, disabled write-cache (tgtd enables write-cache +# by default) and vendor identification set to "MyVendor" + +<target iqn.2008-09.com.example:server.target3> + 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. +</target> + + +# 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" + +<target iqn.2008-09.com.example:server.target4> + 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. +</target> + + +# 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. + +<target iqn.2008-09.com.example:server.target4> + 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 +</target> + + +# 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. + +<target iqn.2008-09.com.example:server.target5> + + <direct-store /dev/sdd> + vendor_id VENDOR1 + removable 1 + device-type cd + # lun 1 # Not yet supported + </direct-store> + + <direct-store /dev/sda> + vendor_id VENDOR2 + # lun 2 # Not yet supported + </direct-store> + + <backing-store /dev/sdb1> + vendor_id back1 + scsi_sn SERIAL + write-cache on + # lun 3 # Not yet supported + </backing-store> + + <backing-store /dev/sdd1> + vendor_id back2 + #mode_page 8:0:18:0x10:0:0xff.... + #mode_page 8:0:18:0x10:0:0xff.... + # lun 4 # Not yet supported + </backing-store> + + # 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 + +</target> + + + +# Not supported configurations, and therefore, commented out: + +#<target iqn.2008-09.com.example:server.target6> +# <direct-store /dev/sdd> +# vendor_id VENDOR1 +# </direct-store> +# +# direct-store /dev/sdc +#</target> + +# This one will break the parser: + +#<target iqn.2008-09.com.example:server.target7> +# <direct-store /dev/sdd> +# vendor_id VENDOR1 +# </direct-store> +# +# direct-store /dev/sdc +# +# <direct-store /dev/sdd> +# vendor_id VENDOR1 +# </direct-store> +#</target> 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 <value> put all or selected targets in ready state (see "--ready help" for more info) + --update <value> update configuration for all or selected targets + (see "--update help" for more info) -s, --show show all the targets -c, --conf <conf file> 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 "<target $2>\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 "<target $target_name>\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 "</target>\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 <<EOF; @@ -539,7 +795,7 @@ sub delete_targets { The target will be deleted only if it's not used (no initiator is connected to it). If you want to delete targets which are in use, - you have to add "--force" flag + you have to add "--force" flag. Example usage: --delete help - display this help @@ -550,27 +806,131 @@ Example usage: EOF exit; } elsif ($delete eq "ALL") { - &process_targets; - # Run over all targets and delete them if they are not in use + process_targets; + # Run over all targets and delete them my @all_targets = keys %tgtadm_output_tid; - foreach my $existing_target (@all_targets) { - &check_in_use($existing_target); + foreach my $current_target (@all_targets) { + main_delete($current_target); } - } 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); + process_targets; + my $current_target = $tgtadm_output_name{$1}; + main_delete($current_target, $1); } else { # Delete by name - &process_targets; - my $existing_target = $delete; - &check_in_use($existing_target); + process_targets; + my $current_target = $delete; + main_delete($current_target); } } -# Some checks -sub check { +# Update targets +sub update_targets { + if ($update eq "help") { + print <<EOF; + --update <value> 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,<FH>); + 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=<FH>; + 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 <unistd.h> #include <errno.h> #include <endian.h> +#include "be_byteshift.h" #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor