init commit for spc/sbc
This commit is contained in:
@@ -16,6 +16,8 @@ limitations under the License.
|
||||
|
||||
package scsi
|
||||
|
||||
import "bytes"
|
||||
|
||||
type SCSICommandType byte
|
||||
|
||||
var (
|
||||
@@ -185,8 +187,8 @@ const (
|
||||
)
|
||||
|
||||
type SCSIDataBuffer struct {
|
||||
Buffer uint64
|
||||
Length uint64
|
||||
Buffer *bytes.Buffer
|
||||
Length uint32
|
||||
TransferLength uint32
|
||||
Resid int32
|
||||
}
|
||||
@@ -197,20 +199,22 @@ type SCSICommand struct {
|
||||
Device *SCSILu
|
||||
State uint64
|
||||
Direction SCSIDataDirection
|
||||
InSDBBuffer *SCSIDataBuffer
|
||||
OutSDBBuffer *SCSIDataBuffer
|
||||
InSDBBuffer SCSIDataBuffer
|
||||
OutSDBBuffer SCSIDataBuffer
|
||||
// Command ITN ID
|
||||
CommandITNID uint64
|
||||
Offset uint64
|
||||
TL uint32
|
||||
SCB *[]byte
|
||||
SCBLength int
|
||||
Lun []uint8
|
||||
Attribute int
|
||||
Tag uint64
|
||||
Result int
|
||||
SenseBuffer []byte
|
||||
SenseLength int
|
||||
CommandITNID uint64
|
||||
Offset uint64
|
||||
TL uint32
|
||||
SCB *bytes.Buffer
|
||||
SCBLength int
|
||||
Lun []uint8
|
||||
Attribute int
|
||||
Tag uint64
|
||||
Result int
|
||||
SenseBuffer *bytes.Buffer
|
||||
SenseLength uint32
|
||||
ITNexus *ITNexus
|
||||
ITNexusLuInfo *ITNexusLuInfo
|
||||
}
|
||||
|
||||
func SCSICDBGroupID(opcode byte) byte {
|
||||
|
||||
@@ -47,7 +47,7 @@ type SCSITargetDriverOps interface {
|
||||
DestroyPortal(name string) error
|
||||
CreateLu(lu *SCSILu) error
|
||||
|
||||
GetLun(lun uint8) (uint64, error)
|
||||
GetLu(lun uint8) (uint64, error)
|
||||
CommandNotify(nid uint64, result int, cmd *SCSICommand) error
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,43 @@ limitations under the License.
|
||||
|
||||
package scsi
|
||||
|
||||
type SCSIError byte
|
||||
import "errors"
|
||||
|
||||
type SCSIError struct {
|
||||
errno byte
|
||||
Err error
|
||||
}
|
||||
|
||||
var (
|
||||
NO_SENSE SCSIError = 0x00
|
||||
RECOVERED_ERROR SCSIError = 0x01
|
||||
NOT_READY SCSIError = 0x02
|
||||
MEDIUM_ERROR SCSIError = 0x03
|
||||
HARDWARE_ERROR SCSIError = 0x04
|
||||
ILLEGAL_REQUEST SCSIError = 0x05
|
||||
UNIT_ATTENTION SCSIError = 0x06
|
||||
DATA_PROTECT SCSIError = 0x07
|
||||
BLANK_CHECK SCSIError = 0x08
|
||||
COPY_ABORTED SCSIError = 0x0a
|
||||
ABORTED_COMMAND SCSIError = 0x0b
|
||||
VOLUME_OVERFLOW SCSIError = 0x0d
|
||||
MISCOMPARE SCSIError = 0x0e
|
||||
NO_SENSE byte = 0x00
|
||||
RECOVERED_ERROR byte = 0x01
|
||||
NOT_READY byte = 0x02
|
||||
MEDIUM_ERROR byte = 0x03
|
||||
HARDWARE_ERROR byte = 0x04
|
||||
ILLEGAL_REQUEST byte = 0x05
|
||||
UNIT_ATTENTION byte = 0x06
|
||||
DATA_PROTECT byte = 0x07
|
||||
BLANK_CHECK byte = 0x08
|
||||
COPY_ABORTED byte = 0x0a
|
||||
ABORTED_COMMAND byte = 0x0b
|
||||
VOLUME_OVERFLOW byte = 0x0d
|
||||
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
|
||||
|
||||
@@ -16,14 +16,58 @@ limitations under the License.
|
||||
|
||||
package scsi
|
||||
|
||||
type SCSILu struct {
|
||||
FD int
|
||||
Address uint64
|
||||
Size uint64
|
||||
Lun uint64
|
||||
Path string
|
||||
BsoFlags int
|
||||
BlockShift uint
|
||||
ReserveID uint64
|
||||
Target *SCSITarget
|
||||
type SCSILuPhyAttribute struct {
|
||||
SCSIID string
|
||||
SCSISN string
|
||||
NumID uint64
|
||||
VendorID string
|
||||
ProductID string
|
||||
ProductRev string
|
||||
VersionDesction []uint16
|
||||
// Peripheral device type
|
||||
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
|
||||
package scsi
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
type SBCSCSIDeviceProtocol struct {
|
||||
BaseSCSIDeviceProtocol
|
||||
}
|
||||
@@ -37,10 +39,12 @@ func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSBCDevice() (SBCSCSIDeviceProtocol, error) {
|
||||
func NewSBCDevice() SBCSCSIDeviceProtocol {
|
||||
var sbc = SBCSCSIDeviceProtocol{
|
||||
BaseSCSIDeviceProtocol{Type: TYPE_DISK,
|
||||
SCSIDeviceOps: make([]SCSIDeviceOperation, 256)},
|
||||
BaseSCSIDeviceProtocol{
|
||||
Type: TYPE_DISK,
|
||||
SCSIDeviceOps: make([]SCSIDeviceOperation, 256),
|
||||
},
|
||||
}
|
||||
for i := 0; i <= 256; i++ {
|
||||
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[VERIFY_12] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN)
|
||||
|
||||
return sbc, nil
|
||||
return sbc
|
||||
}
|
||||
|
||||
func SBCModeSelect(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCModeSelect(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCModeSense(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCModeSense(host int, cmd *SCSICommand) SAMStat {
|
||||
// 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 {
|
||||
return nil
|
||||
// The FORMAT UNIT command requests that the device server format the medium into application client
|
||||
// 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 {
|
||||
return nil
|
||||
func SBCUnmap(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCReadWrite(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCReadWrite(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCReserve(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCReserve(host int, cmd *SCSICommand) SAMStat {
|
||||
if err := deviceReserve(cmd); err != nil {
|
||||
return SAMStatReservationConflict
|
||||
}
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCRelease(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCRelease(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
// The READ CAPACITY (10) command requests that the device server transfer 8 bytes of parameter data
|
||||
// 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 {
|
||||
return nil
|
||||
// The VERIFY (10) command requests that the device server verify the specified logical block(s) on the medium.
|
||||
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 {
|
||||
return nil
|
||||
func SBCReadCapacity16(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCGetLbaStatus(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCGetLbaStatus(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCServiceAction(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SBCServiceAction(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SBCSyncCache(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
// The SYNCHRONIZE CACHE (10) command requests that the device server ensure that
|
||||
// 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
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
DefaultBlockShift int = 9
|
||||
DefaultSenseBufferSize int = 252
|
||||
)
|
||||
|
||||
type SAMStat byte
|
||||
type SCSIDeviceType byte
|
||||
|
||||
var (
|
||||
SAM_STAT_GOOD SAMStat = 0x00
|
||||
SAM_STAT_CHECK_CONDITION SAMStat = 0x02
|
||||
SAM_STAT_CONDITION_MET SAMStat = 0x04
|
||||
SAM_STAT_BUSY SAMStat = 0x08
|
||||
SAM_STAT_INTERMEDIATE SAMStat = 0x10
|
||||
SAM_STAT_INTERMEDIATE_CONDITION_MET SAMStat = 0x14
|
||||
SAM_STAT_RESERVATION_CONFLICT SAMStat = 0x18
|
||||
SAM_STAT_COMMAND_TERMINATED SAMStat = 0x22
|
||||
SAM_STAT_TASK_SET_FULL SAMStat = 0x28
|
||||
SAM_STAT_ACA_ACTIVE SAMStat = 0x30
|
||||
SAM_STAT_TASK_ABORTED SAMStat = 0x40
|
||||
SAM_STAT_GOOD byte = 0x00
|
||||
SAM_STAT_CHECK_CONDITION byte = 0x02
|
||||
SAM_STAT_CONDITION_MET byte = 0x04
|
||||
SAM_STAT_BUSY byte = 0x08
|
||||
SAM_STAT_INTERMEDIATE byte = 0x10
|
||||
SAM_STAT_INTERMEDIATE_CONDITION_MET byte = 0x14
|
||||
SAM_STAT_RESERVATION_CONFLICT byte = 0x18
|
||||
SAM_STAT_COMMAND_TERMINATED byte = 0x22
|
||||
SAM_STAT_TASK_SET_FULL byte = 0x28
|
||||
SAM_STAT_ACA_ACTIVE byte = 0x30
|
||||
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 (
|
||||
@@ -59,7 +79,7 @@ var (
|
||||
TYPE_PT SCSIDeviceType = 0xff
|
||||
)
|
||||
|
||||
type CommandFunc func(host int, cmd *SCSICommand) error
|
||||
type CommandFunc func(host int, cmd *SCSICommand) SAMStat
|
||||
|
||||
type SCSIServiceAction struct {
|
||||
ServiceAction uint32
|
||||
@@ -92,3 +112,33 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
|
||||
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
|
||||
package scsi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
/*
|
||||
* Protocol Identifier Values
|
||||
*
|
||||
@@ -100,78 +108,275 @@ const (
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func SPCReportLuns(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCInquiry(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCStartStop(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCReportLuns(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
func SPCStartStop(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
func SPCTestUnit(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
func SPCPreventAllowMediaRemoval(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
// SPCModeSense Implement SCSI op MODE SENSE(6) and MODE SENSE(10)
|
||||
// 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 {
|
||||
return nil
|
||||
func SPCSendDiagnostics(host int, cmd *SCSICommand) SAMStat {
|
||||
// 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 {
|
||||
return nil
|
||||
// This is useful for the various commands using the SERVICE ACTION format.
|
||||
func SPCServiceAction(host int, cmd *SCSICommand) SAMStat {
|
||||
// TODO
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCPRReportCapabilities(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRReadKeys(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
func SPCPRReadReservation(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCPRReserve(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRReportCapabilities(host int, cmd *SCSICommand) SAMStat {
|
||||
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 {
|
||||
return nil
|
||||
func SPCPRRegister(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCPRClear(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRReserve(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCPRPreempt(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRRelease(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCPRRegisterAndMove(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRClear(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCRequestSense(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRPreempt(host int, cmd *SCSICommand) SAMStat {
|
||||
return SAMStatGood
|
||||
}
|
||||
|
||||
func SPCSendDiagnostics(host int, cmd *SCSICommand) error {
|
||||
return nil
|
||||
func SPCPRRegisterAndMove(host int, cmd *SCSICommand) SAMStat {
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type SCSITargetState int
|
||||
|
||||
var (
|
||||
@@ -32,9 +38,52 @@ const (
|
||||
PR_EA_FN = (1 << 0)
|
||||
)
|
||||
|
||||
type SCSITarget struct {
|
||||
Name string
|
||||
TID int
|
||||
LID int
|
||||
State SCSITargetState
|
||||
type ITNexus struct {
|
||||
ID uint64
|
||||
Ctime uint64
|
||||
Commands []SCSICommand
|
||||
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
|
||||
|
||||
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.
|
||||
func ParseKVText(txt []byte) map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
Reference in New Issue
Block a user