MFC r268240 (by ken):
Add persistent reservation support to camcontrol(8). camcontrol(8) now supports a new 'persist' subcommand that allows users to issue SCSI PERSISTENT RESERVE IN / OUT commands.
This commit is contained in:
parent
c0be426ce6
commit
2c6230a1ad
@ -3,7 +3,7 @@
|
||||
PROG= camcontrol
|
||||
SRCS= camcontrol.c util.c
|
||||
.if !defined(RELEASE_CRUNCH)
|
||||
SRCS+= fwdownload.c modeedit.c progress.c
|
||||
SRCS+= fwdownload.c modeedit.c persist.c progress.c
|
||||
.else
|
||||
CFLAGS+= -DMINIMALISTIC
|
||||
.endif
|
||||
|
@ -27,7 +27,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 6, 2013
|
||||
.Dd November 20, 2013
|
||||
.Dt CAMCONTROL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -269,6 +269,21 @@
|
||||
.Op Fl U Ar pwd
|
||||
.Op Fl y
|
||||
.Nm
|
||||
.Ic persist
|
||||
.Op device id
|
||||
.Op generic args
|
||||
.Aq Fl i Ar action | Fl o Ar action
|
||||
.Op Fl a
|
||||
.Op Fl I Ar trans_id
|
||||
.Op Fl k Ar key
|
||||
.Op Fl K Ar sa_key
|
||||
.Op Fl p
|
||||
.Op Fl R Ar rel_tgt_port
|
||||
.Op Fl s Ar scope
|
||||
.Op Fl S
|
||||
.Op Fl T Ar res_type
|
||||
.Op Fl U
|
||||
.Nm
|
||||
.Ic help
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
@ -1473,6 +1488,276 @@ to output a line for every firmware segment that is sent to the device by the
|
||||
fwdownload command
|
||||
-- the same as the ones shown in simulation mode.
|
||||
.El
|
||||
.It Ic persist
|
||||
Persistent reservation support.
|
||||
Persistent reservations are a way to reserve a particular
|
||||
.Tn SCSI
|
||||
LUN for use by one or more
|
||||
.Tn SCSI
|
||||
initiators.
|
||||
If the
|
||||
.Fl i
|
||||
option is specified,
|
||||
.Nm
|
||||
will issue the
|
||||
.Tn SCSI
|
||||
PERSISTENT RESERVE IN
|
||||
command using the requested service action.
|
||||
If the
|
||||
.Fl o
|
||||
option is specified,
|
||||
.Nm
|
||||
will issue the
|
||||
.Tn SCSI
|
||||
PERSISTENT RESERVE OUT
|
||||
command using the requested service action.
|
||||
One of those two options is required.
|
||||
.Pp
|
||||
Persistent reservations are complex, and fully explaining them is outside
|
||||
the scope of this manual.
|
||||
Please visit
|
||||
http://www.t10.org
|
||||
and download the latest SPC spec for a full explanation of persistent
|
||||
reservations.
|
||||
.Bl -tag -width 8n
|
||||
.It Fl i Ar mode
|
||||
Specify the service action for the PERSISTENT RESERVE IN command.
|
||||
Supported service actions:
|
||||
.Bl -tag -width 19n
|
||||
.It read_keys
|
||||
Report the current persistent reservation generation (PRgeneration) and any
|
||||
registered keys.
|
||||
.It read_reservation
|
||||
Report the persistent reservation, if any.
|
||||
.It report_capabilities
|
||||
Report the persistent reservation capabilities of the LUN.
|
||||
.It read_full_status
|
||||
Report the full status of persistent reservations on the LUN.
|
||||
.El
|
||||
.It Fl o Ar mode
|
||||
Specify the service action for the PERSISTENT RESERVE OUT command.
|
||||
For service actions like register that are components of other service
|
||||
action names, the entire name must be specified.
|
||||
Otherwise, enough of the service action name must be specified to
|
||||
distinguish it from other possible service actions.
|
||||
Supported service actions:
|
||||
.Bl -tag -width 15n
|
||||
.It register
|
||||
Register a reservation key with the LUN or unregister a reservation key.
|
||||
To register a key, specify the requested key as the Service Action
|
||||
Reservation Key.
|
||||
To unregister a key, specify the previously registered key as the
|
||||
Reservation Key.
|
||||
To change a key, specify the old key as the Reservation Key and the new
|
||||
key as the Service Action Reservation Key.
|
||||
.It register_ignore
|
||||
This is similar to the register subcommand, except that the Reservation Key
|
||||
is ignored.
|
||||
The Service Action Reservation Key will overwrite any previous key
|
||||
registered for the initiator.
|
||||
.It reserve
|
||||
Create a reservation.
|
||||
A key must be registered with the LUN before the LUN can be reserved, and
|
||||
it must be specified as the Reservation Key.
|
||||
The type of reservation must also be specified.
|
||||
The scope defaults to LUN scope (LU_SCOPE), but may be changed.
|
||||
.It release
|
||||
Release a reservation.
|
||||
The Reservation Key must be specified.
|
||||
.It clear
|
||||
Release a reservation and remove all keys from the device.
|
||||
The Reservation Key must be specified.
|
||||
.It preempt
|
||||
Remove a reservation belonging to another initiator.
|
||||
The Reservation Key must be specified.
|
||||
The Service Action Reservation Key may be specified, depending on the
|
||||
operation being performed.
|
||||
.It preempt_abort
|
||||
Remove a reservation belonging to another initiator and abort all
|
||||
outstanding commands from that initiator.
|
||||
The Reservation Key must be specified.
|
||||
The Service Action Reservation Key may be specified, depending on the
|
||||
operation being performed.
|
||||
.It register_move
|
||||
Register another initiator with the LUN, and establish a reservation on the
|
||||
LUN for that initiator.
|
||||
The Reservation Key and Service Action Reservation Key must be specified.
|
||||
.It replace_lost
|
||||
Replace Lost Reservation information.
|
||||
.El
|
||||
.It Fl a
|
||||
Set the All Target Ports (ALL_TG_PT) bit.
|
||||
This requests that the key registration be applied to all target ports and
|
||||
not just the particular target port that receives the command.
|
||||
This only applies to the register and register_ignore actions.
|
||||
.It Fl I Ar tid
|
||||
Specify a Transport ID.
|
||||
This only applies to the Register and Register and Move service actions for
|
||||
Persistent Reserve Out.
|
||||
Multiple Transport IDs may be specified with multiple
|
||||
.Fl I
|
||||
arguments.
|
||||
With the Register service action, specifying one or more Transport IDs
|
||||
implicitly enables the
|
||||
.Fl S
|
||||
option which turns on the SPEC_I_PT bit.
|
||||
Transport IDs generally have the format protocol,id.
|
||||
.Bl -tag -width 5n
|
||||
.It SAS
|
||||
A SAS Transport ID consists of
|
||||
.Dq sas,
|
||||
followed by a 64-bit SAS address.
|
||||
For example:
|
||||
.Pp
|
||||
.Dl sas,0x1234567812345678
|
||||
.It FC
|
||||
A Fibre Channel Transport ID consists of
|
||||
.Dq fcp,
|
||||
followed by a 64-bit Fibre Channel World Wide Name.
|
||||
For example:
|
||||
.Pp
|
||||
.Dl fcp,0x1234567812345678
|
||||
.It SPI
|
||||
A Parallel SCSI address consists of
|
||||
.Dq spi,
|
||||
followed by a SCSI target ID and a relative target port identifier.
|
||||
For example:
|
||||
.Pp
|
||||
.Dl spi,4,1
|
||||
.It 1394
|
||||
An IEEE 1394 (Firewire) Transport ID consists of
|
||||
.Dq sbp,
|
||||
followed by a 64-bit EUI-64 IEEE 1394 node unique identifier.
|
||||
For example:
|
||||
.Pp
|
||||
.Dl sbp,0x1234567812345678
|
||||
.It RDMA
|
||||
A SCSI over RDMA Transport ID consists of
|
||||
.Dq srp,
|
||||
followed by a 128-bit RDMA initiator port identifier.
|
||||
The port identifier must be exactly 32 or 34 (if the leading 0x is
|
||||
included) hexadecimal digits.
|
||||
Only hexadecimal (base 16) numbers are supported.
|
||||
For example:
|
||||
.Pp
|
||||
.Dl srp,0x12345678123456781234567812345678
|
||||
.It iSCSI
|
||||
An iSCSI Transport ID consists an iSCSI name and optionally a separator and
|
||||
iSCSI session ID.
|
||||
For example, if only the iSCSI name is specified:
|
||||
.Pp
|
||||
.Dl iqn.2012-06.com.example:target0
|
||||
.Pp
|
||||
If the iSCSI separator and initiator session ID are specified:
|
||||
.Pp
|
||||
.Dl iqn.2012-06.com.example:target0,i,0x123
|
||||
.It PCIe
|
||||
A SCSI over PCIe Transport ID consists of
|
||||
.Dq sop,
|
||||
followed by a PCIe Routing ID.
|
||||
The Routing ID consists of a bus, device and function or in the alternate
|
||||
form, a bus and function.
|
||||
The bus must be in the range of 0 to 255 inclusive and the device must be
|
||||
in the range of 0 to 31 inclusive.
|
||||
The function must be in the range of 0 to 7 inclusive if the standard form
|
||||
is used, and in the range of 0 to 255 inclusive if the alternate form is
|
||||
used.
|
||||
For example, if a bus, device and function are specified for the standard
|
||||
Routing ID form:
|
||||
.Pp
|
||||
.Dl sop,4,5,1
|
||||
.Pp
|
||||
If the alternate Routing ID form is used:
|
||||
.Pp
|
||||
.Dl sop,4,1
|
||||
.El
|
||||
.It Fl k Ar key
|
||||
Specify the Reservation Key.
|
||||
This may be in decimal, octal or hexadecimal format.
|
||||
The value is zero by default if not otherwise specified.
|
||||
The value must be between 0 and 2^64 - 1, inclusive.
|
||||
.It Fl K Ar key
|
||||
Specify the Service Action Reservation Key.
|
||||
This may be in decimal, octal or hexadecimal format.
|
||||
The value is zero by default if not otherwise specified.
|
||||
The value must be between 0 and 2^64 - 1, inclusive.
|
||||
.It Fl p
|
||||
Enable the Activate Persist Through Power Loss bit.
|
||||
This is only used for the register and register_ignore actions.
|
||||
This requests that the reservation persist across power loss events.
|
||||
.It Fl s Ar scope
|
||||
Specify the scope of the reservation.
|
||||
The scope may be specified by name or by number.
|
||||
The scope is ignored for register, register_ignore and clear.
|
||||
If the desired scope isn't available by name, you may specify the number.
|
||||
.Bl -tag -width 7n
|
||||
.It lun
|
||||
LUN scope (0x00).
|
||||
This encompasses the entire LUN.
|
||||
.It extent
|
||||
Extent scope (0x01).
|
||||
.It element
|
||||
Element scope (0x02).
|
||||
.El
|
||||
.It Fl R Ar rtp
|
||||
Specify the Relative Target Port.
|
||||
This only applies to the Register and Move service action of the Persistent
|
||||
Reserve Out command.
|
||||
.It Fl S
|
||||
Enable the SPEC_I_PT bit.
|
||||
This only applies to the Register service action of Persistent Reserve Out.
|
||||
You must also specify at least one Transport ID with
|
||||
.Fl I
|
||||
if this option is set.
|
||||
If you specify a Transport ID, this option is automatically set.
|
||||
It is an error to specify this option for any service action other than
|
||||
Register.
|
||||
.It Fl T Ar type
|
||||
Specify the reservation type.
|
||||
The reservation type may be specified by name or by number.
|
||||
If the desired reservation type isn't available by name, you may specify
|
||||
the number.
|
||||
Supported reservation type names:
|
||||
.Bl -tag -width 11n
|
||||
.It read_shared
|
||||
Read Shared mode.
|
||||
.It wr_ex
|
||||
Write Exclusive mode.
|
||||
May also be specified as
|
||||
.Dq write_exclusive .
|
||||
.It rd_ex
|
||||
Read Exclusive mode.
|
||||
May also be specified as
|
||||
.Dq read_exclusive .
|
||||
.It ex_ac
|
||||
Exclusive access mode.
|
||||
May also be specified as
|
||||
.Dq exclusive_access .
|
||||
.It wr_ex_ro
|
||||
Write Exclusive Registrants Only mode.
|
||||
May also be specified as
|
||||
.Dq write_exclusive_reg_only .
|
||||
.It ex_ac_ro
|
||||
Exclusive Access Registrants Only mode.
|
||||
May also be specified as
|
||||
.Dq exclusive_access_reg_only .
|
||||
.It wr_ex_ar
|
||||
Write Exclusive All Registrants mode.
|
||||
May also be specified as
|
||||
.Dq write_exclusive_all_regs .
|
||||
.It ex_ac_ar
|
||||
Exclusive Access All Registrants mode.
|
||||
May also be specified as
|
||||
.Dq exclusive_access_all_regs .
|
||||
.El
|
||||
.It Fl U
|
||||
Specify that the target should unregister the initiator that sent
|
||||
the Register and Move request.
|
||||
By default, the target will not unregister the initiator that sends the
|
||||
Register and Move request.
|
||||
This option only applies to the Register and Move service action of the
|
||||
Persistent Reserve Out command.
|
||||
.El
|
||||
.It Ic help
|
||||
Print out verbose usage information.
|
||||
.El
|
||||
@ -1639,6 +1924,66 @@ power-on or hardware reset!
|
||||
.Pp
|
||||
.Em DO NOT
|
||||
use this on a device which has an active filesystem!
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -i read_keys
|
||||
.Ed
|
||||
.Pp
|
||||
This will read any persistent reservation keys registered with da0, and
|
||||
display any errors encountered when sending the PERSISTENT RESERVE IN
|
||||
.Tn SCSI
|
||||
command.
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -o register -a -K 0x12345678
|
||||
.Ed
|
||||
.Pp
|
||||
This will register the persistent reservation key 0x12345678 with da0,
|
||||
apply that registration to all ports on da0, and display any errors that
|
||||
occur when sending the PERSISTENT RESERVE OUT command.
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -o reserve -s lun -k 0x12345678 -T ex_ac
|
||||
.Ed
|
||||
.Pp
|
||||
This will reserve da0 for the exlusive use of the initiator issuing the
|
||||
command.
|
||||
The scope of the reservation is the entire LUN.
|
||||
Any errors sending the PERSISTENT RESERVE OUT command will be displayed.
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -i read_full
|
||||
.Ed
|
||||
.Pp
|
||||
This will display the full status of all reservations on da0 and print out
|
||||
status if there are any errors.
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -o release -k 0x12345678 -T ex_ac
|
||||
.Ed
|
||||
.Pp
|
||||
This will release a reservation on da0 of the type ex_ac
|
||||
(Exclusive Access).
|
||||
The Reservation Key for this registration is 0x12345678.
|
||||
Any errors that occur will be displayed.
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -o register -K 0x12345678 -S \e
|
||||
-I sas,0x1234567812345678 -I sas,0x8765432187654321
|
||||
.Ed
|
||||
.Pp
|
||||
This will register the key 0x12345678 with da0, specifying that it applies
|
||||
to the SAS initiators with SAS addresses 0x1234567812345678 and
|
||||
0x8765432187654321.
|
||||
.Bd -literal -offset indent
|
||||
camcontrol persist da0 -v -o register_move -k 0x87654321 \e
|
||||
-K 0x12345678 -U -p -R 2 -I fcp,0x1234567812345678
|
||||
.Ed
|
||||
.Pp
|
||||
This will move the registration from the current initiator, whose
|
||||
Registration Key is 0x87654321, to the Fibre Channel initiator with the
|
||||
Fiber Channel World Wide Node Name 0x1234567812345678.
|
||||
A new registration key, 0x12345678, will be registered for the initiator
|
||||
with the Fibre Channel World Wide Node Name 0x1234567812345678, and the
|
||||
current initiator will be unregistered from the target.
|
||||
The reservation will be moved to relative target port 2 on the target
|
||||
device.
|
||||
The registration will persist across power losses.
|
||||
.Sh SEE ALSO
|
||||
.Xr cam 3 ,
|
||||
.Xr cam_cdbparse 3 ,
|
||||
|
@ -96,6 +96,7 @@ typedef enum {
|
||||
CAM_CMD_SECURITY = 0x0000001d,
|
||||
CAM_CMD_HPA = 0x0000001e,
|
||||
CAM_CMD_SANITIZE = 0x0000001f,
|
||||
CAM_CMD_PERSIST = 0x00000020
|
||||
} cam_cmdmask;
|
||||
|
||||
typedef enum {
|
||||
@ -218,6 +219,7 @@ static struct camcontrol_opts option_table[] = {
|
||||
{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
|
||||
{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
|
||||
{"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"},
|
||||
{"persist", CAM_CMD_PERSIST, CAM_ARG_NONE, "ai:I:k:K:o:ps:ST:U"},
|
||||
#endif /* MINIMALISTIC */
|
||||
{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
|
||||
{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
|
||||
@ -225,12 +227,6 @@ static struct camcontrol_opts option_table[] = {
|
||||
{NULL, 0, 0, NULL}
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CC_OR_NOT_FOUND,
|
||||
CC_OR_AMBIGUOUS,
|
||||
CC_OR_FOUND
|
||||
} camcontrol_optret;
|
||||
|
||||
struct cam_devitem {
|
||||
struct device_match_result dev_match;
|
||||
int num_periphs;
|
||||
@ -7826,6 +7822,9 @@ usage(int printlong)
|
||||
" [-U <user|master>] [-y]\n"
|
||||
" camcontrol hpa [dev_id][generic args] [-f] [-l] [-P] [-p pwd]\n"
|
||||
" [-q] [-s max_sectors] [-U pwd] [-y]\n"
|
||||
" camcontrol persist [dev_id][generic args] <-i action|-o action>\n"
|
||||
" [-a][-I tid][-k key][-K sa_key][-p][-R rtp]\n"
|
||||
" [-s scope][-S][-T type][-U]\n"
|
||||
#endif /* MINIMALISTIC */
|
||||
" camcontrol help\n");
|
||||
if (!printlong)
|
||||
@ -7862,8 +7861,9 @@ usage(int printlong)
|
||||
"idle send the ATA IDLE command to the named device\n"
|
||||
"standby send the ATA STANDBY command to the named device\n"
|
||||
"sleep send the ATA SLEEP command to the named device\n"
|
||||
"fwdownload program firmware of the named device with the given image"
|
||||
"fwdownload program firmware of the named device with the given image\n"
|
||||
"security report or send ATA security commands to the named device\n"
|
||||
"persist send the SCSI PERSISTENT RESERVE IN or OUT commands\n"
|
||||
"help this message\n"
|
||||
"Device Identifiers:\n"
|
||||
"bus:target specify the bus and target, lun defaults to 0\n"
|
||||
@ -7998,6 +7998,22 @@ usage(int printlong)
|
||||
" device\n"
|
||||
"-U pwd unlock the HPA configuration of the device\n"
|
||||
"-y don't ask any questions\n"
|
||||
"persist arguments:\n"
|
||||
"-i action specify read_keys, read_reservation, report_cap, or\n"
|
||||
" read_full_status\n"
|
||||
"-o action specify register, register_ignore, reserve, release,\n"
|
||||
" clear, preempt, preempt_abort, register_move, replace_lost\n"
|
||||
"-a set the All Target Ports (ALL_TG_PT) bit\n"
|
||||
"-I tid specify a Transport ID, e.g.: sas,0x1234567812345678\n"
|
||||
"-k key specify the Reservation Key\n"
|
||||
"-K sa_key specify the Service Action Reservation Key\n"
|
||||
"-p set the Activate Persist Through Power Loss bit\n"
|
||||
"-R rtp specify the Relative Target Port\n"
|
||||
"-s scope specify the scope: lun, extent, element or a number\n"
|
||||
"-S specify Transport ID for register, requires -I\n"
|
||||
"-T res_type specify the reservation type: read_shared, wr_ex, rd_ex,\n"
|
||||
" ex_ac, wr_ex_ro, ex_ac_ro, wr_ex_ar, ex_ac_ar\n"
|
||||
"-U unregister the current initiator for register_move\n"
|
||||
);
|
||||
#endif /* MINIMALISTIC */
|
||||
}
|
||||
@ -8332,6 +8348,11 @@ main(int argc, char **argv)
|
||||
error = scsisanitize(cam_dev, argc, argv,
|
||||
combinedopt, retry_count, timeout);
|
||||
break;
|
||||
case CAM_CMD_PERSIST:
|
||||
error = scsipersist(cam_dev, argc, argv, combinedopt,
|
||||
retry_count, timeout, arglist & CAM_ARG_VERBOSE,
|
||||
arglist & CAM_ARG_ERR_RECOVER);
|
||||
break;
|
||||
#endif /* MINIMALISTIC */
|
||||
case CAM_CMD_USAGE:
|
||||
usage(1);
|
||||
|
@ -30,6 +30,13 @@
|
||||
|
||||
#ifndef _CAMCONTROL_H
|
||||
#define _CAMCONTROL_H
|
||||
|
||||
typedef enum {
|
||||
CC_OR_NOT_FOUND,
|
||||
CC_OR_AMBIGUOUS,
|
||||
CC_OR_FOUND
|
||||
} camcontrol_optret;
|
||||
|
||||
/*
|
||||
* get_hook: Structure for evaluating args in a callback.
|
||||
*/
|
||||
@ -56,6 +63,9 @@ void mode_list(struct cam_device *device, int page_control, int dbd,
|
||||
int retry_count, int timeout);
|
||||
int scsidoinquiry(struct cam_device *device, int argc, char **argv,
|
||||
char *combinedopt, int retry_count, int timeout);
|
||||
int scsipersist(struct cam_device *device, int argc, char **argv,
|
||||
char *combinedopt, int retry_count, int timeout, int verbose,
|
||||
int err_recover);
|
||||
char *cget(void *hook, char *name);
|
||||
int iget(void *hook, char *name);
|
||||
void arg_put(void *hook, int letter, void *arg, int count, char *name);
|
||||
|
966
sbin/camcontrol/persist.c
Normal file
966
sbin/camcontrol/persist.c
Normal file
@ -0,0 +1,966 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Spectra Logic Corporation
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
||||
* substantially similar to the "NO WARRANTY" disclaimer below
|
||||
* ("Disclaimer") and any redistribution must be conditioned upon
|
||||
* including a substantially similar Disclaimer requirement for further
|
||||
* binary redistribution.
|
||||
*
|
||||
* NO WARRANTY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGES.
|
||||
*
|
||||
* Authors: Ken Merry (Spectra Logic Corporation)
|
||||
*/
|
||||
/*
|
||||
* SCSI Persistent Reservation support for camcontrol(8).
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <err.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_debug.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/scsi/scsi_all.h>
|
||||
#include <cam/scsi/scsi_pass.h>
|
||||
#include <cam/scsi/scsi_message.h>
|
||||
#include <camlib.h>
|
||||
#include "camcontrol.h"
|
||||
|
||||
struct persist_transport_id {
|
||||
struct scsi_transportid_header *hdr;
|
||||
unsigned int alloc_len;
|
||||
STAILQ_ENTRY(persist_transport_id) links;
|
||||
};
|
||||
|
||||
/*
|
||||
* Service Actions for PERSISTENT RESERVE IN.
|
||||
*/
|
||||
static struct scsi_nv persist_in_actions[] = {
|
||||
{ "read_keys", SPRI_RK },
|
||||
{ "read_reservation", SPRI_RR },
|
||||
{ "report_capabilities", SPRI_RC },
|
||||
{ "read_full_status", SPRI_RS }
|
||||
};
|
||||
|
||||
/*
|
||||
* Service Actions for PERSISTENT RESERVE OUT.
|
||||
*/
|
||||
static struct scsi_nv persist_out_actions[] = {
|
||||
{ "register", SPRO_REGISTER },
|
||||
{ "reserve", SPRO_RESERVE },
|
||||
{ "release" , SPRO_RELEASE },
|
||||
{ "clear", SPRO_CLEAR },
|
||||
{ "preempt", SPRO_PREEMPT },
|
||||
{ "preempt_abort", SPRO_PRE_ABO },
|
||||
{ "register_ignore", SPRO_REG_IGNO },
|
||||
{ "register_move", SPRO_REG_MOVE },
|
||||
{ "replace_lost", SPRO_REPL_LOST_RES }
|
||||
};
|
||||
|
||||
/*
|
||||
* Known reservation scopes. As of SPC-4, only LU_SCOPE is used in the
|
||||
* spec. The others are obsolete.
|
||||
*/
|
||||
static struct scsi_nv persist_scope_table[] = {
|
||||
{ "lun", SPR_LU_SCOPE },
|
||||
{ "extent", SPR_EXTENT_SCOPE },
|
||||
{ "element", SPR_ELEMENT_SCOPE }
|
||||
};
|
||||
|
||||
/*
|
||||
* Reservation types. The longer name for a given reservation type is
|
||||
* listed first, so that it makes more sense when we print out the
|
||||
* reservation type. We step through the table linearly when looking for
|
||||
* the text name for a particular numeric reservation type value.
|
||||
*/
|
||||
static struct scsi_nv persist_type_table[] = {
|
||||
{ "read_shared", SPR_TYPE_RD_SHARED },
|
||||
{ "write_exclusive", SPR_TYPE_WR_EX },
|
||||
{ "wr_ex", SPR_TYPE_WR_EX },
|
||||
{ "read_exclusive", SPR_TYPE_RD_EX },
|
||||
{ "rd_ex", SPR_TYPE_RD_EX },
|
||||
{ "exclusive_access", SPR_TYPE_EX_AC },
|
||||
{ "ex_ac", SPR_TYPE_EX_AC },
|
||||
{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
|
||||
{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
|
||||
{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
|
||||
{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
|
||||
{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
|
||||
{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
|
||||
{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
|
||||
{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
|
||||
};
|
||||
|
||||
/*
|
||||
* Print out the standard scope/type field.
|
||||
*/
|
||||
static void
|
||||
persist_print_scopetype(uint8_t scopetype)
|
||||
{
|
||||
const char *tmpstr;
|
||||
int num_entries;
|
||||
|
||||
num_entries = sizeof(persist_scope_table) /
|
||||
sizeof(persist_scope_table[0]);
|
||||
tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
|
||||
scopetype & SPR_SCOPE_MASK);
|
||||
fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
|
||||
"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
|
||||
|
||||
num_entries = sizeof(persist_type_table) /
|
||||
sizeof(persist_type_table[0]);
|
||||
tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
|
||||
scopetype & SPR_TYPE_MASK);
|
||||
fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
|
||||
"Unknown", scopetype & SPR_TYPE_MASK);
|
||||
}
|
||||
|
||||
static void
|
||||
persist_print_transportid(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
struct sbuf *sb;
|
||||
|
||||
sb = sbuf_new_auto();
|
||||
if (sb == NULL)
|
||||
fprintf(stderr, "Unable to allocate sbuf\n");
|
||||
|
||||
scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
|
||||
|
||||
sbuf_finish(sb);
|
||||
|
||||
fprintf(stdout, "%s\n", sbuf_data(sb));
|
||||
|
||||
sbuf_delete(sb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out a persistent reservation. This is used with the READ
|
||||
* RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
|
||||
*/
|
||||
static void
|
||||
persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
|
||||
{
|
||||
uint32_t length;
|
||||
struct scsi_per_res_in_rsrv *res;
|
||||
|
||||
length = scsi_4btoul(hdr->length);
|
||||
length = MIN(length, valid_len);
|
||||
|
||||
res = (struct scsi_per_res_in_rsrv *)hdr;
|
||||
|
||||
if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
|
||||
if (length == 0)
|
||||
fprintf(stdout, "No reservations.\n");
|
||||
else
|
||||
warnx("unable to print reservation, only got %u "
|
||||
"valid bytes", length);
|
||||
return;
|
||||
}
|
||||
fprintf(stdout, "PRgeneration: %#x\n",
|
||||
scsi_4btoul(res->header.generation));
|
||||
fprintf(stdout, "Reservation Key: %#jx\n",
|
||||
(uintmax_t)scsi_8btou64(res->data.reservation));
|
||||
fprintf(stdout, "Scope address: %#x\n",
|
||||
scsi_4btoul(res->data.scope_addr));
|
||||
|
||||
persist_print_scopetype(res->data.scopetype);
|
||||
|
||||
fprintf(stdout, "Extent length: %u\n",
|
||||
scsi_2btoul(res->data.extent_length));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out persistent reservation keys. This is used with the READ KEYS
|
||||
* service action of the PERSISTENT RESERVE IN command.
|
||||
*/
|
||||
static void
|
||||
persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
|
||||
{
|
||||
uint32_t length, num_keys, i;
|
||||
struct scsi_per_res_key *key;
|
||||
|
||||
length = scsi_4btoul(hdr->length);
|
||||
length = MIN(length, valid_len);
|
||||
|
||||
num_keys = length / sizeof(*key);
|
||||
|
||||
fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
|
||||
fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
|
||||
(num_keys == 0) ? "." : ":");
|
||||
|
||||
for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
|
||||
i++, key++) {
|
||||
fprintf(stdout, "%u: %#jx\n", i,
|
||||
(uintmax_t)scsi_8btou64(key->key));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print out persistent reservation capabilities. This is used with the
|
||||
* REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
|
||||
*/
|
||||
static void
|
||||
persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
|
||||
{
|
||||
uint32_t length;
|
||||
int check_type_mask = 0;
|
||||
|
||||
length = scsi_2btoul(cap->length);
|
||||
length = MIN(length, valid_len);
|
||||
|
||||
if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
|
||||
fprintf(stdout, "Insufficient data (%u bytes) to report "
|
||||
"full capabilities\n", length);
|
||||
return;
|
||||
}
|
||||
if (length >= __offsetof(struct scsi_per_res_cap, reserved))
|
||||
check_type_mask = 1;
|
||||
|
||||
fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
|
||||
(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
|
||||
fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
|
||||
(cap->flags1 & SPRI_CRH) ? 1 : 0);
|
||||
fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
|
||||
(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
|
||||
fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
|
||||
(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
|
||||
fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
|
||||
(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
|
||||
fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
|
||||
(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
|
||||
/*
|
||||
* These cases are cut-and-pasted from SPC4r36l. There is no
|
||||
* succinct way to describe these otherwise, and even with the
|
||||
* verbose description, the user will probably have to refer to
|
||||
* the spec to fully understand what is going on.
|
||||
*/
|
||||
switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
|
||||
case SPRI_ALLOW_1:
|
||||
fprintf(stdout,
|
||||
" The device server allows the TEST UNIT READY command through Write\n"
|
||||
" Exclusive type reservations and Exclusive Access type reservations\n"
|
||||
" and does not provide information about whether the following commands\n"
|
||||
" are allowed through Write Exclusive type reservations:\n"
|
||||
" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
|
||||
" command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
|
||||
" RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
|
||||
" and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
|
||||
" b) the READ DEFECT DATA command (see SBC-3).\n");
|
||||
break;
|
||||
case SPRI_ALLOW_2:
|
||||
fprintf(stdout,
|
||||
" The device server allows the TEST UNIT READY command through Write\n"
|
||||
" Exclusive type reservations and Exclusive Access type reservations\n"
|
||||
" and does not allow the following commands through Write Exclusive type\n"
|
||||
" reservations:\n"
|
||||
" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
|
||||
" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
|
||||
" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
|
||||
" FUNCTION command; and\n"
|
||||
" b) the READ DEFECT DATA command.\n"
|
||||
" The device server does not allow the RECEIVE COPY RESULTS command\n"
|
||||
" through Write Exclusive type reservations or Exclusive Access type\n"
|
||||
" reservations.\n");
|
||||
break;
|
||||
case SPRI_ALLOW_3:
|
||||
fprintf(stdout,
|
||||
" The device server allows the TEST UNIT READY command through Write\n"
|
||||
" Exclusive type reservations and Exclusive Access type reservations\n"
|
||||
" and allows the following commands through Write Exclusive type\n"
|
||||
" reservations:\n"
|
||||
" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
|
||||
" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
|
||||
" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
|
||||
" FUNCTION command; and\n"
|
||||
" b) the READ DEFECT DATA command.\n"
|
||||
" The device server does not allow the RECEIVE COPY RESULTS command\n"
|
||||
" through Write Exclusive type reservations or Exclusive Access type\n"
|
||||
" reservations.\n");
|
||||
break;
|
||||
case SPRI_ALLOW_4:
|
||||
fprintf(stdout,
|
||||
" The device server allows the TEST UNIT READY command and the RECEIVE\n"
|
||||
" COPY RESULTS command through Write Exclusive type reservations and\n"
|
||||
" Exclusive Access type reservations and allows the following commands\n"
|
||||
" through Write Exclusive type reservations:\n"
|
||||
" a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
|
||||
" command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
|
||||
" OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
|
||||
" FUNCTION command; and\n"
|
||||
" b) the READ DEFECT DATA command.\n");
|
||||
break;
|
||||
case SPRI_ALLOW_NA:
|
||||
fprintf(stdout,
|
||||
" No information is provided about whether certain commands are allowed\n"
|
||||
" through certain types of persistent reservations.\n");
|
||||
break;
|
||||
default:
|
||||
fprintf(stdout,
|
||||
" Unknown ALLOW COMMANDS value %#x\n",
|
||||
(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
|
||||
SPRI_ALLOW_CMD_SHIFT);
|
||||
break;
|
||||
}
|
||||
fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
|
||||
(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
|
||||
if ((check_type_mask != 0)
|
||||
&& (cap->flags2 & SPRI_TMV)) {
|
||||
fprintf(stdout, "Supported Persistent Reservation Types:\n");
|
||||
fprintf(stdout, " Write Exclusive - All Registrants "
|
||||
"(WR_EX_AR): %d\n",
|
||||
(cap->type_mask[0] & SPRI_TM_WR_EX_AR)? 1 : 0);
|
||||
fprintf(stdout, " Exclusive Access - Registrants Only "
|
||||
"(EX_AC_RO): %d\n",
|
||||
(cap->type_mask[0] & SPRI_TM_EX_AC_RO) ? 1 : 0);
|
||||
fprintf(stdout, " Write Exclusive - Registrants Only "
|
||||
"(WR_EX_RO): %d\n",
|
||||
(cap->type_mask[0] & SPRI_TM_WR_EX_RO)? 1 : 0);
|
||||
fprintf(stdout, " Exclusive Access (EX_AC): %d\n",
|
||||
(cap->type_mask[0] & SPRI_TM_EX_AC) ? 1 : 0);
|
||||
fprintf(stdout, " Write Exclusive (WR_EX): %d\n",
|
||||
(cap->type_mask[0] & SPRI_TM_WR_EX) ? 1 : 0);
|
||||
fprintf(stdout, " Exclusive Access - All Registrants "
|
||||
"(EX_AC_AR): %d\n",
|
||||
(cap->type_mask[1] & SPRI_TM_EX_AC_AR) ? 1 : 0);
|
||||
} else {
|
||||
fprintf(stdout, "Persistent Reservation Type Mask is NOT "
|
||||
"valid\n");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
|
||||
{
|
||||
uint32_t length, len_to_go = 0;
|
||||
struct scsi_per_res_in_full_desc *desc;
|
||||
uint8_t *cur_pos;
|
||||
int i;
|
||||
|
||||
length = scsi_4btoul(hdr->length);
|
||||
length = MIN(length, valid_len);
|
||||
|
||||
if (length < sizeof(*desc)) {
|
||||
if (length == 0)
|
||||
fprintf(stdout, "No reservations.\n");
|
||||
else
|
||||
warnx("unable to print reservation, only got %u "
|
||||
"valid bytes", length);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
|
||||
cur_pos = (uint8_t *)&hdr[1];
|
||||
for (len_to_go = length, i = 0,
|
||||
desc = (struct scsi_per_res_in_full_desc *)cur_pos;
|
||||
len_to_go >= sizeof(*desc);
|
||||
desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
|
||||
uint32_t additional_length, cur_length;
|
||||
|
||||
|
||||
fprintf(stdout, "Reservation Key: %#jx\n",
|
||||
(uintmax_t)scsi_8btou64(desc->res_key.key));
|
||||
fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
|
||||
(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
|
||||
fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
|
||||
(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
|
||||
|
||||
if (desc->flags & SPRI_FULL_R_HOLDER)
|
||||
persist_print_scopetype(desc->scopetype);
|
||||
|
||||
if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
|
||||
fprintf(stdout, "Relative Target Port ID: %#x\n",
|
||||
scsi_2btoul(desc->rel_trgt_port_id));
|
||||
|
||||
additional_length = scsi_4btoul(desc->additional_length);
|
||||
|
||||
persist_print_transportid(desc->transport_id,
|
||||
additional_length);
|
||||
|
||||
cur_length = sizeof(*desc) + additional_length;
|
||||
len_to_go -= cur_length;
|
||||
cur_pos += cur_length;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
|
||||
int retry_count, int timeout, int verbosemode, int err_recover)
|
||||
{
|
||||
union ccb *ccb = NULL;
|
||||
int c, in = 0, out = 0;
|
||||
int action = -1, num_ids = 0;
|
||||
int error = 0;
|
||||
uint32_t res_len = 0;
|
||||
unsigned long rel_tgt_port = 0;
|
||||
uint8_t *res_buf = NULL;
|
||||
int scope = SPR_LU_SCOPE, res_type = 0, key_set = 0, sa_key_set = 0;
|
||||
struct persist_transport_id *id, *id2;
|
||||
STAILQ_HEAD(, persist_transport_id) transport_id_list;
|
||||
uint64_t key = 0, sa_key = 0;
|
||||
struct scsi_nv *table = NULL;
|
||||
size_t table_size = 0, id_len = 0;
|
||||
uint32_t valid_len = 0;
|
||||
int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
|
||||
|
||||
STAILQ_INIT(&transport_id_list);
|
||||
|
||||
ccb = cam_getccb(device);
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating CCB", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bzero(&(&ccb->ccb_h)[1],
|
||||
sizeof(union ccb) - sizeof(struct ccb_hdr));
|
||||
|
||||
while ((c = getopt(argc, argv, combinedopt)) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
all_tg_pt = 1;
|
||||
break;
|
||||
case 'I': {
|
||||
int error_str_len = 128;
|
||||
char error_str[error_str_len];
|
||||
char *id_str;
|
||||
|
||||
id = malloc(sizeof(*id));
|
||||
if (id == NULL) {
|
||||
warnx("%s: error allocating %zu bytes",
|
||||
__func__, sizeof(*id));
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
bzero(id, sizeof(*id));
|
||||
|
||||
id_str = strdup(optarg);
|
||||
if (id_str == NULL) {
|
||||
warnx("%s: error duplicating string %s",
|
||||
__func__, optarg);
|
||||
free(id);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
error = scsi_parse_transportid(id_str, &id->hdr,
|
||||
&id->alloc_len, error_str, error_str_len);
|
||||
if (error != 0) {
|
||||
warnx("%s", error_str);
|
||||
error = 1;
|
||||
free(id);
|
||||
free(id_str);
|
||||
goto bailout;
|
||||
}
|
||||
free(id_str);
|
||||
|
||||
STAILQ_INSERT_TAIL(&transport_id_list, id, links);
|
||||
num_ids++;
|
||||
id_len += id->alloc_len;
|
||||
break;
|
||||
}
|
||||
case 'k':
|
||||
case 'K': {
|
||||
char *endptr;
|
||||
uint64_t tmpval;
|
||||
|
||||
tmpval = strtoumax(optarg, &endptr, 0);
|
||||
if (*endptr != '\0') {
|
||||
warnx("%s: invalid key argument %s", __func__,
|
||||
optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
if (c == 'k') {
|
||||
key = tmpval;
|
||||
key_set = 1;
|
||||
} else {
|
||||
sa_key = tmpval;
|
||||
sa_key_set = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
case 'o': {
|
||||
scsi_nv_status status;
|
||||
int table_entry = 0;
|
||||
|
||||
if (c == 'i') {
|
||||
in = 1;
|
||||
table = persist_in_actions;
|
||||
table_size = sizeof(persist_in_actions) /
|
||||
sizeof(persist_in_actions[0]);
|
||||
} else {
|
||||
out = 1;
|
||||
table = persist_out_actions;
|
||||
table_size = sizeof(persist_out_actions) /
|
||||
sizeof(persist_out_actions[0]);
|
||||
}
|
||||
|
||||
if ((in + out) > 1) {
|
||||
warnx("%s: only one in (-i) or out (-o) "
|
||||
"action is allowed", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
status = scsi_get_nv(table, table_size, optarg,
|
||||
&table_entry,SCSI_NV_FLAG_IG_CASE);
|
||||
if (status == SCSI_NV_FOUND)
|
||||
action = table[table_entry].value;
|
||||
else {
|
||||
warnx("%s: %s %s option %s", __func__,
|
||||
(status == SCSI_NV_AMBIGUOUS) ?
|
||||
"ambiguous" : "invalid", in ? "in" :
|
||||
"out", optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
aptpl = 1;
|
||||
break;
|
||||
case 'R': {
|
||||
char *endptr;
|
||||
|
||||
rel_tgt_port = strtoul(optarg, &endptr, 0);
|
||||
if (*endptr != '\0') {
|
||||
warnx("%s: invalid relative target port %s",
|
||||
__func__, optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
rel_port_set = 1;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
size_t scope_size;
|
||||
struct scsi_nv *scope_table = NULL;
|
||||
scsi_nv_status status;
|
||||
int table_entry = 0;
|
||||
char *endptr;
|
||||
|
||||
/*
|
||||
* First check to see if the user gave us a numeric
|
||||
* argument. If so, we'll try using it.
|
||||
*/
|
||||
if (isdigit(optarg[0])) {
|
||||
scope = strtol(optarg, &endptr, 0);
|
||||
if (*endptr != '\0') {
|
||||
warnx("%s: invalid scope %s",
|
||||
__func__, optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
scope = (scope << SPR_SCOPE_SHIFT) &
|
||||
SPR_SCOPE_MASK;
|
||||
break;
|
||||
}
|
||||
|
||||
scope_size = sizeof(persist_scope_table) /
|
||||
sizeof(persist_scope_table[0]);
|
||||
scope_table = persist_scope_table;
|
||||
status = scsi_get_nv(scope_table, scope_size, optarg,
|
||||
&table_entry,SCSI_NV_FLAG_IG_CASE);
|
||||
if (status == SCSI_NV_FOUND)
|
||||
scope = scope_table[table_entry].value;
|
||||
else {
|
||||
warnx("%s: %s scope %s", __func__,
|
||||
(status == SCSI_NV_AMBIGUOUS) ?
|
||||
"ambiguous" : "invalid", optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'S':
|
||||
spec_i_pt = 1;
|
||||
break;
|
||||
case 'T': {
|
||||
size_t res_type_size;
|
||||
struct scsi_nv *rtype_table = NULL;
|
||||
scsi_nv_status status;
|
||||
char *endptr;
|
||||
int table_entry = 0;
|
||||
|
||||
/*
|
||||
* First check to see if the user gave us a numeric
|
||||
* argument. If so, we'll try using it.
|
||||
*/
|
||||
if (isdigit(optarg[0])) {
|
||||
res_type = strtol(optarg, &endptr, 0);
|
||||
if (*endptr != '\0') {
|
||||
warnx("%s: invalid reservation type %s",
|
||||
__func__, optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
res_type_size = sizeof(persist_type_table) /
|
||||
sizeof(persist_type_table[0]);
|
||||
rtype_table = persist_type_table;
|
||||
status = scsi_get_nv(rtype_table, res_type_size,
|
||||
optarg, &table_entry,
|
||||
SCSI_NV_FLAG_IG_CASE);
|
||||
if (status == SCSI_NV_FOUND)
|
||||
res_type = rtype_table[table_entry].value;
|
||||
else {
|
||||
warnx("%s: %s reservation type %s", __func__,
|
||||
(status == SCSI_NV_AMBIGUOUS) ?
|
||||
"ambiguous" : "invalid", optarg);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
unreg = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((in + out) != 1) {
|
||||
warnx("%s: you must specify one of -i or -o", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that we don't really try to figure out whether the user
|
||||
* needs to specify one or both keys. There are a number of
|
||||
* scenarios, and sometimes 0 is a valid and desired value.
|
||||
*/
|
||||
if (in != 0) {
|
||||
switch (action) {
|
||||
case SPRI_RK:
|
||||
case SPRI_RR:
|
||||
case SPRI_RS:
|
||||
/*
|
||||
* Allocate the maximum length possible for these
|
||||
* service actions. According to the spec, the
|
||||
* target is supposed to return the available
|
||||
* length in the header, regardless of the
|
||||
* allocation length. In practice, though, with
|
||||
* the READ FULL STATUS (SPRI_RS) service action,
|
||||
* some Seagate drives (in particular a
|
||||
* Constellation ES, <SEAGATE ST32000444SS 0006>)
|
||||
* don't return the available length if you only
|
||||
* allocate the length of the header. So just
|
||||
* allocate the maximum here so we don't miss
|
||||
* anything.
|
||||
*/
|
||||
res_len = SPRI_MAX_LEN;
|
||||
break;
|
||||
case SPRI_RC:
|
||||
res_len = sizeof(struct scsi_per_res_cap);
|
||||
break;
|
||||
default:
|
||||
/* In theory we should catch this above */
|
||||
warnx("%s: invalid action %d", __func__, action);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
|
||||
/*
|
||||
* XXX KDM need to add length for transport IDs for the
|
||||
* register and move service action and the register
|
||||
* service action with the SPEC_I_PT bit set.
|
||||
*/
|
||||
if (action == SPRO_REG_MOVE) {
|
||||
if (num_ids != 1) {
|
||||
warnx("%s: register and move requires a "
|
||||
"single transport ID (-I)", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
if (rel_port_set == 0) {
|
||||
warnx("%s: register and move requires a "
|
||||
"relative target port (-R)", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
|
||||
} else {
|
||||
res_len = sizeof(struct scsi_per_res_out_parms);
|
||||
if ((action == SPRO_REGISTER)
|
||||
&& (num_ids != 0)) {
|
||||
/*
|
||||
* If the user specifies any IDs with the
|
||||
* register service action, turn on the
|
||||
* spec_i_pt bit.
|
||||
*/
|
||||
spec_i_pt = 1;
|
||||
res_len += id_len;
|
||||
res_len +=
|
||||
sizeof(struct scsi_per_res_out_trans_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
retry:
|
||||
if (res_buf != NULL) {
|
||||
free(res_buf);
|
||||
res_buf = NULL;
|
||||
}
|
||||
res_buf = malloc(res_len);
|
||||
if (res_buf == NULL) {
|
||||
warn("%s: error allocating %d bytes", __func__, res_len);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
bzero(res_buf, res_len);
|
||||
|
||||
if (in != 0) {
|
||||
scsi_persistent_reserve_in(&ccb->csio,
|
||||
/*retries*/ retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*service_action*/ action,
|
||||
/*data_ptr*/ res_buf,
|
||||
/*dxfer_len*/ res_len,
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ? timeout :5000);
|
||||
|
||||
} else {
|
||||
switch (action) {
|
||||
case SPRO_REGISTER:
|
||||
if (spec_i_pt != 0) {
|
||||
struct scsi_per_res_out_trans_ids *id_hdr;
|
||||
uint8_t *bufptr;
|
||||
|
||||
bufptr = res_buf +
|
||||
sizeof(struct scsi_per_res_out_parms) +
|
||||
sizeof(struct scsi_per_res_out_trans_ids);
|
||||
STAILQ_FOREACH(id, &transport_id_list, links) {
|
||||
bcopy(id->hdr, bufptr, id->alloc_len);
|
||||
bufptr += id->alloc_len;
|
||||
}
|
||||
id_hdr = (struct scsi_per_res_out_trans_ids *)
|
||||
(res_buf +
|
||||
sizeof(struct scsi_per_res_out_parms));
|
||||
scsi_ulto4b(id_len, id_hdr->additional_length);
|
||||
}
|
||||
case SPRO_REG_IGNO:
|
||||
case SPRO_PREEMPT:
|
||||
case SPRO_PRE_ABO:
|
||||
case SPRO_RESERVE:
|
||||
case SPRO_RELEASE:
|
||||
case SPRO_CLEAR:
|
||||
case SPRO_REPL_LOST_RES: {
|
||||
struct scsi_per_res_out_parms *parms;
|
||||
|
||||
parms = (struct scsi_per_res_out_parms *)res_buf;
|
||||
|
||||
scsi_u64to8b(key, parms->res_key.key);
|
||||
scsi_u64to8b(sa_key, parms->serv_act_res_key);
|
||||
if (spec_i_pt != 0)
|
||||
parms->flags |= SPR_SPEC_I_PT;
|
||||
if (all_tg_pt != 0)
|
||||
parms->flags |= SPR_ALL_TG_PT;
|
||||
if (aptpl != 0)
|
||||
parms->flags |= SPR_APTPL;
|
||||
break;
|
||||
}
|
||||
case SPRO_REG_MOVE: {
|
||||
struct scsi_per_res_reg_move *reg_move;
|
||||
uint8_t *bufptr;
|
||||
|
||||
reg_move = (struct scsi_per_res_reg_move *)res_buf;
|
||||
|
||||
scsi_u64to8b(key, reg_move->res_key.key);
|
||||
scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
|
||||
if (unreg != 0)
|
||||
reg_move->flags |= SPR_REG_MOVE_UNREG;
|
||||
if (aptpl != 0)
|
||||
reg_move->flags |= SPR_REG_MOVE_APTPL;
|
||||
scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
|
||||
id = STAILQ_FIRST(&transport_id_list);
|
||||
/*
|
||||
* This shouldn't happen, since we already checked
|
||||
* the number of IDs above.
|
||||
*/
|
||||
if (id == NULL) {
|
||||
warnx("%s: No transport IDs found!", __func__);
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
bufptr = (uint8_t *)®_move[1];
|
||||
bcopy(id->hdr, bufptr, id->alloc_len);
|
||||
scsi_ulto4b(id->alloc_len,
|
||||
reg_move->transport_id_length);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
scsi_persistent_reserve_out(&ccb->csio,
|
||||
/*retries*/ retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*service_action*/ action,
|
||||
/*scope*/ scope,
|
||||
/*res_type*/ res_type,
|
||||
/*data_ptr*/ res_buf,
|
||||
/*dxfer_len*/ res_len,
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ?timeout :5000);
|
||||
}
|
||||
|
||||
/* Disable freezing the device queue */
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
|
||||
if (err_recover != 0)
|
||||
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
|
||||
|
||||
if (cam_send_ccb(device, ccb) < 0) {
|
||||
warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
|
||||
"IN" : "OUT");
|
||||
|
||||
if (verbosemode != 0) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
}
|
||||
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
if (verbosemode != 0) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
}
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (in == 0)
|
||||
goto bailout;
|
||||
|
||||
valid_len = res_len - ccb->csio.resid;
|
||||
|
||||
switch (action) {
|
||||
case SPRI_RK:
|
||||
case SPRI_RR:
|
||||
case SPRI_RS: {
|
||||
struct scsi_per_res_in_header *hdr;
|
||||
uint32_t hdr_len;
|
||||
|
||||
if (valid_len < sizeof(*hdr)) {
|
||||
warnx("%s: only got %d valid bytes, need %zd",
|
||||
__func__, valid_len, sizeof(*hdr));
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
hdr = (struct scsi_per_res_in_header *)res_buf;
|
||||
hdr_len = scsi_4btoul(hdr->length);
|
||||
|
||||
if (hdr_len > (res_len - sizeof(*hdr))) {
|
||||
res_len = hdr_len + sizeof(*hdr);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (action == SPRI_RK) {
|
||||
persist_print_keys(hdr, valid_len);
|
||||
} else if (action == SPRI_RR) {
|
||||
persist_print_res(hdr, valid_len);
|
||||
} else {
|
||||
persist_print_full(hdr, valid_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPRI_RC: {
|
||||
struct scsi_per_res_cap *cap;
|
||||
uint32_t cap_len;
|
||||
|
||||
if (valid_len < sizeof(*cap)) {
|
||||
warnx("%s: only got %u valid bytes, need %zd",
|
||||
__func__, valid_len, sizeof(*cap));
|
||||
error = 1;
|
||||
goto bailout;
|
||||
}
|
||||
cap = (struct scsi_per_res_cap *)res_buf;
|
||||
cap_len = scsi_2btoul(cap->length);
|
||||
if (cap_len != sizeof(*cap)) {
|
||||
/*
|
||||
* We should be able to deal with this,
|
||||
* it's just more trouble.
|
||||
*/
|
||||
warnx("%s: reported size %u is different "
|
||||
"than expected size %zd", __func__,
|
||||
cap_len, sizeof(*cap));
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is more data available, grab it all,
|
||||
* even though we don't really know what to do with
|
||||
* the extra data since it obviously wasn't in the
|
||||
* spec when this code was written.
|
||||
*/
|
||||
if (cap_len > res_len) {
|
||||
res_len = cap_len;
|
||||
goto retry;
|
||||
}
|
||||
persist_print_cap(cap, valid_len);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bailout:
|
||||
free(res_buf);
|
||||
|
||||
if (ccb != NULL)
|
||||
cam_freeccb(ccb);
|
||||
|
||||
STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
|
||||
STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
|
||||
links);
|
||||
free(id);
|
||||
}
|
||||
return (error);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -278,6 +278,7 @@ struct scsi_per_res_in
|
||||
#define SPRI_RS 0x03
|
||||
u_int8_t reserved[5];
|
||||
u_int8_t length[2];
|
||||
#define SPRI_MAX_LEN 0xffff
|
||||
u_int8_t control;
|
||||
};
|
||||
|
||||
@ -302,18 +303,21 @@ struct scsi_per_res_cap
|
||||
{
|
||||
uint8_t length[2];
|
||||
uint8_t flags1;
|
||||
#define SPRI_CRH 0x10
|
||||
#define SPRI_SIP_C 0x08
|
||||
#define SPRI_ATP_C 0x04
|
||||
#define SPRI_PTPL_C 0x01
|
||||
#define SPRI_RLR_C 0x80
|
||||
#define SPRI_CRH 0x10
|
||||
#define SPRI_SIP_C 0x08
|
||||
#define SPRI_ATP_C 0x04
|
||||
#define SPRI_PTPL_C 0x01
|
||||
uint8_t flags2;
|
||||
#define SPRI_TMV 0x80
|
||||
#define SPRI_ALLOW_MASK 0x70
|
||||
#define SPRI_ALLOW_0 0x00
|
||||
#define SPRI_ALLOW_1 0x10
|
||||
#define SPRI_ALLOW_2 0x20
|
||||
#define SPRI_ALLOW_3 0x30
|
||||
#define SPRI_PTPL_A 0x01
|
||||
#define SPRI_TMV 0x80
|
||||
#define SPRI_ALLOW_CMD_MASK 0x70
|
||||
#define SPRI_ALLOW_CMD_SHIFT 4
|
||||
#define SPRI_ALLOW_NA 0x00
|
||||
#define SPRI_ALLOW_1 0x10
|
||||
#define SPRI_ALLOW_2 0x20
|
||||
#define SPRI_ALLOW_3 0x30
|
||||
#define SPRI_ALLOW_4 0x40
|
||||
#define SPRI_PTPL_A 0x01
|
||||
uint8_t type_mask[2];
|
||||
#define SPRI_TM_WR_EX_AR 0x8000
|
||||
#define SPRI_TM_EX_AC_RO 0x4000
|
||||
@ -327,7 +331,7 @@ struct scsi_per_res_cap
|
||||
struct scsi_per_res_in_rsrv_data
|
||||
{
|
||||
uint8_t reservation[8];
|
||||
uint8_t obsolete1[4];
|
||||
uint8_t scope_addr[4];
|
||||
uint8_t reserved;
|
||||
uint8_t scopetype;
|
||||
#define SPRT_WE 0x01
|
||||
@ -336,7 +340,7 @@ struct scsi_per_res_in_rsrv_data
|
||||
#define SPRT_EARO 0x06
|
||||
#define SPRT_WEAR 0x07
|
||||
#define SPRT_EAAR 0x08
|
||||
uint8_t obsolete2[2];
|
||||
uint8_t extent_length[2];
|
||||
};
|
||||
|
||||
struct scsi_per_res_in_rsrv
|
||||
@ -345,6 +349,26 @@ struct scsi_per_res_in_rsrv
|
||||
struct scsi_per_res_in_rsrv_data data;
|
||||
};
|
||||
|
||||
struct scsi_per_res_in_full_desc
|
||||
{
|
||||
struct scsi_per_res_key res_key;
|
||||
uint8_t reserved1[4];
|
||||
uint8_t flags;
|
||||
#define SPRI_FULL_ALL_TG_PT 0x02
|
||||
#define SPRI_FULL_R_HOLDER 0x01
|
||||
uint8_t scopetype;
|
||||
uint8_t reserved2[4];
|
||||
uint8_t rel_trgt_port_id[2];
|
||||
uint8_t additional_length[4];
|
||||
uint8_t transport_id[];
|
||||
};
|
||||
|
||||
struct scsi_per_res_in_full
|
||||
{
|
||||
struct scsi_per_res_in_header header;
|
||||
struct scsi_per_res_in_full_desc desc[];
|
||||
};
|
||||
|
||||
struct scsi_per_res_out
|
||||
{
|
||||
u_int8_t opcode;
|
||||
@ -357,13 +381,20 @@ struct scsi_per_res_out
|
||||
#define SPRO_PRE_ABO 0x05
|
||||
#define SPRO_REG_IGNO 0x06
|
||||
#define SPRO_REG_MOVE 0x07
|
||||
#define SPRO_REPL_LOST_RES 0x08
|
||||
#define SPRO_ACTION_MASK 0x1f
|
||||
u_int8_t scope_type;
|
||||
#define SPR_SCOPE_MASK 0xf0
|
||||
#define SPR_SCOPE_SHIFT 4
|
||||
#define SPR_LU_SCOPE 0x00
|
||||
#define SPR_EXTENT_SCOPE 0x10
|
||||
#define SPR_ELEMENT_SCOPE 0x20
|
||||
#define SPR_TYPE_MASK 0x0f
|
||||
#define SPR_TYPE_RD_SHARED 0x00
|
||||
#define SPR_TYPE_WR_EX 0x01
|
||||
#define SPR_TYPE_RD_EX 0x02
|
||||
#define SPR_TYPE_EX_AC 0x03
|
||||
#define SPR_TYPE_SHARED 0x04
|
||||
#define SPR_TYPE_WR_EX_RO 0x05
|
||||
#define SPR_TYPE_EX_AC_RO 0x06
|
||||
#define SPR_TYPE_WR_EX_AR 0x07
|
||||
@ -377,15 +408,139 @@ struct scsi_per_res_out_parms
|
||||
{
|
||||
struct scsi_per_res_key res_key;
|
||||
u_int8_t serv_act_res_key[8];
|
||||
u_int8_t obsolete1[4];
|
||||
u_int8_t scope_spec_address[4];
|
||||
u_int8_t flags;
|
||||
#define SPR_SPEC_I_PT 0x08
|
||||
#define SPR_ALL_TG_PT 0x04
|
||||
#define SPR_APTPL 0x01
|
||||
u_int8_t reserved1;
|
||||
u_int8_t obsolete2[2];
|
||||
u_int8_t extent_length[2];
|
||||
u_int8_t transport_id_list[];
|
||||
};
|
||||
|
||||
struct scsi_per_res_out_trans_ids {
|
||||
u_int8_t additional_length[4];
|
||||
u_int8_t transport_ids[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Used with REGISTER AND MOVE serivce action of the PERSISTENT RESERVE OUT
|
||||
* command.
|
||||
*/
|
||||
struct scsi_per_res_reg_move
|
||||
{
|
||||
struct scsi_per_res_key res_key;
|
||||
u_int8_t serv_act_res_key[8];
|
||||
u_int8_t reserved;
|
||||
u_int8_t flags;
|
||||
#define SPR_REG_MOVE_UNREG 0x02
|
||||
#define SPR_REG_MOVE_APTPL 0x01
|
||||
u_int8_t rel_trgt_port_id[2];
|
||||
u_int8_t transport_id_length[4];
|
||||
u_int8_t transport_id[];
|
||||
};
|
||||
|
||||
struct scsi_transportid_header
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_FORMAT_MASK 0xc0
|
||||
#define SCSI_TRN_FORMAT_SHIFT 6
|
||||
#define SCSI_TRN_PROTO_MASK 0x0f
|
||||
};
|
||||
|
||||
struct scsi_transportid_fcp
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_FCP_FORMAT_DEFAULT 0x00
|
||||
uint8_t reserved1[7];
|
||||
uint8_t n_port_name[8];
|
||||
uint8_t reserved2[8];
|
||||
};
|
||||
|
||||
struct scsi_transportid_spi
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_SPI_FORMAT_DEFAULT 0x00
|
||||
uint8_t reserved1;
|
||||
uint8_t scsi_addr[2];
|
||||
uint8_t obsolete[2];
|
||||
uint8_t rel_trgt_port_id[2];
|
||||
uint8_t reserved2[16];
|
||||
};
|
||||
|
||||
struct scsi_transportid_1394
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_1394_FORMAT_DEFAULT 0x00
|
||||
uint8_t reserved1[7];
|
||||
uint8_t eui64[8];
|
||||
uint8_t reserved2[8];
|
||||
};
|
||||
|
||||
struct scsi_transportid_rdma
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_RDMA_FORMAT_DEFAULT 0x00
|
||||
uint8_t reserved[7];
|
||||
#define SCSI_TRN_RDMA_PORT_LEN 16
|
||||
uint8_t initiator_port_id[SCSI_TRN_RDMA_PORT_LEN];
|
||||
};
|
||||
|
||||
struct scsi_transportid_iscsi_device
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_ISCSI_FORMAT_DEVICE 0x00
|
||||
uint8_t reserved;
|
||||
uint8_t additional_length[2];
|
||||
uint8_t iscsi_name[];
|
||||
};
|
||||
|
||||
struct scsi_transportid_iscsi_port
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_ISCSI_FORMAT_PORT 0x40
|
||||
uint8_t reserved;
|
||||
uint8_t additional_length[2];
|
||||
uint8_t iscsi_name[];
|
||||
/*
|
||||
* Followed by a separator and iSCSI initiator session ID
|
||||
*/
|
||||
};
|
||||
|
||||
struct scsi_transportid_sas
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_SAS_FORMAT_DEFAULT 0x00
|
||||
uint8_t reserved1[3];
|
||||
uint8_t sas_address[8];
|
||||
uint8_t reserved2[12];
|
||||
};
|
||||
|
||||
struct scsi_sop_routing_id_norm {
|
||||
uint8_t bus;
|
||||
uint8_t devfunc;
|
||||
#define SCSI_TRN_SOP_BUS_MAX 0xff
|
||||
#define SCSI_TRN_SOP_DEV_MAX 0x1f
|
||||
#define SCSI_TRN_SOP_DEV_MASK 0xf8
|
||||
#define SCSI_TRN_SOP_DEV_SHIFT 3
|
||||
#define SCSI_TRN_SOP_FUNC_NORM_MASK 0x07
|
||||
#define SCSI_TRN_SOP_FUNC_NORM_MAX 0x07
|
||||
};
|
||||
|
||||
struct scsi_sop_routing_id_alt {
|
||||
uint8_t bus;
|
||||
uint8_t function;
|
||||
#define SCSI_TRN_SOP_FUNC_ALT_MAX 0xff
|
||||
};
|
||||
|
||||
struct scsi_transportid_sop
|
||||
{
|
||||
uint8_t format_protocol;
|
||||
#define SCSI_TRN_SOP_FORMAT_DEFAULT 0x00
|
||||
uint8_t reserved1;
|
||||
uint8_t routing_id[2];
|
||||
uint8_t reserved2[20];
|
||||
};
|
||||
|
||||
struct scsi_log_sense
|
||||
{
|
||||
@ -611,21 +766,41 @@ struct scsi_info_exceptions_page {
|
||||
u_int8_t report_count[4];
|
||||
};
|
||||
|
||||
/*
|
||||
* SCSI protocol identifier values, current as of SPC4r36l.
|
||||
*/
|
||||
#define SCSI_PROTO_FC 0x00 /* Fibre Channel */
|
||||
#define SCSI_PROTO_SPI 0x01 /* Parallel SCSI */
|
||||
#define SCSI_PROTO_SSA 0x02 /* Serial Storage Arch. */
|
||||
#define SCSI_PROTO_1394 0x03 /* IEEE 1394 (Firewire) */
|
||||
#define SCSI_PROTO_RDMA 0x04 /* SCSI RDMA Protocol */
|
||||
#define SCSI_PROTO_ISCSI 0x05 /* Internet SCSI */
|
||||
#define SCSI_PROTO_iSCSI 0x05 /* Internet SCSI */
|
||||
#define SCSI_PROTO_SAS 0x06 /* SAS Serial SCSI Protocol */
|
||||
#define SCSI_PROTO_ADT 0x07 /* Automation/Drive Int. Trans. Prot.*/
|
||||
#define SCSI_PROTO_ADITP 0x07 /* Automation/Drive Int. Trans. Prot.*/
|
||||
#define SCSI_PROTO_ATA 0x08 /* AT Attachment Interface */
|
||||
#define SCSI_PROTO_UAS 0x09 /* USB Atached SCSI */
|
||||
#define SCSI_PROTO_SOP 0x0a /* SCSI over PCI Express */
|
||||
#define SCSI_PROTO_NONE 0x0f /* No specific protocol */
|
||||
|
||||
struct scsi_proto_specific_page {
|
||||
u_int8_t page_code;
|
||||
#define SPSP_PAGE_SAVABLE 0x80 /* Page is savable */
|
||||
u_int8_t page_length;
|
||||
u_int8_t protocol;
|
||||
#define SPSP_PROTO_FC 0x00
|
||||
#define SPSP_PROTO_SPI 0x01
|
||||
#define SPSP_PROTO_SSA 0x02
|
||||
#define SPSP_PROTO_1394 0x03
|
||||
#define SPSP_PROTO_RDMA 0x04
|
||||
#define SPSP_PROTO_ISCSI 0x05
|
||||
#define SPSP_PROTO_SAS 0x06
|
||||
#define SPSP_PROTO_ADT 0x07
|
||||
#define SPSP_PROTO_ATA 0x08
|
||||
#define SPSP_PROTO_NONE 0x0f
|
||||
#define SPSP_PROTO_FC SCSI_PROTO_FC
|
||||
#define SPSP_PROTO_SPI SCSI_PROTO_SPI
|
||||
#define SPSP_PROTO_SSA SCSI_PROTO_SSA
|
||||
#define SPSP_PROTO_1394 SCSI_PROTO_1394
|
||||
#define SPSP_PROTO_RDMA SCSI_PROTO_RDMA
|
||||
#define SPSP_PROTO_ISCSI SCSI_PROTO_ISCSI
|
||||
#define SPSP_PROTO_SAS SCSI_PROTO_SAS
|
||||
#define SPSP_PROTO_ADT SCSI_PROTO_ADITP
|
||||
#define SPSP_PROTO_ATA SCSI_PROTO_ATA
|
||||
#define SPSP_PROTO_UAS SCSI_PROTO_UAS
|
||||
#define SPSP_PROTO_SOP SCSI_PROTO_SOP
|
||||
#define SPSP_PROTO_NONE SCSI_PROTO_NONE
|
||||
};
|
||||
|
||||
struct scsi_reserve
|
||||
@ -1423,15 +1598,9 @@ struct scsi_vpd_device_id
|
||||
struct scsi_vpd_id_descriptor
|
||||
{
|
||||
u_int8_t proto_codeset;
|
||||
#define SCSI_PROTO_FC 0x00
|
||||
#define SCSI_PROTO_SPI 0x01
|
||||
#define SCSI_PROTO_SSA 0x02
|
||||
#define SCSI_PROTO_1394 0x03
|
||||
#define SCSI_PROTO_RDMA 0x04
|
||||
#define SCSI_PROTO_ISCSI 0x05
|
||||
#define SCSI_PROTO_SAS 0x06
|
||||
#define SCSI_PROTO_ADT 0x07
|
||||
#define SCSI_PROTO_ATA 0x08
|
||||
/*
|
||||
* See the SCSI_PROTO definitions above for the protocols.
|
||||
*/
|
||||
#define SVPD_ID_PROTO_SHIFT 4
|
||||
#define SVPD_ID_CODESET_BINARY 0x01
|
||||
#define SVPD_ID_CODESET_ASCII 0x02
|
||||
@ -2354,6 +2523,22 @@ typedef enum {
|
||||
SSS_FLAG_PRINT_COMMAND = 0x01
|
||||
} scsi_sense_string_flags;
|
||||
|
||||
struct scsi_nv {
|
||||
const char *name;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SCSI_NV_FOUND,
|
||||
SCSI_NV_AMBIGUOUS,
|
||||
SCSI_NV_NOT_FOUND
|
||||
} scsi_nv_status;
|
||||
|
||||
typedef enum {
|
||||
SCSI_NV_FLAG_NONE = 0x00,
|
||||
SCSI_NV_FLAG_IG_CASE = 0x01 /* Case insensitive comparison */
|
||||
} scsi_nv_flags;
|
||||
|
||||
struct ccb_scsiio;
|
||||
struct cam_periph;
|
||||
union ccb;
|
||||
@ -2495,6 +2680,64 @@ struct scsi_vpd_id_descriptor *
|
||||
scsi_get_devid(struct scsi_vpd_device_id *id, uint32_t len,
|
||||
scsi_devid_checkfn_t ck_fn);
|
||||
|
||||
int scsi_transportid_sbuf(struct sbuf *sb,
|
||||
struct scsi_transportid_header *hdr,
|
||||
uint32_t valid_len);
|
||||
|
||||
const char * scsi_nv_to_str(struct scsi_nv *table, int num_table_entries,
|
||||
uint64_t value);
|
||||
|
||||
scsi_nv_status scsi_get_nv(struct scsi_nv *table, int num_table_entries,
|
||||
char *name, int *table_entry, scsi_nv_flags flags);
|
||||
|
||||
int scsi_parse_transportid_64bit(int proto_id, char *id_str,
|
||||
struct scsi_transportid_header **hdr,
|
||||
unsigned int *alloc_len,
|
||||
#ifdef _KERNEL
|
||||
struct malloc_type *type, int flags,
|
||||
#endif
|
||||
char *error_str, int error_str_len);
|
||||
|
||||
int scsi_parse_transportid_spi(char *id_str,
|
||||
struct scsi_transportid_header **hdr,
|
||||
unsigned int *alloc_len,
|
||||
#ifdef _KERNEL
|
||||
struct malloc_type *type, int flags,
|
||||
#endif
|
||||
char *error_str, int error_str_len);
|
||||
|
||||
int scsi_parse_transportid_rdma(char *id_str,
|
||||
struct scsi_transportid_header **hdr,
|
||||
unsigned int *alloc_len,
|
||||
#ifdef _KERNEL
|
||||
struct malloc_type *type, int flags,
|
||||
#endif
|
||||
char *error_str, int error_str_len);
|
||||
|
||||
int scsi_parse_transportid_iscsi(char *id_str,
|
||||
struct scsi_transportid_header **hdr,
|
||||
unsigned int *alloc_len,
|
||||
#ifdef _KERNEL
|
||||
struct malloc_type *type, int flags,
|
||||
#endif
|
||||
char *error_str,int error_str_len);
|
||||
|
||||
int scsi_parse_transportid_sop(char *id_str,
|
||||
struct scsi_transportid_header **hdr,
|
||||
unsigned int *alloc_len,
|
||||
#ifdef _KERNEL
|
||||
struct malloc_type *type, int flags,
|
||||
#endif
|
||||
char *error_str,int error_str_len);
|
||||
|
||||
int scsi_parse_transportid(char *transportid_str,
|
||||
struct scsi_transportid_header **hdr,
|
||||
unsigned int *alloc_len,
|
||||
#ifdef _KERNEL
|
||||
struct malloc_type *type, int flags,
|
||||
#endif
|
||||
char *error_str, int error_str_len);
|
||||
|
||||
void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
union ccb *),
|
||||
@ -2690,6 +2933,20 @@ void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
u_int8_t tag_action, int start, int load_eject,
|
||||
int immediate, u_int8_t sense_len, u_int32_t timeout);
|
||||
|
||||
void scsi_persistent_reserve_in(struct ccb_scsiio *csio, uint32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,union ccb *),
|
||||
uint8_t tag_action, int service_action,
|
||||
uint8_t *data_ptr, uint32_t dxfer_len,
|
||||
int sense_len, int timeout);
|
||||
|
||||
void scsi_persistent_reserve_out(struct ccb_scsiio *csio, uint32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
union ccb *),
|
||||
uint8_t tag_action, int service_action,
|
||||
int scope, int res_type, uint8_t *data_ptr,
|
||||
uint32_t dxfer_len, int sense_len,
|
||||
int timeout);
|
||||
|
||||
int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry);
|
||||
int scsi_static_inquiry_match(caddr_t inqbuffer,
|
||||
caddr_t table_entry);
|
||||
|
Loading…
x
Reference in New Issue
Block a user