init commit for spc/sbc
This commit is contained in:
@@ -16,6 +16,8 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
type SCSICommandType byte
|
type SCSICommandType byte
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -185,8 +187,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SCSIDataBuffer struct {
|
type SCSIDataBuffer struct {
|
||||||
Buffer uint64
|
Buffer *bytes.Buffer
|
||||||
Length uint64
|
Length uint32
|
||||||
TransferLength uint32
|
TransferLength uint32
|
||||||
Resid int32
|
Resid int32
|
||||||
}
|
}
|
||||||
@@ -197,20 +199,22 @@ type SCSICommand struct {
|
|||||||
Device *SCSILu
|
Device *SCSILu
|
||||||
State uint64
|
State uint64
|
||||||
Direction SCSIDataDirection
|
Direction SCSIDataDirection
|
||||||
InSDBBuffer *SCSIDataBuffer
|
InSDBBuffer SCSIDataBuffer
|
||||||
OutSDBBuffer *SCSIDataBuffer
|
OutSDBBuffer SCSIDataBuffer
|
||||||
// Command ITN ID
|
// Command ITN ID
|
||||||
CommandITNID uint64
|
CommandITNID uint64
|
||||||
Offset uint64
|
Offset uint64
|
||||||
TL uint32
|
TL uint32
|
||||||
SCB *[]byte
|
SCB *bytes.Buffer
|
||||||
SCBLength int
|
SCBLength int
|
||||||
Lun []uint8
|
Lun []uint8
|
||||||
Attribute int
|
Attribute int
|
||||||
Tag uint64
|
Tag uint64
|
||||||
Result int
|
Result int
|
||||||
SenseBuffer []byte
|
SenseBuffer *bytes.Buffer
|
||||||
SenseLength int
|
SenseLength uint32
|
||||||
|
ITNexus *ITNexus
|
||||||
|
ITNexusLuInfo *ITNexusLuInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func SCSICDBGroupID(opcode byte) byte {
|
func SCSICDBGroupID(opcode byte) byte {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ type SCSITargetDriverOps interface {
|
|||||||
DestroyPortal(name string) error
|
DestroyPortal(name string) error
|
||||||
CreateLu(lu *SCSILu) error
|
CreateLu(lu *SCSILu) error
|
||||||
|
|
||||||
GetLun(lun uint8) (uint64, error)
|
GetLu(lun uint8) (uint64, error)
|
||||||
CommandNotify(nid uint64, result int, cmd *SCSICommand) error
|
CommandNotify(nid uint64, result int, cmd *SCSICommand) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,22 +16,43 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
type SCSIError byte
|
import "errors"
|
||||||
|
|
||||||
|
type SCSIError struct {
|
||||||
|
errno byte
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
NO_SENSE SCSIError = 0x00
|
NO_SENSE byte = 0x00
|
||||||
RECOVERED_ERROR SCSIError = 0x01
|
RECOVERED_ERROR byte = 0x01
|
||||||
NOT_READY SCSIError = 0x02
|
NOT_READY byte = 0x02
|
||||||
MEDIUM_ERROR SCSIError = 0x03
|
MEDIUM_ERROR byte = 0x03
|
||||||
HARDWARE_ERROR SCSIError = 0x04
|
HARDWARE_ERROR byte = 0x04
|
||||||
ILLEGAL_REQUEST SCSIError = 0x05
|
ILLEGAL_REQUEST byte = 0x05
|
||||||
UNIT_ATTENTION SCSIError = 0x06
|
UNIT_ATTENTION byte = 0x06
|
||||||
DATA_PROTECT SCSIError = 0x07
|
DATA_PROTECT byte = 0x07
|
||||||
BLANK_CHECK SCSIError = 0x08
|
BLANK_CHECK byte = 0x08
|
||||||
COPY_ABORTED SCSIError = 0x0a
|
COPY_ABORTED byte = 0x0a
|
||||||
ABORTED_COMMAND SCSIError = 0x0b
|
ABORTED_COMMAND byte = 0x0b
|
||||||
VOLUME_OVERFLOW SCSIError = 0x0d
|
VOLUME_OVERFLOW byte = 0x0d
|
||||||
MISCOMPARE SCSIError = 0x0e
|
MISCOMPARE byte = 0x0e
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
NoSenseError = SCSIError{NO_SENSE, errors.New("no sense")}
|
||||||
|
RecoveredError = SCSIError{RECOVERED_ERROR, errors.New("recovered error")}
|
||||||
|
NotReadyError = SCSIError{NOT_READY, errors.New("not ready")}
|
||||||
|
MediumError = SCSIError{MEDIUM_ERROR, errors.New("medium error")}
|
||||||
|
HardwareError = SCSIError{HARDWARE_ERROR, errors.New("hardware error")}
|
||||||
|
IllegalRequestError = SCSIError{ILLEGAL_REQUEST, errors.New("illegal request")}
|
||||||
|
UnitAttentionError = SCSIError{UNIT_ATTENTION, errors.New("unit attention")}
|
||||||
|
DataProtectError = SCSIError{DATA_PROTECT, errors.New("data protect")}
|
||||||
|
BlankCheckError = SCSIError{BLANK_CHECK, errors.New("blank check")}
|
||||||
|
CopyAbortedError = SCSIError{COPY_ABORTED, errors.New("copy aborted")}
|
||||||
|
AbortedCommandError = SCSIError{ABORTED_COMMAND, errors.New("aborted command")}
|
||||||
|
VolumeOverflowError = SCSIError{VOLUME_OVERFLOW, errors.New("volume overflow")}
|
||||||
|
MiscompareError = SCSIError{MISCOMPARE, errors.New("miscompare")}
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCSISubError uint16
|
type SCSISubError uint16
|
||||||
|
|||||||
@@ -16,14 +16,58 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
type SCSILu struct {
|
type SCSILuPhyAttribute struct {
|
||||||
FD int
|
SCSIID string
|
||||||
Address uint64
|
SCSISN string
|
||||||
Size uint64
|
NumID uint64
|
||||||
Lun uint64
|
VendorID string
|
||||||
Path string
|
ProductID string
|
||||||
BsoFlags int
|
ProductRev string
|
||||||
BlockShift uint
|
VersionDesction []uint16
|
||||||
ReserveID uint64
|
// Peripheral device type
|
||||||
Target *SCSITarget
|
DeviceType uint
|
||||||
|
// Peripheral Qualifier
|
||||||
|
Qualifier bool
|
||||||
|
// Removable media
|
||||||
|
Removable bool
|
||||||
|
// Read Only media
|
||||||
|
Readonly bool
|
||||||
|
// Software Write Protect
|
||||||
|
SWP bool
|
||||||
|
// Use thin-provisioning for this LUN
|
||||||
|
Thinprovisioning bool
|
||||||
|
// Logical Unit online
|
||||||
|
Online bool
|
||||||
|
// Descrptor format sense data supported
|
||||||
|
SenseFormat bool
|
||||||
|
// Logical blocks per physical block exponent
|
||||||
|
Lbppbe int
|
||||||
|
// Do not update it automatically when the backing file changes
|
||||||
|
NoLbppbe int
|
||||||
|
// Lowest aligned LBA
|
||||||
|
LowestAlignedLBA int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCSILu struct {
|
||||||
|
FD int
|
||||||
|
Address uint64
|
||||||
|
Size uint64
|
||||||
|
Lun uint64
|
||||||
|
Path string
|
||||||
|
BsoFlags int
|
||||||
|
BlockShift uint
|
||||||
|
ReserveID uint64
|
||||||
|
DeviceProtocol SCSIDeviceProtocol
|
||||||
|
Storage *BackingStore
|
||||||
|
Target *SCSITarget
|
||||||
|
Attrs SCSILuPhyAttribute
|
||||||
|
|
||||||
|
// function handler for command performing and finishing
|
||||||
|
PerformCommand CommandFunc
|
||||||
|
FinishCommand func(*SCSITarget, *SCSICommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
func luPreventRemoval(lu *SCSILu) bool {
|
||||||
|
// TODO
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
208
pkg/scsi/sbc.go
208
pkg/scsi/sbc.go
@@ -17,6 +17,8 @@ limitations under the License.
|
|||||||
// SCSI block command processing
|
// SCSI block command processing
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
type SBCSCSIDeviceProtocol struct {
|
type SBCSCSIDeviceProtocol struct {
|
||||||
BaseSCSIDeviceProtocol
|
BaseSCSIDeviceProtocol
|
||||||
}
|
}
|
||||||
@@ -37,10 +39,12 @@ func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *SCSILu) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSBCDevice() (SBCSCSIDeviceProtocol, error) {
|
func NewSBCDevice() SBCSCSIDeviceProtocol {
|
||||||
var sbc = SBCSCSIDeviceProtocol{
|
var sbc = SBCSCSIDeviceProtocol{
|
||||||
BaseSCSIDeviceProtocol{Type: TYPE_DISK,
|
BaseSCSIDeviceProtocol{
|
||||||
SCSIDeviceOps: make([]SCSIDeviceOperation, 256)},
|
Type: TYPE_DISK,
|
||||||
|
SCSIDeviceOps: make([]SCSIDeviceOperation, 256),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i := 0; i <= 256; i++ {
|
for i := 0; i <= 256; i++ {
|
||||||
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
|
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
|
||||||
@@ -95,57 +99,201 @@ func NewSBCDevice() (SBCSCSIDeviceProtocol, error) {
|
|||||||
sbc.SCSIDeviceOps[WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[VERIFY_12] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[VERIFY_12] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
|
||||||
|
|
||||||
return sbc, nil
|
return sbc
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCModeSelect(host int, cmd *SCSICommand) error {
|
func SBCModeSelect(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCModeSense(host int, cmd *SCSICommand) error {
|
func SBCModeSense(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
// DPOFUA = 0x10
|
||||||
|
var deviceSpecific uint8 = 0x10
|
||||||
|
|
||||||
|
if err := SPCModeSense(host, cmd); err.Err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a read-only lun, we must set the write protect bit
|
||||||
|
if cmd.Device.Attrs.Readonly || cmd.Device.Attrs.SWP {
|
||||||
|
deviceSpecific |= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
data := cmd.InSDBBuffer.Buffer
|
||||||
|
data.Next(2)
|
||||||
|
|
||||||
|
if cmd.SCB.Bytes()[0] == 0x1a {
|
||||||
|
data.WriteByte(deviceSpecific)
|
||||||
|
} else {
|
||||||
|
data.Next(1)
|
||||||
|
data.WriteByte(deviceSpecific)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCFormatUnit(host int, cmd *SCSICommand) error {
|
// The FORMAT UNIT command requests that the device server format the medium into application client
|
||||||
return nil
|
// accessible logical blocks as specified in the number of blocks and block length values received
|
||||||
|
// in the last mode parameter block descriptor in a MODE SELECT command (see SPC-3). In addition,
|
||||||
|
// the device server may certify the medium and create control structures for the management of the medium and defects.
|
||||||
|
// The degree that the medium is altered by this command is vendor-specific.
|
||||||
|
func SBCFormatUnit(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
var (
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := deviceReserve(cmd); err != nil {
|
||||||
|
return SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.Device.Attrs.Online {
|
||||||
|
key = NOT_READY
|
||||||
|
asc = ASC_MEDIUM_NOT_PRESENT
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Device.Attrs.Readonly || cmd.Device.Attrs.SWP {
|
||||||
|
key = DATA_PROTECT
|
||||||
|
asc = ASC_WRITE_PROTECT
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.SCB.Bytes()[1]&0x80 != 0 {
|
||||||
|
// we dont support format protection information
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if cmd.SCB.Bytes()[1]&0x10 != 0 {
|
||||||
|
// we dont support format data
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if cmd.SCB.Bytes()[1]&0x07 != 0 {
|
||||||
|
// defect list format must be 0
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
return SAMStatGood
|
||||||
|
sense:
|
||||||
|
BuildSenseData(cmd, key, asc)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCUnmap(host int, cmd *SCSICommand) error {
|
func SBCUnmap(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCReadWrite(host int, cmd *SCSICommand) error {
|
func SBCReadWrite(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCReserve(host int, cmd *SCSICommand) error {
|
func SBCReserve(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
if err := deviceReserve(cmd); err != nil {
|
||||||
|
return SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCRelease(host int, cmd *SCSICommand) error {
|
func SBCRelease(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
if err := deviceRelease(cmd.Target.TID, cmd.CommandITNID, cmd.Device.Lun, false); err != nil {
|
||||||
|
return SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCReadCapacity(host int, cmd *SCSICommand) error {
|
// The READ CAPACITY (10) command requests that the device server transfer 8 bytes of parameter data
|
||||||
return nil
|
// describing the capacity and medium format of the direct-access block device to the data-in buffer.
|
||||||
|
// This command may be processed as if it has a HEAD OF QUEUE task attribute. If the logical unit supports
|
||||||
|
// protection information, the application client should use the READ CAPACITY (16) command instead of
|
||||||
|
// the READ CAPACITY (10) command.
|
||||||
|
func SBCReadCapacity(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
var (
|
||||||
|
scb = cmd.SCB.Bytes()
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_LUN_NOT_SUPPORTED
|
||||||
|
data = cmd.InSDBBuffer.Buffer
|
||||||
|
bshift = cmd.Device.BlockShift
|
||||||
|
size = cmd.Device.Size >> bshift
|
||||||
|
)
|
||||||
|
|
||||||
|
if cmd.Device.Attrs.Removable && !cmd.Device.Attrs.Online {
|
||||||
|
key = NOT_READY
|
||||||
|
asc = ASC_MEDIUM_NOT_PRESENT
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scb[8]&0x1 == 0) && (scb[2]|scb[3]|scb[4]|scb[5]) != 0 {
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.InSDBBuffer.Length < 8 {
|
||||||
|
goto overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
// data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
|
||||||
|
if size>>32 != 0 {
|
||||||
|
binary.Write(data, binary.BigEndian, uint32(0xffffffff))
|
||||||
|
} else {
|
||||||
|
binary.Write(data, binary.BigEndian, uint32(size-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// data[1] = __cpu_to_be32(1U << bshift);
|
||||||
|
binary.Write(data, binary.BigEndian, uint32(1<<bshift))
|
||||||
|
overflow:
|
||||||
|
cmd.InSDBBuffer.Resid = 8
|
||||||
|
return SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, key, asc)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCVerify(host int, cmd *SCSICommand) error {
|
// The VERIFY (10) command requests that the device server verify the specified logical block(s) on the medium.
|
||||||
return nil
|
func SBCVerify(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
var (
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
)
|
||||||
|
if cmd.Device.Attrs.Removable && !cmd.Device.Attrs.Online {
|
||||||
|
key = NOT_READY
|
||||||
|
asc = ASC_MEDIUM_NOT_PRESENT
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.SCB.Bytes()[1]&0xe0 != 0 {
|
||||||
|
// We only support protection information type 0
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.SCB.Bytes()[1]&0x02 == 0 {
|
||||||
|
// no data compare with the media
|
||||||
|
return SAMStatGood
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, key, asc)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCReadCapacity16(host int, cmd *SCSICommand) error {
|
func SBCReadCapacity16(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCGetLbaStatus(host int, cmd *SCSICommand) error {
|
func SBCGetLbaStatus(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCServiceAction(host int, cmd *SCSICommand) error {
|
func SBCServiceAction(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCSyncCache(host int, cmd *SCSICommand) error {
|
// The SYNCHRONIZE CACHE (10) command requests that the device server ensure that
|
||||||
return nil
|
// the specified logical blocks have their most recent data values recorded in
|
||||||
|
// non-volatile cache and/or on the medium, based on the SYNC_NV bit.
|
||||||
|
func SBCSyncCache(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|||||||
56
pkg/scsi/sbc_test.go
Normal file
56
pkg/scsi/sbc_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SCSI block command processing
|
||||||
|
package scsi
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSBCModeSelect(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCModeSense(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCFormatUnit(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCUnmap(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCReadWrite(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCReserve(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCRelease(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCReadCapacity(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCVerify(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCReadCapacity16(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCGetLbaStatus(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSBCSyncCache(t *testing.T) {
|
||||||
|
}
|
||||||
@@ -16,26 +16,46 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultBlockShift int = 9
|
DefaultBlockShift int = 9
|
||||||
DefaultSenseBufferSize int = 252
|
DefaultSenseBufferSize int = 252
|
||||||
)
|
)
|
||||||
|
|
||||||
type SAMStat byte
|
|
||||||
type SCSIDeviceType byte
|
type SCSIDeviceType byte
|
||||||
|
|
||||||
var (
|
var (
|
||||||
SAM_STAT_GOOD SAMStat = 0x00
|
SAM_STAT_GOOD byte = 0x00
|
||||||
SAM_STAT_CHECK_CONDITION SAMStat = 0x02
|
SAM_STAT_CHECK_CONDITION byte = 0x02
|
||||||
SAM_STAT_CONDITION_MET SAMStat = 0x04
|
SAM_STAT_CONDITION_MET byte = 0x04
|
||||||
SAM_STAT_BUSY SAMStat = 0x08
|
SAM_STAT_BUSY byte = 0x08
|
||||||
SAM_STAT_INTERMEDIATE SAMStat = 0x10
|
SAM_STAT_INTERMEDIATE byte = 0x10
|
||||||
SAM_STAT_INTERMEDIATE_CONDITION_MET SAMStat = 0x14
|
SAM_STAT_INTERMEDIATE_CONDITION_MET byte = 0x14
|
||||||
SAM_STAT_RESERVATION_CONFLICT SAMStat = 0x18
|
SAM_STAT_RESERVATION_CONFLICT byte = 0x18
|
||||||
SAM_STAT_COMMAND_TERMINATED SAMStat = 0x22
|
SAM_STAT_COMMAND_TERMINATED byte = 0x22
|
||||||
SAM_STAT_TASK_SET_FULL SAMStat = 0x28
|
SAM_STAT_TASK_SET_FULL byte = 0x28
|
||||||
SAM_STAT_ACA_ACTIVE SAMStat = 0x30
|
SAM_STAT_ACA_ACTIVE byte = 0x30
|
||||||
SAM_STAT_TASK_ABORTED SAMStat = 0x40
|
SAM_STAT_TASK_ABORTED byte = 0x40
|
||||||
|
)
|
||||||
|
|
||||||
|
type SAMStat struct {
|
||||||
|
Stat byte
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
SAMStatGood = SAMStat{SAM_STAT_GOOD, nil}
|
||||||
|
SAMStatCheckCondition = SAMStat{SAM_STAT_CHECK_CONDITION, errors.New("check condition")}
|
||||||
|
SAMStatConditionMet = SAMStat{SAM_STAT_CONDITION_MET, errors.New("condition met")}
|
||||||
|
SAMStatBusy = SAMStat{SAM_STAT_BUSY, errors.New("busy")}
|
||||||
|
SAMStatIntermediate = SAMStat{SAM_STAT_INTERMEDIATE, errors.New("intermediate")}
|
||||||
|
SAMStatIntermediateConditionMet = SAMStat{SAM_STAT_INTERMEDIATE_CONDITION_MET, errors.New("intermediate condition met")}
|
||||||
|
SAMStatReservationConflict = SAMStat{SAM_STAT_RESERVATION_CONFLICT, errors.New("reservation conflict")}
|
||||||
|
SAMStatCommandTerminated = SAMStat{SAM_STAT_COMMAND_TERMINATED, errors.New("command terminated")}
|
||||||
|
SAMStatTaskSetFull = SAMStat{SAM_STAT_TASK_SET_FULL, errors.New("task set full")}
|
||||||
|
SAMStatAcaActive = SAMStat{SAM_STAT_ACA_ACTIVE, errors.New("aca active")}
|
||||||
|
SAMStatTaskAborted = SAMStat{SAM_STAT_TASK_ABORTED, errors.New("task aborted")}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -59,7 +79,7 @@ var (
|
|||||||
TYPE_PT SCSIDeviceType = 0xff
|
TYPE_PT SCSIDeviceType = 0xff
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandFunc func(host int, cmd *SCSICommand) error
|
type CommandFunc func(host int, cmd *SCSICommand) SAMStat
|
||||||
|
|
||||||
type SCSIServiceAction struct {
|
type SCSIServiceAction struct {
|
||||||
ServiceAction uint32
|
ServiceAction uint32
|
||||||
@@ -92,3 +112,33 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
|
|||||||
PRConflictBits: pr,
|
PRConflictBits: pr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BuildSenseData(cmd *SCSICommand, key byte, asc SCSISubError) {
|
||||||
|
senseBuffer := cmd.SenseBuffer
|
||||||
|
if cmd.Device.Attrs.SenseFormat {
|
||||||
|
// descriptor format
|
||||||
|
// current, not deferred
|
||||||
|
senseBuffer.WriteByte(0x72)
|
||||||
|
senseBuffer.WriteByte(key)
|
||||||
|
senseBuffer.WriteByte((byte(asc) >> 8) & 0xff)
|
||||||
|
senseBuffer.WriteByte(byte(asc) & 0xff)
|
||||||
|
cmd.SenseLength = 8
|
||||||
|
} else {
|
||||||
|
// fixed format
|
||||||
|
var length uint32 = 0xa
|
||||||
|
// current, not deferred
|
||||||
|
senseBuffer.WriteByte(0x70)
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
|
senseBuffer.WriteByte(key)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
senseBuffer.WriteByte(byte(length))
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
senseBuffer.WriteByte((byte(asc) >> 8) & 0xff)
|
||||||
|
senseBuffer.WriteByte(byte(asc) & 0xff)
|
||||||
|
cmd.SenseLength = length + 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
277
pkg/scsi/spc.go
277
pkg/scsi/spc.go
@@ -17,6 +17,14 @@ limitations under the License.
|
|||||||
// SCSI primary command processing
|
// SCSI primary command processing
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Protocol Identifier Values
|
* Protocol Identifier Values
|
||||||
*
|
*
|
||||||
@@ -100,78 +108,275 @@ const (
|
|||||||
DESG_SCSI
|
DESG_SCSI
|
||||||
)
|
)
|
||||||
|
|
||||||
func SPCIllegalOp(host int, cmd *SCSICommand) error {
|
func SPCIllegalOp(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
return SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
func SPCLuOffline(lu *SCSILu) error {
|
||||||
|
lu.Attrs.Online = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCInquiry(host int, cmd *SCSICommand) error {
|
func SPCLuOnline(lu *SCSILu) error {
|
||||||
|
if luPreventRemoval(lu) {
|
||||||
|
return fmt.Errorf("lu(%s) prevent removal", lu.Lun)
|
||||||
|
}
|
||||||
|
|
||||||
|
lu.Attrs.Online = false
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCReportLuns(host int, cmd *SCSICommand) error {
|
func SPCInquiry(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCStartStop(host int, cmd *SCSICommand) error {
|
func SPCReportLuns(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
var (
|
||||||
|
remainLength uint32
|
||||||
|
actualLength uint32 = 8
|
||||||
|
availLength uint32 = 0
|
||||||
|
allocationLength uint32
|
||||||
|
data *bytes.Buffer
|
||||||
|
scb *bytes.Buffer = cmd.SCB
|
||||||
|
)
|
||||||
|
// Get Allocation Length
|
||||||
|
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
|
||||||
|
if allocationLength < 16 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if cmd.InSDBBuffer.Length < allocationLength {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
data = cmd.InSDBBuffer.Buffer
|
||||||
|
remainLength = allocationLength - 8
|
||||||
|
availLength = 8 * uint32(len(cmd.Target.Devices))
|
||||||
|
binary.Write(data, binary.BigEndian, availLength)
|
||||||
|
cmd.InSDBBuffer.Resid = int32(actualLength)
|
||||||
|
// Skip through to byte 8, Reserved
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
data.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, lu := range cmd.Target.Devices {
|
||||||
|
if remainLength > 0 {
|
||||||
|
lun := lu.Lun
|
||||||
|
if lun > 0xff {
|
||||||
|
lun = 0x1 << 30
|
||||||
|
} else {
|
||||||
|
lun = 0
|
||||||
|
}
|
||||||
|
lun = (0x3fff & lun) << 16
|
||||||
|
lun = uint64(lun << 32)
|
||||||
|
binary.Write(data, binary.BigEndian, lun)
|
||||||
|
remainLength -= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCTestUnit(host int, cmd *SCSICommand) error {
|
func SPCStartStop(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
var (
|
||||||
|
pwrcnd, loej, start byte
|
||||||
|
)
|
||||||
|
if err := deviceReserve(cmd); err != nil {
|
||||||
|
return SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
scb := cmd.SCB.Bytes()
|
||||||
|
pwrcnd = scb[4] & 0xf0
|
||||||
|
if pwrcnd != 0 {
|
||||||
|
return SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
loej = scb[4] & 0x02
|
||||||
|
start = scb[4] & 0x01
|
||||||
|
|
||||||
|
if loej != 0 && start == 0 && cmd.Device.Attrs.Removable {
|
||||||
|
if luPreventRemoval(cmd.Device) {
|
||||||
|
if cmd.Device.Attrs.Online {
|
||||||
|
// online == media is present
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_MEDIUM_REMOVAL_PREVENTED)
|
||||||
|
} else {
|
||||||
|
// !online == media is not present
|
||||||
|
BuildSenseData(cmd, NOT_READY, ASC_MEDIUM_REMOVAL_PREVENTED)
|
||||||
|
}
|
||||||
|
return SAMStatCheckCondition
|
||||||
|
}
|
||||||
|
SPCLuOffline(cmd.Device)
|
||||||
|
}
|
||||||
|
if loej != 0 && start != 0 && cmd.Device.Attrs.Removable {
|
||||||
|
SPCLuOnline(cmd.Device)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPreventAllowMediaRemoval(host int, cmd *SCSICommand) error {
|
func SPCTestUnit(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
if err := deviceReserve(cmd); err != nil {
|
||||||
|
return SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
if cmd.Device.Attrs.Online {
|
||||||
|
return SAMStatGood
|
||||||
|
}
|
||||||
|
if cmd.Device.Attrs.Removable {
|
||||||
|
BuildSenseData(cmd, NOT_READY, ASC_MEDIUM_NOT_PRESENT)
|
||||||
|
} else {
|
||||||
|
BuildSenseData(cmd, NOT_READY, ASC_BECOMING_READY)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCModeSense(host int, cmd *SCSICommand) error {
|
func SPCPreventAllowMediaRemoval(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
if err := deviceReserve(cmd); err != nil {
|
||||||
|
return SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
// PREVENT_MASK = 0x03
|
||||||
|
cmd.ITNexusLuInfo.Prevent = int(cmd.SCB.Bytes()[4] & 0x03)
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCServiceAction(host int, cmd *SCSICommand) error {
|
// SPCModeSense Implement SCSI op MODE SENSE(6) and MODE SENSE(10)
|
||||||
return nil
|
// Reference : SPC4r11
|
||||||
|
// 6.11 - MODE SENSE(6)
|
||||||
|
// 6.12 - MODE SENSE(10)
|
||||||
|
func SPCModeSense(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReadKeys(host int, cmd *SCSICommand) error {
|
func SPCSendDiagnostics(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
// we only support SELF-TEST==1
|
||||||
|
if cmd.SCB.Bytes()[1]&0x04 == 0 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
return SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReadReservation(host int, cmd *SCSICommand) error {
|
// This is useful for the various commands using the SERVICE ACTION format.
|
||||||
return nil
|
func SPCServiceAction(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
// TODO
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReportCapabilities(host int, cmd *SCSICommand) error {
|
func SPCPRReadKeys(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
allocationLength := util.GetUnalignedUint32(cmd.SCB.Bytes()[7:9])
|
||||||
|
if allocationLength < 8 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if cmd.InSDBBuffer.Length < allocationLength {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRRegister(host int, cmd *SCSICommand) error {
|
func SPCPRReadReservation(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReserve(host int, cmd *SCSICommand) error {
|
func SPCPRReportCapabilities(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
var (
|
||||||
|
buf []byte = make([]byte, 8)
|
||||||
|
availLength uint32 = 8
|
||||||
|
actualLength uint32 = 0
|
||||||
|
data *bytes.Buffer = cmd.InSDBBuffer.Buffer
|
||||||
|
)
|
||||||
|
allocationLength := util.GetUnalignedUint32(cmd.SCB.Bytes()[7:9])
|
||||||
|
if allocationLength < 8 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if cmd.InSDBBuffer.Length < allocationLength {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(buf[0:2], uint16(8))
|
||||||
|
// Persistent Reservation Type Mask format
|
||||||
|
// Type Mask Valid (TMV)
|
||||||
|
buf[3] |= 0x80
|
||||||
|
// PR_TYPE_EXCLUSIVE_ACCESS_ALLREG
|
||||||
|
buf[4] |= 0x80
|
||||||
|
// PR_TYPE_EXCLUSIVE_ACCESS_REGONLY
|
||||||
|
buf[4] |= 0x40
|
||||||
|
// PR_TYPE_WRITE_EXCLUSIVE_REGONLY
|
||||||
|
buf[4] |= 0x20
|
||||||
|
// PR_TYPE_EXCLUSIVE_ACCESS
|
||||||
|
buf[4] |= 0x08
|
||||||
|
// PR_TYPE_WRITE_EXCLUSIVE
|
||||||
|
buf[4] |= 0x02
|
||||||
|
// PR_TYPE_EXCLUSIVE_ACCESS_ALLREG
|
||||||
|
buf[5] |= 0x01
|
||||||
|
|
||||||
|
if err := binary.Write(data, binary.BigEndian, buf); err != nil {
|
||||||
|
goto sense
|
||||||
|
} else {
|
||||||
|
actualLength = availLength
|
||||||
|
}
|
||||||
|
cmd.InSDBBuffer.Resid = int32(actualLength)
|
||||||
|
return SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRRelease(host int, cmd *SCSICommand) error {
|
func SPCPRRegister(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRClear(host int, cmd *SCSICommand) error {
|
func SPCPRReserve(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRPreempt(host int, cmd *SCSICommand) error {
|
func SPCPRRelease(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRRegisterAndMove(host int, cmd *SCSICommand) error {
|
func SPCPRClear(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCRequestSense(host int, cmd *SCSICommand) error {
|
func SPCPRPreempt(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCSendDiagnostics(host int, cmd *SCSICommand) error {
|
func SPCPRRegisterAndMove(host int, cmd *SCSICommand) SAMStat {
|
||||||
return nil
|
return SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
func SPCRequestSense(host int, cmd *SCSICommand) SAMStat {
|
||||||
|
var (
|
||||||
|
allocationLength uint32
|
||||||
|
actualLength uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
|
||||||
|
if allocationLength > cmd.InSDBBuffer.Length {
|
||||||
|
allocationLength = cmd.InSDBBuffer.Length
|
||||||
|
}
|
||||||
|
BuildSenseData(cmd, NO_SENSE, NO_ADDITIONAL_SENSE)
|
||||||
|
if cmd.SenseLength < allocationLength {
|
||||||
|
actualLength = cmd.SenseLength
|
||||||
|
} else {
|
||||||
|
actualLength = allocationLength
|
||||||
|
}
|
||||||
|
binary.Write(cmd.InSDBBuffer.Buffer, binary.BigEndian, cmd.SenseBuffer.Bytes()[0:actualLength])
|
||||||
|
cmd.InSDBBuffer.Resid = int32(actualLength)
|
||||||
|
|
||||||
|
// reset sense buffer in cmnd
|
||||||
|
cmd.SenseBuffer = &bytes.Buffer{}
|
||||||
|
cmd.SenseLength = 0
|
||||||
|
|
||||||
|
return SAMStatGood
|
||||||
}
|
}
|
||||||
|
|||||||
73
pkg/scsi/spc_test.go
Normal file
73
pkg/scsi/spc_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SCSI primary command processing test
|
||||||
|
package scsi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test SPCReportLuns function
|
||||||
|
func TestSPCReportLuns(t *testing.T) {
|
||||||
|
// make a fake REPORT_LUNS command
|
||||||
|
cmd := new(SCSICommand)
|
||||||
|
device := new(SCSILu)
|
||||||
|
cmd.Device = device
|
||||||
|
lu := new(SCSILu)
|
||||||
|
target := new(SCSITarget)
|
||||||
|
target.Devices = append(target.Devices, *lu)
|
||||||
|
cmd.Target = target
|
||||||
|
cmd.SCB = &bytes.Buffer{}
|
||||||
|
cmd.SenseBuffer = &bytes.Buffer{}
|
||||||
|
cmd.InSDBBuffer.Length = 16
|
||||||
|
cmd.InSDBBuffer.Buffer = &bytes.Buffer{}
|
||||||
|
cmd.SCB.WriteByte(byte(REPORT_LUNS))
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
cmd.SCB.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
binary.Write(cmd.SCB, binary.BigEndian, uint32(16))
|
||||||
|
|
||||||
|
if err := SPCReportLuns(0, cmd); err.Err != nil {
|
||||||
|
t.Errorf("Expected not error, but got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.InSDBBuffer.Length = 10
|
||||||
|
if err := SPCReportLuns(0, cmd); err.Err == nil {
|
||||||
|
t.Error("Expected error, but got nothing")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.SCB = &bytes.Buffer{}
|
||||||
|
cmd.SCB.WriteByte(byte(REPORT_LUNS))
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
cmd.SCB.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
binary.Write(cmd.SCB, binary.BigEndian, uint32(10))
|
||||||
|
if err := SPCReportLuns(0, cmd); err.Err == nil {
|
||||||
|
t.Error("Expected error, but got nothing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSPCStartStop(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSPCTestUnit(t *testing.T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSPCPreventAllowMediaRemoval(t *testing.T) {
|
||||||
|
}
|
||||||
@@ -16,6 +16,12 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
)
|
||||||
|
|
||||||
type SCSITargetState int
|
type SCSITargetState int
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,9 +38,52 @@ const (
|
|||||||
PR_EA_FN = (1 << 0)
|
PR_EA_FN = (1 << 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCSITarget struct {
|
type ITNexus struct {
|
||||||
Name string
|
ID uint64
|
||||||
TID int
|
Ctime uint64
|
||||||
LID int
|
Commands []SCSICommand
|
||||||
State SCSITargetState
|
Target *SCSITarget
|
||||||
|
Host int
|
||||||
|
Info string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ITNexusLuInfo struct {
|
||||||
|
Lu *SCSILu
|
||||||
|
ID uint64
|
||||||
|
Prevent int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCSITarget struct {
|
||||||
|
Name string
|
||||||
|
TID int
|
||||||
|
LID int
|
||||||
|
State SCSITargetState
|
||||||
|
Devices []SCSILu
|
||||||
|
ITNexus []ITNexus
|
||||||
|
}
|
||||||
|
|
||||||
|
func deviceReserve(cmd *SCSICommand) error {
|
||||||
|
var lu *SCSILu
|
||||||
|
for _, dev := range cmd.Target.Devices {
|
||||||
|
if dev.Lun == cmd.Device.Lun {
|
||||||
|
lu = &dev
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lu == nil {
|
||||||
|
glog.Errorf("invalid target and lun %d %s", cmd.Target.TID, cmd.Device.Lun)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if lu.ReserveID != 0 && lu.ReserveID != cmd.CommandITNID {
|
||||||
|
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.CommandITNID)
|
||||||
|
return fmt.Errorf("already reserved")
|
||||||
|
}
|
||||||
|
lu.ReserveID = cmd.CommandITNID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deviceRelease(tid int, itn, lun uint64, force bool) error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ limitations under the License.
|
|||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
func GetUnalignedUint16(u8 []uint8) uint16 {
|
||||||
|
return binary.BigEndian.Uint16(u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUnalignedUint32(u8 []uint8) uint32 {
|
||||||
|
return binary.BigEndian.Uint32(u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUnalignedUint64(u8 []uint8) uint64 {
|
||||||
|
return binary.BigEndian.Uint64(u8)
|
||||||
|
}
|
||||||
|
|
||||||
// ParseKVText parses iSCSI key value data.
|
// ParseKVText parses iSCSI key value data.
|
||||||
func ParseKVText(txt []byte) map[string]string {
|
func ParseKVText(txt []byte) map[string]string {
|
||||||
m := make(map[string]string)
|
m := make(map[string]string)
|
||||||
|
|||||||
Reference in New Issue
Block a user