init commit for spc/sbc

This commit is contained in:
Lei Xue
2015-12-27 20:44:54 +08:00
parent e7366b4ed1
commit d770eb33ac
11 changed files with 788 additions and 124 deletions

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
View 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) {
}

View File

@@ -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
}
}

View File

@@ -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
View 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) {
}

View File

@@ -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
}

View File

@@ -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)