fix bugs and make it run basically

This commit is contained in:
Lei Xue
2016-08-27 20:07:51 +08:00
parent f97ab027de
commit aedc09021f
22 changed files with 1997 additions and 431 deletions
+6 -13
View File
@@ -1,5 +1,5 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Copyright 2016 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.
@@ -18,6 +18,7 @@ package scsi
import (
"fmt"
"os"
"github.com/gostor/gotgt/pkg/api"
)
@@ -28,15 +29,7 @@ type BaseBackingStore struct {
OflagsSupported int
}
type BackingStore interface {
Open(dev *api.SCSILu, path string, fd *int, size *uint64) error
Close(dev *api.SCSILu) error
Init(dev *api.SCSILu, Opts string) error
Exit(dev *api.SCSILu) error
CommandSubmit(cmd *api.SCSICommand) error
}
type BackingStoreFunc func() (BackingStore, error)
type BackingStoreFunc func() (api.BackingStore, error)
var registeredPlugins = map[string](BackingStoreFunc){}
@@ -44,7 +37,7 @@ func RegisterBackingStore(name string, f BackingStoreFunc) {
registeredPlugins[name] = f
}
func NewBackingStore(name string) (BackingStore, error) {
func NewBackingStore(name string) (api.BackingStore, error) {
if name == "" {
return nil, nil
}
@@ -59,8 +52,8 @@ type fakeBackingStore struct {
BaseBackingStore
}
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string, fd *int, size *uint64) error {
return nil
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
return nil, nil
}
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
+165 -17
View File
@@ -1,5 +1,5 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Copyright 2016 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.
@@ -16,40 +16,188 @@ limitations under the License.
package backingstore
import "github.com/gostor/gotgt/pkg/scsi"
import (
"bytes"
"fmt"
"os"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi"
"github.com/gostor/gotgt/pkg/util"
)
func init() {
scsi.RegisterBackingStore("file", new)
}
type FileBackingStore struct {
BaseBackingStore
scsi.BaseBackingStore
}
func new() (scsi.BackingStore, error) {
return NullBackingStore{
Name: "file",
DataSize: 0,
OflagsSupported: 0,
func new() (api.BackingStore, error) {
return &FileBackingStore{
BaseBackingStore: scsi.BaseBackingStore{
Name: "file",
DataSize: 0,
OflagsSupported: 0,
},
}, nil
}
func (bs *FileBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm)
if err != nil {
return nil, err
}
return f, nil
}
func (bs *FileBackingStore) Close(dev *api.SCSILu) error {
return dev.File.Close()
}
func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error {
return nil
}
func (bs *FileBackingStore) Close(dev *SCSILu) error {
func (bs *FileBackingStore) Exit(dev *api.SCSILu) error {
return nil
}
func (bs *FileBackingStore) Init(dev *SCSILu, Opts string) error {
return nil
}
func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
var (
scb = cmd.SCB.Bytes()
offset = cmd.Offset
opcode = api.SCSICommandType(scb[0])
lu = cmd.Device
key = scsi.ILLEGAL_REQUEST
asc = scsi.ASC_INVALID_FIELD_IN_CDB
wbuf []byte = []byte{}
rbuf []byte = []byte{}
length int
doVerify bool = false
doWrite bool = false
)
switch opcode {
case api.ORWRITE_16:
tmpbuf := []byte{}
length, err = lu.File.ReadAt(tmpbuf, int64(offset))
if length != len(tmpbuf) {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
break
}
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(tmpbuf)
func (bs *FileBackingStore) Exit(dev *SCSILu) error {
return nil
}
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
doWrite = true
goto write
case api.COMPARE_AND_WRITE:
// TODO
doWrite = true
goto write
case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16:
break
case api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
doVerify = true
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16:
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
doWrite = true
goto write
case api.WRITE_SAME, api.WRITE_SAME_16:
// TODO
break
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
length, err = lu.File.ReadAt(rbuf, int64(offset))
if err != nil {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
break
}
for i := 0; i < int(cmd.TL)-length; i++ {
rbuf = append(rbuf, 0)
}
if (opcode != api.READ_6) && (scb[1]&0x10 != 0) {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
}
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf)
case api.PRE_FETCH_10, api.PRE_FETCH_16:
err = util.Fadvise(lu.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED)
if err != nil {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
}
case api.VERIFY_10, api.VERIFY_12, api.VERIFY_16:
doVerify = true
goto verify
case api.UNMAP:
// TODO
default:
break
}
write:
if doWrite {
// hack: wbuf = []byte("hello world!")
length, err = lu.File.WriteAt(wbuf, int64(offset))
if err != nil || length != len(wbuf) {
glog.Error(err)
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
goto sense
}
glog.V(2).Infof("write data at %d for length %d", offset, length)
var pg *api.ModePage
for _, p := range lu.ModePages {
if p.Pcode == 0x08 && p.SubPcode == 0 {
pg = &p
break
}
}
if pg == nil {
key = scsi.ILLEGAL_REQUEST
asc = scsi.ASC_INVALID_FIELD_IN_CDB
goto sense
}
if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) {
if err = util.Fdatasync(lu.File); err != nil {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
goto sense
}
}
if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
}
}
verify:
if doVerify {
length, err = lu.File.ReadAt(rbuf, int64(offset))
if length != len(rbuf) {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
goto sense
}
if !bytes.Equal(cmd.OutSDBBuffer.Buffer.Bytes(), rbuf) {
err = fmt.Errorf("verify fail between out buffer and read buffer")
key = scsi.MISCOMPARE
asc = scsi.ASC_MISCOMPARE_DURING_VERIFY_OPERATION
goto sense
}
if scb[1]&0x10 != 0 {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
}
}
glog.Infof("io done %s", string(scb))
sense:
if err != nil {
glog.Error(err)
return err
}
_ = key
_ = asc
func (bs *FileBackingStore) CommandSubmit(cmd *SCSICommand) error {
return nil
}
+21 -19
View File
@@ -17,45 +17,47 @@ limitations under the License.
package backingstore
import (
"github.com/gostor/gotgt/pkg/scsi"
"os"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi"
)
func init() {
scsi.RegisterBackingStore("null", new)
scsi.RegisterBackingStore("null", newNull)
}
type NullBackingStore struct {
BaseBackingStore
scsi.BaseBackingStore
}
func new() (scsi.BackingStore, error) {
return NullBackingStore{
Name: "null",
DataSize: 0,
OflagsSupported: 0,
func newNull() (api.BackingStore, error) {
return &NullBackingStore{
BaseBackingStore: scsi.BaseBackingStore{
Name: "null",
DataSize: 0,
OflagsSupported: 0,
},
}, nil
}
func (bs *NullBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
glog.V(1).Infof("NULL backing store open, size: %d", size)
func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
return nil, nil
}
func (bs *NullBackingStore) Close(dev *api.SCSILu) error {
return nil
}
func (bs *NullBackingStore) Close(dev *SCSILu) error {
func (bs *NullBackingStore) Init(dev *api.SCSILu, Opts string) error {
return nil
}
func (bs *NullBackingStore) Init(dev *SCSILu, Opts string) error {
func (bs *NullBackingStore) Exit(dev *api.SCSILu) error {
return nil
}
func (bs *NullBackingStore) Exit(dev *SCSILu) error {
return nil
}
func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error {
cmd.Result = SAM_STAT_GOOD
func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
cmd.Result = api.SAM_STAT_GOOD
return nil
}
+42 -10
View File
@@ -16,19 +16,51 @@ limitations under the License.
package scsi
import "github.com/gostor/gotgt/pkg/api"
import (
"os"
type SCSILuOps struct {
*api.SCSILu
"github.com/gostor/gotgt/pkg/api"
)
DeviceProtocol SCSIDeviceProtocol
Storage *BackingStore
Target *api.SCSITarget
Attrs api.SCSILuPhyAttribute
func NewSCSILu(lun uint64, target *api.SCSITarget) (*api.SCSILu, error) {
sbc := NewSBCDevice()
backing, err := NewBackingStore("file")
if err != nil {
return nil, err
}
var lu = &api.SCSILu{
Lun: lun,
Target: target,
PerformCommand: luPerformCommand,
DeviceProtocol: sbc,
Storage: backing,
BlockShift: 0,
Size: 1024 * 1024 * 1024,
}
// hack this
if _, err = os.Stat("/tmp/data.txt"); err != nil && os.IsExist(err) {
os.Create("/tmp/data.txt")
}
f, err := backing.Open(lu, "/tmp/data.txt")
if err != nil {
return nil, err
}
lu.File = f
lu.DeviceProtocol.InitLu(lu)
lu.Attrs.Online = true
return lu, nil
}
// function handler for command performing and finishing
PerformCommand CommandFunc
FinishCommand func(*api.SCSITarget, *api.SCSICommand)
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
op := int(cmd.SCB.Bytes()[0])
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
if fn != nil {
fnop := fn.(SCSIDeviceOperation)
// host := cmd.ITNexus.Host
host := 0
return fnop.CommandPerformFunc(host, cmd)
}
return api.SAMStatGood
}
func luPreventRemoval(lu *api.SCSILu) bool {
+224 -27
View File
@@ -18,9 +18,13 @@ limitations under the License.
package scsi
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
const (
@@ -36,30 +40,75 @@ type SBCSCSIDeviceProtocol struct {
BaseSCSIDeviceProtocol
}
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
return nil
func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
return sbc.SCSIDeviceOps[opcode]
}
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
return nil
}
func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
var tgt = lu.Target
// init LU's phy attribute
lu.Attrs.DeviceType = api.TYPE_DISK
lu.Attrs.Qualifier = false
lu.Attrs.Thinprovisioning = false
lu.Attrs.Removable = false
lu.Attrs.Readonly = false
lu.Attrs.SWP = false
lu.Attrs.SenseFormat = false
lu.Attrs.VendorID = "GOSTOR"
lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR %x%d", tgt.TID, lu.Lun)
lu.Attrs.SCSISN = fmt.Sprintf("beaf%d%d", tgt.TID, lu.Lun)
lu.Attrs.ProductID = "VIRTUAL-DISK"
lu.Attrs.VersionDesction = []uint16{
0x04C0, // SBC-3 no version claimed
0x0960, // iSCSI
0x0300, // SPC-3
}
if lu.BlockShift == 0 {
lu.BlockShift = api.DefaultBlockShift
}
pages := []api.ModePage{}
// Vendor uniq - However most apps seem to call for mode page 0
pages = append(pages, api.ModePage{0, 0, []byte{}})
// Disconnect page
pages = append(pages, api.ModePage{2, 0, []byte{0x80, 0x80, 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}})
// Caching Page
pages = append(pages, api.ModePage{8, 0, []byte{0x14, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}})
// Control page
pages = append(pages, api.ModePage{0x0a, 0, []byte{2, 0x10, 0, 0, 0, 0, 0, 0, 2, 0}})
// Control Extensions mode page: TCMOS:1
pages = append(pages, api.ModePage{0x0a, 1, []byte{0x04, 0x00, 0x00}})
// Informational Exceptions Control page
pages = append(pages, api.ModePage{0x1c, 0, []byte{8, 0, 0, 0, 0, 0, 0, 0, 0, 0}})
lu.ModePages = pages
return nil
}
func NewSBCDevice() SBCSCSIDeviceProtocol {
func (sbc SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
return nil
}
func (sbc SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
return nil
}
func (sbc SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
return nil
}
func (sbc SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
return nil
}
func NewSBCDevice() api.SCSIDeviceProtocol {
var sbc = SBCSCSIDeviceProtocol{
BaseSCSIDeviceProtocol{
Type: api.TYPE_DISK,
SCSIDeviceOps: make([]SCSIDeviceOperation, 256),
SCSIDeviceOps: []SCSIDeviceOperation{},
},
}
for i := 0; i <= 256; i++ {
for i := 0; i < 256; i++ {
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
}
sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
@@ -103,9 +152,10 @@ func NewSBCDevice() SBCSCSIDeviceProtocol {
sbc.SCSIDeviceOps[api.PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SBCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
@@ -132,15 +182,18 @@ func SBCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
deviceSpecific |= 0x80
}
data := cmd.InSDBBuffer.Buffer
data.Next(2)
buf := cmd.InSDBBuffer.Buffer
data := []byte{0x00, 0x00, 0x00, 0x00}
if buf != nil {
data = buf.Bytes()
}
if cmd.SCB.Bytes()[0] == 0x1a {
data.WriteByte(deviceSpecific)
data[2] = deviceSpecific
} else {
data.Next(1)
data.WriteByte(deviceSpecific)
data[3] = deviceSpecific
}
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
return api.SAMStatGood
}
@@ -196,7 +249,135 @@ func SBCUnmap(host int, cmd *api.SCSICommand) api.SAMStat {
}
func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood
var (
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
dev = cmd.Device
scb = cmd.SCB.Bytes()
opcode = api.SCSICommandType(scb[0])
lba uint64
tl uint32
err error
)
if dev.Attrs.Removable && !dev.Attrs.Online {
key = NOT_READY
asc = ASC_MEDIUM_NOT_PRESENT
glog.Warningf("sense")
goto sense
}
switch opcode {
case api.READ_10, api.READ_12, api.READ_16, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.ORWRITE_16,
api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16, api.COMPARE_AND_WRITE:
// We only support protection information type 0
/*
if scb[1]&0xe0 != 0 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
glog.Warningf("sense")
goto sense
}
*/
if cmd.OutSDBBuffer.Buffer == nil {
cmd.OutSDBBuffer.Buffer = &bytes.Buffer{}
}
case api.WRITE_SAME, api.WRITE_SAME_16:
// We dont support resource-provisioning so ANCHOR bit == 1 is an error.
if scb[1]&0x10 != 0 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
// We only support unmap for thin provisioned LUNS
if (scb[1]&0x08 != 0) && !dev.Attrs.Thinprovisioning {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
// We only support protection information type 0
if scb[1]&0xe0 != 0 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
// LBDATA and PBDATA can not both be set
if (scb[1] & 0x06) == 0x06 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
}
if dev.Attrs.Readonly || dev.Attrs.SWP {
switch opcode {
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.ORWRITE_16,
api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16, api.WRITE_SAME, api.WRITE_SAME_16,
api.PRE_FETCH_10, api.PRE_FETCH_16, api.COMPARE_AND_WRITE:
key = DATA_PROTECT
asc = ASC_WRITE_PROTECT
glog.Warningf("sense")
goto sense
}
}
lba = getSCSIReadWriteOffset(scb)
tl = getSCSIReadWriteCount(scb)
// Verify that we are not doing i/o beyond the end-of-lun
if tl != 0 {
if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift {
key = ILLEGAL_REQUEST
asc = ASC_LBA_OUT_OF_RANGE
glog.Warningf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift)
goto sense
}
} else {
if lba >= dev.Size>>dev.BlockShift {
key = ILLEGAL_REQUEST
asc = ASC_LBA_OUT_OF_RANGE
glog.Warningf("sense")
goto sense
}
}
cmd.Offset = lba << dev.BlockShift
cmd.TL = tl << dev.BlockShift
// Handle residuals
switch opcode {
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
/*
if (cmd->tl != scsi_get_in_length(cmd))
scsi_set_in_resid_by_actual(cmd, cmd->tl);
*/
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
/*
if (cmd->tl != scsi_get_out_length(cmd)) {
scsi_set_out_resid_by_actual(cmd, cmd->tl);
/* We need to clamp the size of the in-buffer
* so that we dont try to write > cmd->tl in the
* backend store.
*
if (cmd->tl < scsi_get_out_length(cmd)) {
scsi_set_out_length(cmd, cmd->tl);
}
}
*/
}
err = dev.Storage.CommandSubmit(cmd)
if err != nil {
glog.Error(err)
key = HARDWARE_ERROR
asc = ASC_INTERNAL_TGT_FAILURE
} else {
return api.SAMStatGood
}
sense:
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
}
func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
@@ -224,7 +405,7 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
scb = cmd.SCB.Bytes()
key = ILLEGAL_REQUEST
asc = ASC_LUN_NOT_SUPPORTED
data = cmd.InSDBBuffer.Buffer
data = &bytes.Buffer{}
bshift = cmd.Device.BlockShift
size = cmd.Device.Size >> bshift
)
@@ -240,9 +421,11 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
goto sense
}
if cmd.InSDBBuffer.Length < 8 {
goto overflow
}
/*
if cmd.InSDBBuffer.Length < 8 {
goto overflow
}
*/
// data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
if size>>32 != 0 {
@@ -253,8 +436,9 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
// data[1] = __cpu_to_be32(1U << bshift);
binary.Write(data, binary.BigEndian, uint32(1<<bshift))
overflow:
//overflow:
cmd.InSDBBuffer.Resid = 8
cmd.InSDBBuffer.Buffer = data
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
@@ -293,6 +477,12 @@ sense:
}
func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat {
data := &bytes.Buffer{}
data.Write(util.MarshalUint64(1))
data.Write(util.MarshalUint32(1 << cmd.Device.BlockShift))
val := (cmd.Device.Attrs.Lbppbe << 16) | cmd.Device.Attrs.LowestAlignedLBA
data.Write(util.MarshalUint32(uint32(val)))
cmd.InSDBBuffer.Buffer = data
return api.SAMStatGood
}
@@ -301,6 +491,13 @@ func SBCGetLbaStatus(host int, cmd *api.SCSICommand) api.SAMStat {
}
func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
opcode := api.SCSICommandType(cmd.SCB.Bytes()[1] & 0x1f)
switch opcode {
case api.READ_CAPACITY:
return SBCReadCapacity(host, cmd)
case api.SAI_READ_CAPACITY_16:
return SBCReadCapacity16(host, cmd)
}
return api.SAMStatGood
}
+114 -13
View File
@@ -16,17 +16,82 @@ limitations under the License.
package scsi
import "github.com/gostor/gotgt/pkg/api"
import (
"bytes"
"encoding/binary"
"fmt"
"sync"
type CommandFunc func(host int, cmd *api.SCSICommand) api.SAMStat
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
)
type SCSITargetService struct {
mutex sync.RWMutex
Targets []*api.SCSITarget
}
var _instance *SCSITargetService
func NewSCSITargetService() *SCSITargetService {
if _instance == nil {
_instance = &SCSITargetService{Targets: []*api.SCSITarget{}}
}
return _instance
}
func (s *SCSITargetService) GetTargetList() ([]api.SCSITarget, error) {
result := []api.SCSITarget{}
s.mutex.RLock()
for _, t := range s.Targets {
result = append(result, *t)
}
s.mutex.RUnlock()
return result, nil
}
func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) error {
var (
target *api.SCSITarget
itn *api.ITNexus
)
s.mutex.RLock()
for _, t := range s.Targets {
if t.TID == tid {
target = t
break
}
}
s.mutex.RUnlock()
if target == nil {
return fmt.Errorf("Cannot find the target with ID(%d)", tid)
}
scmd.Target = target
for _, it := range target.ITNexus {
if it.ID == scmd.CommandITNID {
itn = it
break
}
}
scmd.ITNexus = itn
scmd.Device = target.Devices[0]
result := scmd.Device.PerformCommand(tid, scmd)
scmd.Result = result.Stat
if result.Err != nil {
glog.Error(result.Err)
return result.Err
}
return nil
}
type SCSIServiceAction struct {
ServiceAction uint32
CommandPerformFunc CommandFunc
CommandPerformFunc api.CommandFunc
}
type SCSIDeviceOperation struct {
CommandPerformFunc CommandFunc
CommandPerformFunc api.CommandFunc
ServiceAction *SCSIServiceAction
PRConflictBits uint8
}
@@ -36,15 +101,7 @@ type BaseSCSIDeviceProtocol struct {
SCSIDeviceOps []SCSIDeviceOperation
}
type SCSIDeviceProtocol interface {
InitLu(lu *api.SCSILu) error
ExitLu(lu *api.SCSILu) error
ConfigLu(lu *api.SCSILu) error
OnlineLu(lu *api.SCSILu) error
OfflineLu(lu *api.SCSILu) error
}
func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
return SCSIDeviceOperation{
CommandPerformFunc: fn,
ServiceAction: sa,
@@ -54,6 +111,9 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
senseBuffer := cmd.SenseBuffer
if senseBuffer == nil {
senseBuffer = &bytes.Buffer{}
}
if cmd.Device.Attrs.SenseFormat {
// descriptor format
// current, not deferred
@@ -81,3 +141,44 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
cmd.SenseLength = length + 8
}
}
func getSCSIReadWriteOffset(scb []byte) uint64 {
var off uint64
var opcode = api.SCSICommandType(scb[0])
switch opcode {
case api.READ_6, api.WRITE_6:
off = uint64((scb[1]&0x1f))<<16 + uint64(scb[2])<<8 + uint64(scb[3])
case api.READ_10, api.PRE_FETCH_10, api.WRITE_10, api.VERIFY_10, api.WRITE_VERIFY, api.WRITE_SAME, api.SYNCHRONIZE_CACHE, api.READ_12, api.WRITE_12, api.VERIFY_12, api.WRITE_VERIFY_12:
off = uint64(binary.BigEndian.Uint32(scb[2:]))
case api.READ_16, api.PRE_FETCH_16, api.WRITE_16, api.ORWRITE_16, api.VERIFY_16, api.WRITE_VERIFY_16, api.WRITE_SAME_16, api.SYNCHRONIZE_CACHE_16, api.COMPARE_AND_WRITE:
off = binary.BigEndian.Uint64(scb[2:])
default:
}
return off
}
func getSCSIReadWriteCount(scb []byte) uint32 {
var cnt uint32
var opcode = api.SCSICommandType(scb[0])
switch opcode {
case api.READ_6, api.WRITE_6:
cnt = uint32(scb[4])
if cnt == 0 {
cnt = 256
}
case api.READ_10, api.PRE_FETCH_10, api.WRITE_10, api.VERIFY_10, api.WRITE_VERIFY, api.WRITE_SAME, api.SYNCHRONIZE_CACHE:
cnt = uint32(scb[7])<<8 | uint32(scb[8])
case api.READ_12, api.WRITE_12, api.VERIFY_12, api.WRITE_VERIFY_12:
cnt = binary.BigEndian.Uint32(scb[6:])
case api.READ_16, api.PRE_FETCH_16, api.WRITE_16, api.ORWRITE_16, api.VERIFY_16, api.WRITE_VERIFY_16, api.WRITE_SAME_16, api.SYNCHRONIZE_CACHE_16:
cnt = binary.BigEndian.Uint32(scb[10:])
case api.COMPARE_AND_WRITE:
cnt = uint32(scb[13])
default:
}
return cnt
}
+160 -12
View File
@@ -21,7 +21,9 @@ import (
"bytes"
"encoding/binary"
"fmt"
"reflect"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
@@ -128,6 +130,39 @@ func SPCLuOnline(lu *api.SCSILu) error {
}
func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
buf := &bytes.Buffer{}
var b byte = 0x75
if reflect.DeepEqual(util.MarshalUint64(cmd.Device.Lun)[0:7], cmd.Lun[0:7]) {
b = (uint8(0) & 0x7) << 5
b |= uint8(0) & 0x1f
}
buf.WriteByte(b)
b = 0
buf.WriteByte(b)
buf.WriteByte(byte(1))
b = 0x02
buf.WriteByte(b)
buf.WriteByte(0x00)
// byte 5
b = 0
b |= byte(1) << 4 & 0x30
buf.WriteByte(b)
// byte 6
b = 0
buf.WriteByte(b)
buf.WriteByte(0x02)
buf.Write([]byte{'1', '1', 'c', 'a', 'n', 's'})
buf.WriteByte(0x00)
buf.WriteByte(0x00)
buf.Write([]byte{'c', 'o', 'f', 'f', 'e', 'e'})
for i := 0; i < 10; i++ {
buf.WriteByte(0x00)
}
buf.Write([]byte{'1', '.', '0'})
buf.WriteByte(0x00)
data := buf.Bytes()
data[4] = byte(len(data) - 4)
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
return api.SAMStatGood
}
@@ -137,25 +172,22 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
actualLength uint32 = 8
availLength uint32 = 0
allocationLength uint32
data *bytes.Buffer
buf *bytes.Buffer = &bytes.Buffer{}
scb *bytes.Buffer = cmd.SCB
)
// Get Allocation Length
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
if allocationLength < 16 {
glog.Warningf("goto sense, 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)
buf.Write(util.MarshalUint32(availLength))
cmd.InSDBBuffer.Resid = int32(actualLength)
// Skip through to byte 8, Reserved
for i := 0; i < 4; i++ {
data.WriteByte(0x00)
buf.WriteByte(0x00)
}
for _, lu := range cmd.Target.Devices {
@@ -168,10 +200,11 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
}
lun = (0x3fff & lun) << 16
lun = uint64(lun << 32)
binary.Write(data, binary.BigEndian, lun)
buf.Write(util.MarshalUint64(lun))
remainLength -= 8
}
}
cmd.InSDBBuffer.Buffer = buf
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
@@ -218,9 +251,11 @@ func SPCStartStop(host int, cmd *api.SCSICommand) api.SAMStat {
}
func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat {
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
/*
if err := deviceReserve(cmd); err != nil {
return api.SAMStatReservationConflict
}
*/
if cmd.Device.Attrs.Online {
return api.SAMStatGood
}
@@ -247,7 +282,33 @@ func SPCPreventAllowMediaRemoval(host int, cmd *api.SCSICommand) api.SAMStat {
// 6.11 - MODE SENSE(6)
// 6.12 - MODE SENSE(10)
func SPCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
var (
scb = cmd.SCB.Bytes()
mode6 = (scb[0] == 0x1a)
dbd = scb[1] & 0x8 /* Disable Block Descriptors */
pcode = scb[2] & 0x3f
pctrl = (scb[2] & 0xc0) >> 6
subpcode = scb[3]
blkDesctionLen = 0
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
)
if dbd == 0 {
blkDesctionLen = 8
}
if pctrl == 3 {
asc = ASC_SAVING_PARMS_UNSUP
goto sense
}
_ = dbd
_ = pcode
_ = subpcode
_ = mode6
_ = blkDesctionLen
return api.SAMStatGood
sense:
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
}
func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
@@ -263,10 +324,93 @@ sense:
return api.SAMStatCheckCondition
}
func getSCSICmdSize(opcode api.SCSICommandType) byte {
var scsi_command_size = []byte{6, 10, 10, 12, 16, 12, 10, 10}
return scsi_command_size[(byte(opcode)>>5)&7]
}
func reportOpcodesAll(cmd *api.SCSICommand, rctd int) error {
var (
data = []byte{0x00, 0x00, 0x00, 0x00}
)
for _, i := range []api.SCSICommandType{api.TEST_UNIT_READY, api.WRITE_6, api.INQUIRY, api.READ_CAPACITY, api.WRITE_10, api.WRITE_16, api.REPORT_LUNS, api.WRITE_12} {
data = append(data, byte(i))
// reserved
data = append(data, 0x00)
// service action
data = append(data, 0x00)
data = append(data, 0x00)
// reserved
data = append(data, 0x00)
// flags : no service action, possibly timeout desc
if rctd != 0 {
data = append(data, 0x02)
} else {
data = append(data, 0x00)
}
// cdb length
length := getSCSICmdSize(i)
data = append(data, (length>>8)&0xff)
data = append(data, length&0xff)
// timeout descriptor
if rctd != 0 {
// length == 0x0a
data[1] = 0x0a
for n := 0; n < 12; n++ {
data = append(data, 0x00)
}
}
}
buf := util.MarshalUint32(uint32(len(data) - 4))
buf = append(buf, data[4:]...)
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(buf)
return nil
}
func reportOpcodeOne(cmd *api.SCSICommand, rctd int, opcode byte, rsa uint16, serviceAction bool) error {
return nil
}
// This is useful for the various commands using the SERVICE ACTION format.
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
// TODO
scb := cmd.SCB.Bytes()
reporting_options := scb[2] & 0x07
opcode := scb[3]
rctd := int(scb[2] & 0x80)
rsa := util.GetUnalignedUint16(scb[4:])
switch reporting_options {
case 0x00: /* report all */
glog.V(3).Infof("Service Action: report all")
err := reportOpcodesAll(cmd, rctd)
if err != nil {
glog.Error(err)
goto sense
}
case 0x01: /* report one no service action*/
glog.V(3).Infof("Service Action: report one no service action")
err := reportOpcodeOne(cmd, rctd, opcode, rsa, false)
if err != nil {
glog.Error(err)
goto sense
}
case 0x02: /* report one service action */
glog.V(3).Infof("Service Action: report one service action")
err := reportOpcodeOne(cmd, rctd, opcode, rsa, true)
if err != nil {
glog.Error(err)
goto sense
}
default:
goto sense
}
return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
return api.SAMStatCheckCondition
}
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
@@ -360,6 +504,7 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
var (
allocationLength uint32
actualLength uint32
data = &bytes.Buffer{}
)
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
@@ -372,8 +517,11 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
} else {
actualLength = allocationLength
}
binary.Write(cmd.InSDBBuffer.Buffer, binary.BigEndian, cmd.SenseBuffer.Bytes()[0:actualLength])
if cmd.SenseBuffer != nil {
data.Write(cmd.SenseBuffer.Bytes()[0:actualLength])
}
cmd.InSDBBuffer.Resid = int32(actualLength)
cmd.InSDBBuffer.Buffer = data
// reset sense buffer in cmnd
cmd.SenseBuffer = &bytes.Buffer{}
+16 -6
View File
@@ -21,18 +21,28 @@ import (
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port"
)
func NewTarget(tid int, driverName, name string) (*api.SCSITarget, error) {
func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
// verify the target ID
// verify the target's Name
// verify the low level driver
var target = &api.SCSITarget{Name: name, TID: tid}
var tgt = port.NewTargetDriver(driverName, target)
target.SCSITargetDriver = tgt
var target = &api.SCSITarget{
Name: name,
TID: tid,
Devices: []*api.SCSILu{},
}
lun, err := NewSCSILu(0, target)
if err != nil {
glog.Errorf("fail to create LU: %v", err)
return nil, err
}
s.mutex.Lock()
target.Devices = append(target.Devices, lun)
s.Targets = append(s.Targets, target)
s.mutex.Unlock()
return target, nil
}
@@ -40,7 +50,7 @@ func deviceReserve(cmd *api.SCSICommand) error {
var lu *api.SCSILu
for _, dev := range cmd.Target.Devices {
if dev.Lun == cmd.Device.Lun {
lu = &dev
lu = dev
break
}
}