diff --git a/.travis.yml b/.travis.yml index 5de8acb..7113f64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -74,10 +74,6 @@ script: - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteVerify10 iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteVerify16 iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteVerify12 iscsi://127.0.0.1:3260/${TARGET}/0 - - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteAtomic16.BeyondEol iscsi://127.0.0.1:3260/${TARGET}/0 - - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteAtomic16.ZeroBlocks iscsi://127.0.0.1:3260/${TARGET}/0 - - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteAtomic16.WriteProtect iscsi://127.0.0.1:3260/${TARGET}/0 - - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteAtomic16.DpoFua iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteSame10.Simple iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=ALL.WriteSame16.Simple iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=ALL.Verify10 iscsi://127.0.0.1:3260/${TARGET}/0 diff --git a/examples/config.json b/examples/config.json index 2143c2b..0e749e7 100644 --- a/examples/config.json +++ b/examples/config.json @@ -3,7 +3,9 @@ { "deviceID":1000, "path":"file:/var/tmp/disk.img", - "online":true + "online":true, + "thinProvisioning":false, + "blockShift": 9, } ], "iscsiportals":[ diff --git a/pkg/api/types.go b/pkg/api/types.go index 9b533a0..de303e7 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -19,7 +19,7 @@ import ( "errors" "sync" - "github.com/satori/go.uuid" + uuid "github.com/satori/go.uuid" ) type SCSICommandType byte @@ -264,7 +264,7 @@ type SCSILuPhyAttribute struct { // Software Write Protect SWP bool // Use thin-provisioning for this LUN - Thinprovisioning bool + ThinProvisioning bool // Logical Unit online Online bool // Descrptor format sense data supported @@ -351,6 +351,7 @@ type BackingStore interface { Write([]byte, int64) error DataSync() error DataAdvise(int64, int64, uint32) error + Unmap([]UnmapBlockDescriptor) error } type SCSIDeviceProtocol interface { @@ -400,3 +401,8 @@ type SCSILu struct { } type LUNMap map[uint64]*SCSILu + +type UnmapBlockDescriptor struct { + Offset uint64 + TL uint32 +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 092b81f..6fb4a42 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -101,9 +101,11 @@ var ( ) type BackendStorage struct { - DeviceID uint64 `json:"deviceID"` - Path string `json:"path"` - Online bool `json:"online"` + DeviceID uint64 `json:"deviceID"` + Path string `json:"path"` + Online bool `json:"online"` + ThinProvisioning bool `json:"thinProvisioning"` + BlockShift uint `json:"blockShift"` } type ISCSIPortalInfo struct { diff --git a/pkg/scsi/backingstore.go b/pkg/scsi/backingstore.go index 7cf0315..f5d91b3 100644 --- a/pkg/scsi/backingstore.go +++ b/pkg/scsi/backingstore.go @@ -64,6 +64,7 @@ func bsPerformCommand(bs api.BackingStore, cmd *api.SCSICommand) (err error, key rbuf, wbuf []byte tl int64 = int64(cmd.TL) ) + key = HARDWARE_ERROR asc = ASC_INTERNAL_TGT_FAILURE switch opcode { @@ -134,8 +135,6 @@ func bsPerformCommand(bs api.BackingStore, cmd *api.SCSICommand) (err error, key } doVerify = true goto verify - case api.UNMAP: - // TODO default: break } diff --git a/pkg/scsi/backingstore/cephstore_linux.go b/pkg/scsi/backingstore/cephstore_linux.go index 291283a..9e99f42 100644 --- a/pkg/scsi/backingstore/cephstore_linux.go +++ b/pkg/scsi/backingstore/cephstore_linux.go @@ -149,3 +149,7 @@ func (bs *CephBackingStore) DataSync() error { func (bs *CephBackingStore) DataAdvise(offset, length int64, advise uint32) error { return nil } + +func (bs *CephBackingStore) Unmap([]api.UnmapBlockDescriptor) error { + return nil +} diff --git a/pkg/scsi/backingstore/common.go b/pkg/scsi/backingstore/common.go index 4d78fab..2a2d4b3 100644 --- a/pkg/scsi/backingstore/common.go +++ b/pkg/scsi/backingstore/common.go @@ -113,3 +113,7 @@ func (bs *FileBackingStore) DataSync() error { func (bs *FileBackingStore) DataAdvise(offset, length int64, advise uint32) error { return util.Fadvise(bs.file, offset, length, advise) } + +func (bs *FileBackingStore) Unmap([]api.UnmapBlockDescriptor) error { + return nil +} diff --git a/pkg/scsi/backingstore/null.go b/pkg/scsi/backingstore/null.go index 2cbde02..8c74739 100644 --- a/pkg/scsi/backingstore/null.go +++ b/pkg/scsi/backingstore/null.go @@ -74,3 +74,7 @@ func (bs *NullBackingStore) DataSync() error { func (bs *NullBackingStore) DataAdvise(offset, length int64, advise uint32) error { return nil } + +func (bs *NullBackingStore) Unmap([]api.UnmapBlockDescriptor) error { + return nil +} diff --git a/pkg/scsi/lun.go b/pkg/scsi/lun.go index a7a8afa..244b24a 100644 --- a/pkg/scsi/lun.go +++ b/pkg/scsi/lun.go @@ -20,13 +20,13 @@ import ( "strings" "github.com/gostor/gotgt/pkg/api" + "github.com/gostor/gotgt/pkg/config" ) // NewSCSILu: create a new SCSI LU // path format :/absolute/file/path -func NewSCSILu(device_uuid uint64, path string, online bool) (*api.SCSILu, error) { - - pathinfo := strings.SplitN(path, ":", 2) +func NewSCSILu(bs *config.BackendStorage) (*api.SCSILu, error) { + pathinfo := strings.SplitN(bs.Path, ":", 2) if len(pathinfo) < 2 { return nil, errors.New("invalid device path string") } @@ -43,8 +43,8 @@ func NewSCSILu(device_uuid uint64, path string, online bool) (*api.SCSILu, error PerformCommand: luPerformCommand, DeviceProtocol: sbc, Storage: backing, - BlockShift: api.DefaultBlockShift, - UUID: device_uuid, + BlockShift: bs.BlockShift, + UUID: bs.DeviceID, } err = backing.Open(lu, backendPath) @@ -53,7 +53,8 @@ func NewSCSILu(device_uuid uint64, path string, online bool) (*api.SCSILu, error } lu.Size = backing.Size(lu) lu.DeviceProtocol.InitLu(lu) - lu.Attrs.Online = online + lu.Attrs.ThinProvisioning = bs.ThinProvisioning + lu.Attrs.Online = bs.Online lu.Attrs.Lbppbe = 3 return lu, nil } diff --git a/pkg/scsi/sbc.go b/pkg/scsi/sbc.go index 98e7a0f..2b864cb 100644 --- a/pkg/scsi/sbc.go +++ b/pkg/scsi/sbc.go @@ -18,6 +18,7 @@ limitations under the License. package scsi import ( + "encoding/binary" "fmt" "unsafe" @@ -58,7 +59,7 @@ func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error { // init LU's phy attribute lu.Attrs.DeviceType = sbc.DeviceType lu.Attrs.Qualifier = false - lu.Attrs.Thinprovisioning = false + lu.Attrs.ThinProvisioning = false lu.Attrs.Removable = false lu.Attrs.Readonly = false lu.Attrs.SWP = false @@ -295,6 +296,33 @@ sense: } func SBCUnmap(host int, cmd *api.SCSICommand) api.SAMStat { + // check ANCHOR + if cmd.SCB[1]&0x01 != 0 { + BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) + return api.SAMStatCheckCondition + } + + const blockDescLen = 16 + + var blockDescs []api.UnmapBlockDescriptor + for off := 8; uint32(off+blockDescLen) <= cmd.OutSDBBuffer.Length; off += blockDescLen { + lba := binary.BigEndian.Uint64(cmd.OutSDBBuffer.Buffer[off : off+8]) + num := binary.BigEndian.Uint32(cmd.OutSDBBuffer.Buffer[off+8 : off+12]) + blockDescs = append(blockDescs, api.UnmapBlockDescriptor{ + Offset: lba << cmd.Device.BlockShift, + TL: num << cmd.Device.BlockShift, + }) + } + + if len(blockDescs) == 0 { + return api.SAMStatGood + } + + if err := cmd.Device.Storage.Unmap(blockDescs); err != nil { + BuildSenseData(cmd, MEDIUM_ERROR, NO_ADDITIONAL_SENSE) + return api.SAMStatCheckCondition + } + return api.SAMStatGood } @@ -352,7 +380,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { goto sense } // We only support unmap for thin provisioned LUNS - if (scb[1]&0x08 != 0) && !dev.Attrs.Thinprovisioning { + if (scb[1]&0x08 != 0) && !dev.Attrs.ThinProvisioning { key = ILLEGAL_REQUEST asc = ASC_INVALID_FIELD_IN_CDB goto sense @@ -594,7 +622,11 @@ func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat { if allocationLength > 12 { copy(cmd.InSDBBuffer.Buffer[8:], util.MarshalUint32(uint32(1< 16 { - val := (cmd.Device.Attrs.Lbppbe << 16) | cmd.Device.Attrs.LowestAlignedLBA + var lbpme int + if cmd.Device.Attrs.ThinProvisioning { + lbpme = 1 + } + val := (cmd.Device.Attrs.Lbppbe << 16) | (lbpme << 15) | cmd.Device.Attrs.LowestAlignedLBA copy(cmd.InSDBBuffer.Buffer[12:], util.MarshalUint32(uint32(val))) } } diff --git a/pkg/scsi/scsilumap.go b/pkg/scsi/scsilumap.go index 5803507..a968eef 100644 --- a/pkg/scsi/scsilumap.go +++ b/pkg/scsi/scsilumap.go @@ -73,7 +73,7 @@ func InitSCSILUMap(config *config.Config) error { defer globalSCSILUMap.mutex.Unlock() for _, bs := range config.Storages { - lu, err := NewSCSILu(bs.DeviceID, bs.Path, bs.Online) + lu, err := NewSCSILu(&bs) if err != nil { return fmt.Errorf("Init SCSI LU map error: %v", err) } diff --git a/pkg/scsi/spc.go b/pkg/scsi/spc.go index cec5d1a..f30de9c 100644 --- a/pkg/scsi/spc.go +++ b/pkg/scsi/spc.go @@ -25,7 +25,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/util" - "github.com/satori/go.uuid" + uuid "github.com/satori/go.uuid" ) func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat { @@ -58,11 +58,11 @@ func InquiryPage0x00(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) { descBuf.WriteByte(0x00) descBuf.WriteByte(0x80) descBuf.WriteByte(0x83) + descBuf.WriteByte(0xB0) + descBuf.WriteByte(0xB2) /* TODO: descBuf.WriteByte(0x86) - descBuf.WriteByte(0xB0) - descBuf.WriteByte(0xB2) */ data = descBuf.Bytes() @@ -188,6 +188,80 @@ func InquiryPage0x83(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) { return buf, pageLength } +func InquiryPage0xB0(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) { + var ( + buf = &bytes.Buffer{} + pageLength uint16 = 0x3C + maxUnmapLbaCount uint32 = 0 + maxUnmapBlockDescriptorCount uint32 = 0 + ) + + if cmd.Device.Attrs.ThinProvisioning { + maxUnmapLbaCount = 0xFFFFFFFF + maxUnmapBlockDescriptorCount = 0xFFFFFFFF + } + + //byte 0 + if cmd.Device.Attrs.Online { + buf.WriteByte(PQ_DEVICE_CONNECTED | byte(cmd.Device.Attrs.DeviceType)) + } else { + buf.WriteByte(PQ_DEVICE_NOT_CONNECT | byte(cmd.Device.Attrs.DeviceType)) + } + //PAGE CODE + buf.WriteByte(0xB0) + //PAGE LENGTH + binary.Write(buf, binary.BigEndian, pageLength) + + buf.Write(make([]byte, 16)) + + //MAXIMUM UNMAP LBA COUNT + binary.Write(buf, binary.BigEndian, maxUnmapLbaCount) + + //MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT + binary.Write(buf, binary.BigEndian, maxUnmapBlockDescriptorCount) + + buf.Write(make([]byte, 36)) + return buf, pageLength +} + +func InquiryPage0xB2(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) { + var ( + buf = &bytes.Buffer{} + pageLength uint16 = 0x4 + ) + + //byte 0 + if cmd.Device.Attrs.Online { + buf.WriteByte(PQ_DEVICE_CONNECTED | byte(cmd.Device.Attrs.DeviceType)) + } else { + buf.WriteByte(PQ_DEVICE_NOT_CONNECT | byte(cmd.Device.Attrs.DeviceType)) + } + //PAGE CODE + buf.WriteByte(0xB2) + + //PAGE LENGTH + binary.Write(buf, binary.BigEndian, pageLength) + + var lbpu byte + if cmd.Device.Attrs.ThinProvisioning { + lbpu = 1 << 7 + } + + // THRESHOLD EXPONENT + buf.WriteByte(0) + + // LBPU | LBPWS | LBPWS10 | LBPRZ | ANC_SUP | DP + buf.WriteByte(lbpu) + + // MINIMUM PERCENTAGE | PROVISIONING TYPE + buf.WriteByte(0) + + // THRESHOLD PERCENTAGE + buf.WriteByte(0) + + return buf, pageLength +} + /* * SPCInquiry Implements SCSI INQUIRY command * The INQUIRY command requests the device server to return information regarding the logical unit and SCSI target device. @@ -231,6 +305,10 @@ func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat { buf, _ = InquiryPage0x80(host, cmd) case 0x83: buf, _ = InquiryPage0x83(host, cmd) + case 0xB0: + buf, _ = InquiryPage0xB0(host, cmd) + case 0xB2: + buf, _ = InquiryPage0xB2(host, cmd) default: goto sense } @@ -600,7 +678,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} { + 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, api.UNMAP} { data = append(data, byte(i)) // reserved data = append(data, 0x00) @@ -617,7 +695,7 @@ func reportOpcodesAll(cmd *api.SCSICommand, rctd int) error { } // cdb length length := getSCSICmdSize(i) - data = append(data, (length>>8)&0xff) + data = append(data, 0) data = append(data, length&0xff) // timeout descriptor if rctd != 0 { diff --git a/test/libiscsi-gotgt-test.sh b/test/libiscsi-gotgt-test.sh index bb8d8bb..5050245 100755 --- a/test/libiscsi-gotgt-test.sh +++ b/test/libiscsi-gotgt-test.sh @@ -25,12 +25,11 @@ TESTCASES="ALL.Inquiry.Standard\ ALL.ReportSupportedOpcodes.Simple ALL.Reserve6.Simple \ ALL.StartStopUnit ALL.TestUnitReady \ ALL.Write10 ALL.Write16 ALL.Write12 ALL.WriteVerify10 \ - ALL.WriteVerify16 ALL.WriteVerify12 ALL.WriteAtomic16.BeyondEol \ - ALL.WriteAtomic16.ZeroBlocks ALL.WriteAtomic16.WriteProtect \ - ALL.WriteAtomic16.DpoFua \ + ALL.WriteVerify16 ALL.WriteVerify12 \ ALL.WriteSame10.Simple ALL.WriteSame16.Simple \ ALL.Verify10 ALL.Verify12 ALL.Verify16 \ ALL.iSCSITMF ALL.iSCSIcmdsn \ + ALL.Unmap.Simple ALL.Unmap.VPD ALL.Unmap.ZeroBlocks \ " #for i in $NEWCASES