diff --git a/pkg/api/types.go b/pkg/api/types.go index 7238de3..4daca00 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -17,6 +17,7 @@ package api import ( "errors" + "io" "sync" uuid "github.com/satori/go.uuid" @@ -165,6 +166,7 @@ var ( ) type SCSICommand struct { + OpCode byte Target *SCSITarget DeviceID uint64 Device *SCSILu @@ -395,6 +397,9 @@ type SCSILu struct { Storage BackingStore DeviceProtocol SCSIDeviceProtocol ModeBlockDescriptor []byte + SCSIVendorID string + SCSIProductID string + SCSIID string PerformCommand CommandFunc FinishCommand func(*SCSITarget, *SCSICommand) @@ -406,3 +411,14 @@ type UnmapBlockDescriptor struct { Offset uint64 TL uint32 } + +type ReaderWriterAt interface { + io.ReaderAt + io.WriterAt +} + +type RemoteBackingStore interface { + ReaderWriterAt + Sync() (int, error) + Unmap(int64, int64) (int, error) +} diff --git a/pkg/port/iscsit/cmd.go b/pkg/port/iscsit/cmd.go index ea8232e..f592568 100644 --- a/pkg/port/iscsit/cmd.go +++ b/pkg/port/iscsit/cmd.go @@ -20,7 +20,9 @@ import ( "bytes" "fmt" "strings" + "time" + "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/util" log "github.com/sirupsen/logrus" ) @@ -93,6 +95,7 @@ type ISCSICommand struct { FinalInSeq bool Immediate bool TaskTag uint32 + StartTime time.Time ExpCmdSN, MaxCmdSN uint32 AHSLen int Resid uint32 @@ -124,6 +127,7 @@ type ISCSICommand struct { StatusDetail uint8 // SCSI commands + SCSIOpCode byte ExpectedDataLen uint32 CDB []byte Status byte @@ -225,6 +229,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) { m.AHSLen = int(data[4]) * 4 m.DataLen = int(ParseUint(data[5:8])) m.TaskTag = uint32(ParseUint(data[16:20])) + m.StartTime = time.Now() switch m.OpCode { case OpSCSICmd: m.LUN = [8]byte{data[9]} @@ -234,6 +239,14 @@ func parseHeader(data []byte) (*ISCSICommand, error) { m.Write = data[1]&0x20 == 0x20 m.CDB = data[32:48] m.ExpStatSN = uint32(ParseUint(data[28:32])) + m.SCSIOpCode = m.CDB[0] + SCSIOpcode := api.SCSICommandType(m.SCSIOpCode) + switch SCSIOpcode { + case api.READ_6, api.READ_10, api.READ_12, api.READ_16: + m.Read = true + case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16: + m.Write = true + } fallthrough case OpSCSITaskReq: m.ReferencedTaskTag = uint32(ParseUint(data[20:24])) @@ -358,7 +371,9 @@ func (m *ISCSICommand) dataInBytes() []byte { copy(buf[36:], util.MarshalUint32(m.DataSN)) copy(buf[40:], util.MarshalUint32(m.BufferOffset)) copy(buf[44:], util.MarshalUint32(m.Resid)) - copy(buf[48:], m.RawData[m.BufferOffset:m.BufferOffset+uint32(m.DataLen)]) + if m.ExpectedDataLen != 0 { + copy(buf[48:], m.RawData[m.BufferOffset:m.BufferOffset+uint32(m.DataLen)]) + } return buf } diff --git a/pkg/port/iscsit/conn.go b/pkg/port/iscsit/conn.go index d74fd5e..f72d8e7 100644 --- a/pkg/port/iscsit/conn.go +++ b/pkg/port/iscsit/conn.go @@ -51,6 +51,7 @@ var ( ) type iscsiConnection struct { + ConnNum int state int authState int session *ISCSISession @@ -96,12 +97,13 @@ const ( ) type iscsiTask struct { - tag uint32 - conn *iscsiConnection - cmd *ISCSICommand - scmd *api.SCSICommand - state taskState - result byte + tag uint32 + conn *iscsiConnection + cmd *ISCSICommand + scmd *api.SCSICommand + state taskState + expectedDataLength int64 + result byte offset int r2tCount int @@ -154,6 +156,7 @@ func (conn *iscsiConnection) buildRespPackage(oc OpCode, task *iscsiTask) error task = conn.rxTask } conn.resp = &ISCSICommand{ + StartTime: conn.req.StartTime, StatSN: conn.req.ExpStatSN, TaskTag: conn.req.TaskTag, ExpCmdSN: conn.session.ExpCmdSN, @@ -164,6 +167,7 @@ func (conn *iscsiConnection) buildRespPackage(oc OpCode, task *iscsiTask) error case OpReady: conn.resp.OpCode = OpReady conn.resp.R2TSN = task.r2tSN + conn.resp.Final = true conn.resp.BufferOffset = uint32(task.offset) conn.resp.DesiredLength = uint32(task.r2tCount) if val := conn.loginParam.sessionParam[ISCSI_PARAM_MAX_BURST].Value; task.r2tCount > int(val) { @@ -171,6 +175,7 @@ func (conn *iscsiConnection) buildRespPackage(oc OpCode, task *iscsiTask) error } case OpSCSIIn, OpSCSIResp: conn.resp.OpCode = oc + conn.resp.SCSIOpCode = conn.req.SCSIOpCode conn.resp.Immediate = true conn.resp.Final = true conn.resp.SCSIResponse = 0x00 diff --git a/pkg/port/iscsit/iscsid.go b/pkg/port/iscsit/iscsid.go index 5a265aa..ffc14d4 100644 --- a/pkg/port/iscsit/iscsid.go +++ b/pkg/port/iscsit/iscsid.go @@ -22,6 +22,7 @@ import ( "os" "strconv" "sync" + "time" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/config" @@ -35,15 +36,30 @@ const ( ISCSI_UNSPEC_TSIH = uint16(0) ) -type ISCSITargetDriver struct { - SCSI *scsi.SCSITargetService - Name string - iSCSITargets map[string]*ISCSITarget - TSIHPool map[uint16]bool - TSIHPoolMutex sync.Mutex +const ( + STATE_INIT = iota + STATE_RUNNING + STATE_SHUTTING_DOWN + STATE_TERMINATE +) - mu sync.Mutex - l net.Listener +var ( + EnableStats bool +) + +type ISCSITargetDriver struct { + SCSI *scsi.SCSITargetService + Name string + iSCSITargets map[string]*ISCSITarget + TSIHPool map[uint16]bool + TSIHPoolMutex sync.Mutex + isClientConnected bool + enableStats bool + mu *sync.RWMutex + l net.Listener + state uint8 + OpCode int + TargetStats scsi.Stats } func init() { @@ -51,12 +67,19 @@ func init() { } func NewISCSITargetDriver(base *scsi.SCSITargetService) (scsi.SCSITargetDriver, error) { - return &ISCSITargetDriver{ + driver := &ISCSITargetDriver{ Name: iSCSIDriverName, iSCSITargets: map[string]*ISCSITarget{}, SCSI: base, TSIHPool: map[uint16]bool{0: true, 65535: true}, - }, nil + mu: &sync.RWMutex{}, + } + + if EnableStats { + driver.enableStats = true + driver.TargetStats.SCSIIOCount = map[int]int64{} + } + return driver, nil } func (s *ISCSITargetDriver) AllocTSIH() uint16 { @@ -165,46 +188,72 @@ func (s *ISCSITargetDriver) Run() error { s.mu.Lock() s.l = l s.mu.Unlock() + log.Infof("iSCSI service listening on: %v", s.l.Addr()) + s.setState(STATE_RUNNING) for { - log.Info("Listening ...") conn, err := l.Accept() if err != nil { if err, ok := err.(net.Error); ok { if !err.Temporary() { - log.Info("Closing ...") + log.Warning("Closing connection with initiator...") break } } log.Error(err) continue } + log.Info(conn.LocalAddr().String()) - log.Info("Accepting ...") + s.setClientStatus(true) iscsiConn := &iscsiConnection{conn: conn, loginParam: &iscsiLoginParam{}} iscsiConn.init() iscsiConn.rxIOState = IOSTATE_RX_BHS - - log.Infof("connection is connected from %s...\n", conn.RemoteAddr().String()) + log.Infof("Target is connected to initiator: %s", conn.RemoteAddr().String()) // start a new thread to do with this command go s.handler(DATAIN, iscsiConn) } return nil } +func (s *ISCSITargetDriver) setClientStatus(ok bool) { + s.isClientConnected = ok +} + +func (s *ISCSITargetDriver) isInitiatorConnected() bool { + return s.isClientConnected +} func (s *ISCSITargetDriver) Close() error { s.mu.Lock() l := s.l + s.setClientStatus(false) s.mu.Unlock() if l != nil { - return l.Close() + s.setState(STATE_SHUTTING_DOWN) + if err := l.Close(); err != nil { + return err + } + s.setState(STATE_TERMINATE) + return nil } return nil } +func (s *ISCSITargetDriver) setState(st uint8) { + s.mu.Lock() + defer s.mu.Unlock() + s.state = st +} + +func (s *ISCSITargetDriver) Resize(size uint64) error { + s.mu.Lock() + defer s.mu.Unlock() + return s.SCSI.Resize(size) +} + func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) { if events&DATAIN != 0 { @@ -324,6 +373,7 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) { } case OpLogoutReq: log.Debug("OpLogoutReq") + s.setClientStatus(false) if err := iscsiExecLogout(conn); err != nil { log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE @@ -386,6 +436,7 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error { } func iscsiExecLogout(conn *iscsiConnection) error { + log.Infof("Logout request received from initiator: %v", conn.conn.RemoteAddr().String()) cmd := conn.req conn.resp = &ISCSICommand{ OpCode: OpLogoutResp, @@ -473,6 +524,12 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) { resp.DataSN = 0 maxCount := conn.maxSeqCount + if s.enableStats { + if resp.OpCode == OpSCSIResp || resp.OpCode == OpSCSIIn { + s.UpdateStats(conn) + } + } + /* send data splitted by segmentLen */ SendRemainingData: if resp.OpCode == OpSCSIIn { @@ -573,6 +630,13 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error switch req.OpCode { case OpSCSICmd: log.Debugf("SCSI Command processing...") + if s.enableStats { + if _, ok := s.TargetStats.SCSIIOCount[(int)(req.CDB[0])]; ok != false { + s.TargetStats.SCSIIOCount[(int)(req.CDB[0])] += 1 + } else { + s.TargetStats.SCSIIOCount[(int)(req.CDB[0])] = 1 + } + } scmd := &api.SCSICommand{ ITNexusID: conn.session.ITNexus.ID, SCB: req.CDB, @@ -594,8 +658,17 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error } task := &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: scmd} + task.scmd.OpCode = conn.req.SCSIOpCode + if scmd.Direction == api.SCSIDataBidirection { + task.scmd.Result = api.SAMStatCheckCondition.Stat + scsi.BuildSenseData(task.scmd, scsi.ILLEGAL_REQUEST, scsi.NO_ADDITIONAL_SENSE) + conn.buildRespPackage(OpSCSIResp, task) + conn.rxTask = nil + break + } if req.Write { task.r2tCount = int(req.ExpectedDataLen) - req.DataLen + task.expectedDataLength = int64(req.ExpectedDataLen) if !req.Final { task.unsolCount = 1 } @@ -649,7 +722,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error } return } else { - if scmd.Direction == api.SCSIDataRead && scmd.SenseBuffer == nil { + if scmd.Direction == api.SCSIDataRead && scmd.SenseBuffer == nil && req.ExpectedDataLen != 0 { conn.buildRespPackage(OpSCSIIn, task) } else { conn.buildRespPackage(OpSCSIResp, task) @@ -710,6 +783,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error case OpNoopOut: iscsiExecNoopOut(conn) case OpLogoutReq: + s.setClientStatus(false) conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag} conn.txIOState = IOSTATE_TX_BHS iscsiExecLogout(conn) @@ -824,13 +898,31 @@ func (s *ISCSITargetDriver) iscsiExecTask(task *iscsiTask) error { fallthrough case ISCSI_TM_FUNC_TARGET_WARM_RESET, ISCSI_TM_FUNC_TARGET_COLD_RESET, ISCSI_TM_FUNC_TASK_REASSIGN: task.result = ISCSI_TMF_RSP_NOT_SUPPORTED - return fmt.Errorf("The task function is not supported") default: task.result = ISCSI_TMF_RSP_REJECTED - return fmt.Errorf("Unknown task function") } // return response to initiator return task.conn.buildRespPackage(OpSCSITaskResp, task) } return nil } + +func (s *ISCSITargetDriver) Stats() scsi.Stats { + return s.TargetStats +} + +func (s *ISCSITargetDriver) UpdateStats(conn *iscsiConnection) { + s.TargetStats.IsClientConnected = s.isClientConnected + switch api.SCSICommandType(conn.resp.SCSIOpCode) { + case api.READ_6, api.READ_10, api.READ_12, api.READ_16: + s.TargetStats.ReadIOPS += 1 + s.TargetStats.TotalReadTime += int64(time.Since(conn.resp.StartTime)) + s.TargetStats.TotalReadBlockCount += int64(conn.resp.ExpectedDataLen) + break + case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16: + s.TargetStats.WriteIOPS += 1 + s.TargetStats.TotalWriteTime += int64(time.Since(conn.resp.StartTime)) + s.TargetStats.TotalWriteBlockCount += int64(conn.resp.ExpectedDataLen) + break + } +} diff --git a/pkg/port/iscsit/login.go b/pkg/port/iscsit/login.go index 0f819a9..2a8d452 100644 --- a/pkg/port/iscsit/login.go +++ b/pkg/port/iscsit/login.go @@ -152,8 +152,8 @@ func (conn *iscsiConnection) processLoginData() ([]util.KeyValue, error) { conn.loginParam.iniCSG, conn.loginParam.iniNSG, conn.loginParam.iniTrans) } } else { - conn.loginParam.tgtNSG = LoginOperationalNegotiation - conn.loginParam.tgtTrans = false + conn.loginParam.tgtNSG = FullFeaturePhase + conn.loginParam.tgtTrans = true } return negoKV, nil } diff --git a/pkg/port/iscsit/session.go b/pkg/port/iscsit/session.go index 4c74463..cd0c072 100644 --- a/pkg/port/iscsit/session.go +++ b/pkg/port/iscsit/session.go @@ -184,13 +184,13 @@ func boolKeyInConv(value uint) string { var sessionKeys map[string]*iscsiSessionKeys = map[string]*iscsiSessionKeys{ // ISCSI_PARAM_MAX_RECV_DLENGTH - "MaxRecvDataSegmentLength": {ISCSI_PARAM_MAX_RECV_DLENGTH, true, 32768, 512, 16777215, numberKeyConv, numberKeyInConv}, + "MaxRecvDataSegmentLength": {ISCSI_PARAM_MAX_RECV_DLENGTH, true, 65536, 512, 16777215, numberKeyConv, numberKeyInConv}, // ISCSI_PARAM_HDRDGST_EN "HeaderDigest": {ISCSI_PARAM_HDRDGST_EN, false, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, digestKeyConv, digestKeyInConv}, // ISCSI_PARAM_DATADGST_EN "DataDigest": {ISCSI_PARAM_DATADGST_EN, false, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, digestKeyConv, digestKeyInConv}, // ISCSI_PARAM_INITIAL_R2T_EN - "InitialR2T": {ISCSI_PARAM_INITIAL_R2T_EN, false, 1, 0, 1, boolKeyConv, boolKeyInConv}, + "InitialR2T": {ISCSI_PARAM_INITIAL_R2T_EN, true, 1, 0, 1, boolKeyConv, boolKeyInConv}, // ISCSI_PARAM_MAX_R2T "MaxOutstandingR2T": {ISCSI_PARAM_MAX_R2T, true, 1, 1, 65535, numberKeyConv, numberKeyInConv}, // ISCSI_PARAM_IMM_DATA_EN @@ -392,8 +392,8 @@ func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error { } if newSess.SessionType == SESSION_NORMAL { - log.Infof("New Session initiator name:%v,target name:%v,ISID:0x%x", - conn.loginParam.initiator, conn.loginParam.target, conn.loginParam.isid) + log.Infof("Login request received from initiator: %v, Session type: %s, Target name:%v, ISID: 0x%x", + conn.loginParam.initiator, "Normal", conn.loginParam.target, conn.loginParam.isid) //register normal session itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(newSess)} scsi.AddITNexus(&newSess.Target.SCSITarget, itnexus) @@ -404,6 +404,8 @@ func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error { newSess.Target.Sessions[newSess.TSIH] = newSess newSess.Target.SessionsRWMutex.Unlock() } else { + log.Infof("Discovery request received from initiator: %v, Session type: %s, ISID: 0x%x", + conn.loginParam.initiator, "Discovery", conn.loginParam.isid) conn.session = newSess } } else { diff --git a/pkg/scsi/backingstore.go b/pkg/scsi/backingstore.go index 731b84a..9890695 100644 --- a/pkg/scsi/backingstore.go +++ b/pkg/scsi/backingstore.go @@ -125,6 +125,11 @@ func bsPerformCommand(bs api.BackingStore, cmd *api.SCSICommand) (err error, key bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_NOREUSE) } cmd.InSDBBuffer.Resid = uint32(length) + if cmd.InSDBBuffer.Length < uint32(length) { + key = ILLEGAL_REQUEST + asc = ASC_INVALID_FIELD_IN_CDB + goto sense + } copy(cmd.InSDBBuffer.Buffer, rbuf) case api.PRE_FETCH_10, api.PRE_FETCH_16: err = bs.DataAdvise(int64(offset), tl, util.POSIX_FADV_WILLNEED) @@ -148,7 +153,7 @@ write: if err != nil { log.Error(err) key = MEDIUM_ERROR - asc = ASC_READ_ERROR + asc = ASC_WRITE_ERROR goto sense } log.Debugf("write data at 0x%x for length %d", offset, len(wbuf)) @@ -167,7 +172,7 @@ write: if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) { if err = bs.DataSync(int64(offset), tl); err != nil { key = MEDIUM_ERROR - asc = ASC_READ_ERROR + asc = ASC_WRITE_ERROR goto sense } } diff --git a/pkg/scsi/backingstore/remote/remote.go b/pkg/scsi/backingstore/remote/remote.go new file mode 100644 index 0000000..5d6a12e --- /dev/null +++ b/pkg/scsi/backingstore/remote/remote.go @@ -0,0 +1,118 @@ +/* +Copyright 2016 openebs 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. +*/ + +package remote + +import ( + "fmt" + + "github.com/gostor/gotgt/pkg/api" + "github.com/gostor/gotgt/pkg/scsi" + log "github.com/sirupsen/logrus" +) + +var ( + Size uint64 +) + +func init() { + scsi.RegisterBackingStore("RemBs", NewRemoteBackingStore) +} + +// RemBackingStore +type RemBackingStore struct { + scsi.BaseBackingStore + // Remote backing store, remote server exposing + // read and write methods. + RemBs api.RemoteBackingStore +} + +func NewRemoteBackingStore() (api.BackingStore, error) { + return &RemBackingStore{ + BaseBackingStore: scsi.BaseBackingStore{ + Name: "RemBs", + OflagsSupported: 0, + }, + }, nil +} + +func (bs *RemBackingStore) Open(dev *api.SCSILu, path string) error { + if Size == 0 { + return fmt.Errorf("Size is not initialized") + } + var err error + bs.DataSize = Size + bs.RemBs, err = scsi.GetTargetBSMap(path) + if err != nil { + return err + } + return nil +} + +func (bs *RemBackingStore) Close(dev *api.SCSILu) error { + /* TODO return bs.File.Close()*/ + return nil +} + +func (bs *RemBackingStore) Init(dev *api.SCSILu, Opts string) error { + return nil +} + +func (bs *RemBackingStore) Exit(dev *api.SCSILu) error { + return nil +} + +func (bs *RemBackingStore) Size(dev *api.SCSILu) uint64 { + return bs.DataSize +} + +func (bs *RemBackingStore) Read(offset, tl int64) ([]byte, error) { + tmpbuf := make([]byte, tl) + length, err := bs.RemBs.ReadAt(tmpbuf, offset) + if err != nil { + return nil, err + } + if length != len(tmpbuf) { + return nil, fmt.Errorf("Incomplete read expected:%d actual:%d", tl, length) + } + return tmpbuf, nil +} + +func (bs *RemBackingStore) Write(wbuf []byte, offset int64) error { + length, err := bs.RemBs.WriteAt(wbuf, offset) + if err != nil { + log.Error(err) + return err + } + if length != len(wbuf) { + return fmt.Errorf("Incomplete write expected:%d actual:%d", len(wbuf), length) + } + return nil +} + +func (bs *RemBackingStore) DataAdvise(offset, length int64, advise uint32) error { + return nil +} + +func (bs *RemBackingStore) DataSync(offset, length int64) (err error) { + _, err = bs.RemBs.Sync() + return +} + +func (bs *RemBackingStore) Unmap(bd []api.UnmapBlockDescriptor) (err error) { + //_, err = bs.RemBs.Unmap(int64(bd[0].Offset), int64(bd[0].TL)) + return +} diff --git a/pkg/scsi/cmd.go b/pkg/scsi/cmd.go index 1ad4d4c..4bb80c4 100644 --- a/pkg/scsi/cmd.go +++ b/pkg/scsi/cmd.go @@ -228,9 +228,10 @@ const ( NAA_IEEE_REGD_EXTD = byte(0x6) ) -const ( +var ( SCSIVendorID = "GOSTOR" SCSIProductID = "GOTGT" + SCSIID = "iqn.2016-09.com.gotgt.gostor:iscsi-tgt" ) /* diff --git a/pkg/scsi/drivers.go b/pkg/scsi/drivers.go index 0571f91..9c64498 100644 --- a/pkg/scsi/drivers.go +++ b/pkg/scsi/drivers.go @@ -28,6 +28,27 @@ type SCSITargetDriver interface { NewTarget(string, *config.Config) error RereadTargetLUNMap() Close() error + Resize(uint64) error + Stats() Stats +} + +type Stats struct { + IsClientConnected bool + RevisionCounter int64 + ReplicaCounter int64 + SCSIIOCount map[int]int64 + + ReadIOPS int64 + TotalReadTime int64 + TotalReadBlockCount int64 + + WriteIOPS int64 + TotalWriteTime int64 + TotalWriteBlockCount int64 + + UsedLogicalBlocks int64 + UsedBlocks int64 + SectorSize int64 } type TargetDriverFunc func(*SCSITargetService) (SCSITargetDriver, error) diff --git a/pkg/scsi/sbc.go b/pkg/scsi/sbc.go index 627234c..ec3a4a8 100644 --- a/pkg/scsi/sbc.go +++ b/pkg/scsi/sbc.go @@ -37,6 +37,11 @@ const ( PR_EA_FN = (1 << 0) ) +var ( + EnableORWrite16 = true + EnablePersistentReservation = true +) + type SBCSCSIDeviceProtocol struct { BaseSCSIDeviceProtocol } @@ -76,7 +81,7 @@ func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error { leave it with a default target name */ - lu.Attrs.SCSIID = "iqn.2016-09.com.gotgt.gostor:iscsi-tgt" + lu.Attrs.SCSIID = SCSIID /* The PRODUCT SERIAL NUMBER field contains right-aligned ASCII data (see 4.3.1) @@ -176,26 +181,29 @@ func NewSBCDevice(deviceType api.SCSIDeviceType) api.SCSIDeviceProtocol { sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN) sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN) - sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{ - {ServiceAction: PR_IN_READ_KEYS, CommandPerformFunc: SPCPRReadKeys}, - {ServiceAction: PR_IN_READ_RESERVATION, CommandPerformFunc: SPCPRReadReservation}, - {ServiceAction: PR_IN_REPORT_CAPABILITIES, CommandPerformFunc: SPCPRReportCapabilities}, - }, 0) - - sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{ - {ServiceAction: PR_OUT_REGISTER, CommandPerformFunc: SPCPRRegister}, - {ServiceAction: PR_OUT_RESERVE, CommandPerformFunc: SPCPRReserve}, - {ServiceAction: PR_OUT_RELEASE, CommandPerformFunc: SPCPRRelease}, - {ServiceAction: PR_OUT_CLEAR, CommandPerformFunc: SPCPRClear}, - {ServiceAction: PR_OUT_PREEMPT, CommandPerformFunc: SPCPRPreempt}, - // {ServiceAction: PR_OUT_PREEMPT_AND_ABORT, CommandPerformFunc: SPCPRPreempt}, - {ServiceAction: PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, CommandPerformFunc: SPCPRRegister}, - {ServiceAction: PR_OUT_REGISTER_AND_MOVE, CommandPerformFunc: SPCPRRegisterAndMove}, - }, 0) + if EnablePersistentReservation { + sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{ + {ServiceAction: PR_IN_READ_KEYS, CommandPerformFunc: SPCPRReadKeys}, + {ServiceAction: PR_IN_READ_RESERVATION, CommandPerformFunc: SPCPRReadReservation}, + {ServiceAction: PR_IN_REPORT_CAPABILITIES, CommandPerformFunc: SPCPRReportCapabilities}, + }, 0) + sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{ + {ServiceAction: PR_OUT_REGISTER, CommandPerformFunc: SPCPRRegister}, + {ServiceAction: PR_OUT_RESERVE, CommandPerformFunc: SPCPRReserve}, + {ServiceAction: PR_OUT_RELEASE, CommandPerformFunc: SPCPRRelease}, + {ServiceAction: PR_OUT_CLEAR, CommandPerformFunc: SPCPRClear}, + {ServiceAction: PR_OUT_PREEMPT, CommandPerformFunc: SPCPRPreempt}, + // {ServiceAction: PR_OUT_PREEMPT_AND_ABORT, CommandPerformFunc: SPCPRPreempt}, + {ServiceAction: PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, CommandPerformFunc: SPCPRRegister}, + {ServiceAction: PR_OUT_REGISTER_AND_MOVE, CommandPerformFunc: SPCPRRegisterAndMove}, + }, 0) + } sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN) - sbc.SCSIDeviceOps[api.ORWRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) + if EnableORWrite16 { + sbc.SCSIDeviceOps[api.ORWRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) + } sbc.SCSIDeviceOps[api.WRITE_VERIFY_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.VERIFY_16] = NewSCSIDeviceOperation(SBCVerify, nil, PR_EA_FA|PR_EA_FN) @@ -459,7 +467,9 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { err, key, asc = bsPerformCommand(dev.Storage, cmd) if err != nil { - goto sense + log.Errorf("Error from backend: %v", err) + BuildSenseData(cmd, key, asc) + return api.SAMStatBusy } else { return api.SAMStatGood } @@ -533,7 +543,9 @@ overflow: cmd.InSDBBuffer.Resid = 8 return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, key, asc) return api.SAMStatCheckCondition } @@ -679,7 +691,9 @@ func SBCGetLbaStatus(host int, cmd *api.SCSICommand) api.SAMStat { } return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, key, asc) return api.SAMStatCheckCondition } diff --git a/pkg/scsi/scsi.go b/pkg/scsi/scsi.go index 119bfa6..dd2aa67 100644 --- a/pkg/scsi/scsi.go +++ b/pkg/scsi/scsi.go @@ -53,6 +53,20 @@ func (s *SCSITargetService) GetTargetList() ([]api.SCSITarget, error) { return result, nil } +func (s *SCSITargetService) Resize(size uint64) error { + s.mutex.Lock() + //TODO for multiple LUNs + for _, t := range s.Targets { + if t.Devices != nil { + for i := range t.Devices { + t.Devices[i].Size = size + } + } + } + s.mutex.Unlock() + return nil +} + func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) error { var ( target *api.SCSITarget @@ -96,7 +110,7 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro result := scmd.Device.PerformCommand(tid, scmd) if result != api.SAMStatGood { scmd.Result = result.Stat - log.Warnf("%v", result.Err) + log.Warnf("opcode: %xh err: %v", scmd.OpCode, result.Err) } return nil } @@ -128,9 +142,18 @@ func NewSCSIDeviceOperation(fn api.CommandFunc, sa []*SCSIServiceAction, pr uint func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) { senseBuffer := &bytes.Buffer{} inBufLen, ok := SCSICDBBufXLength(cmd.SCB) - var length uint32 = 0xa + var ( + length uint32 = 0xa + fixedFormat bool = true + ) - if cmd.Device.Attrs.SenseFormat { + if cmd.Device != nil { + if cmd.Device.Attrs.SenseFormat { + fixedFormat = false + } + } + + if !fixedFormat { // descriptor format // current, not deferred senseBuffer.WriteByte(0x72) diff --git a/pkg/scsi/scsilumap.go b/pkg/scsi/scsilumap.go index b6106cc..a6fb823 100644 --- a/pkg/scsi/scsilumap.go +++ b/pkg/scsi/scsilumap.go @@ -17,6 +17,7 @@ limitations under the License. package scsi import ( + "errors" "fmt" "strconv" "sync" @@ -33,9 +34,15 @@ type SCSILUMap struct { AllDevices api.LUNMap // use target name as the key for target's LUN map TargetsLUNMap map[string]api.LUNMap + + TargetsBSMap map[string]api.RemoteBackingStore /* use target name as the key for target's Backing Store (temp) */ } -var globalSCSILUMap = SCSILUMap{AllDevices: make(api.LUNMap), TargetsLUNMap: make(map[string]api.LUNMap)} +var globalSCSILUMap = SCSILUMap{ + AllDevices: make(api.LUNMap), + TargetsLUNMap: make(map[string]api.LUNMap), + TargetsBSMap: make(map[string]api.RemoteBackingStore), +} type LUNMapping struct { TargetName string @@ -71,6 +78,18 @@ func GetTargetLUNMap(tgtName string) api.LUNMap { return lunMap } +func GetTargetBSMap(tgtName string) (api.RemoteBackingStore, error) { + globalSCSILUMap.mutex.RLock() + defer globalSCSILUMap.mutex.RUnlock() + + bs, ok := globalSCSILUMap.TargetsBSMap[tgtName] + if !ok { + return nil, errors.New("Remote backing store is not found in globalSCSILUMap") + } + + return bs, nil +} + func AddBackendStorage(bs config.BackendStorage) error { globalSCSILUMap.mutex.Lock() defer globalSCSILUMap.mutex.Unlock() @@ -130,3 +149,30 @@ func InitSCSILUMap(config *config.Config) error { } return nil } + +func InitSCSILUMapEx(config *config.BackendStorage, tgtName string, lun uint64, bs api.RemoteBackingStore) error { + if bs == nil { + return errors.New("Remote backing store is nil") + } + + globalSCSILUMap.mutex.Lock() + globalSCSILUMap.TargetsBSMap[tgtName] = bs + globalSCSILUMap.mutex.Unlock() + + lu, err := NewSCSILu(config) + if err != nil { + return fmt.Errorf("Init SCSI LU map error, err: %v", err) + } + + globalSCSILUMap.mutex.Lock() + globalSCSILUMap.AllDevices[config.DeviceID] = lu + globalSCSILUMap.mutex.Unlock() + + mappingLUN(LUNMapping{ + DeviceID: config.DeviceID, + LUN: lun, + TargetName: tgtName, + }, + ) + return nil +} diff --git a/pkg/scsi/spc.go b/pkg/scsi/spc.go index 942ce74..6ed3ee4 100644 --- a/pkg/scsi/spc.go +++ b/pkg/scsi/spc.go @@ -28,8 +28,12 @@ import ( log "github.com/sirupsen/logrus" ) +var ( + EnableMultipath = true +) + func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat { - BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) + BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_OP_CODE) return api.SAMStatCheckCondition } @@ -325,7 +329,11 @@ func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat { } //byte 5 //SCCS(0) AAC(0) TPGS(0) 3PC(0) PROTECT(0) - addBuf.WriteByte(INQUIRY_TPGS_IMPLICIT) + if EnableMultipath { + addBuf.WriteByte(INQUIRY_TPGS_IMPLICIT) + } else { + addBuf.WriteByte(INQUIRY_TPGS_NO) + } //byte 6 //ENCSERV(0) VS(0) MULTIP(0) ADDR16(0) addBuf.WriteByte(0x00) @@ -454,7 +462,9 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat { copy(cmd.InSDBBuffer.Buffer, buf.Bytes()) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -467,7 +477,9 @@ func SPCStartStop(host int, cmd *api.SCSICommand) api.SAMStat { return api.SAMStatReservationConflict } - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } scb := cmd.SCB pwrcnd = scb[4] & 0xf0 if pwrcnd != 0 { @@ -662,7 +674,9 @@ func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat { return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -711,7 +725,7 @@ func reportOpcodesAll(cmd *api.SCSICommand, rctd int) error { } func reportOpcodeOne(cmd *api.SCSICommand, rctd int, opcode byte, rsa uint16, serviceAction bool) error { - return fmt.Errorf("non support") + return fmt.Errorf("rsa: %xh, sa:%v not supported", rsa, serviceAction) } func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMStat { @@ -748,7 +762,9 @@ func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMSta return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -764,7 +780,9 @@ func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat { fnop := serviceAction.(*SCSIServiceAction) return fnop.CommandPerformFunc(host, cmd) } - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -797,7 +815,9 @@ func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat { cmd.InSDBBuffer.Resid = uint32(additionLength) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -853,7 +873,9 @@ func SPCPRReadReservation(host int, cmd *api.SCSICommand) api.SAMStat { return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -891,7 +913,9 @@ func SPCPRReportCapabilities(host int, cmd *api.SCSICommand) api.SAMStat { cmd.InSDBBuffer.Resid = uint32(actualLength) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -970,7 +994,9 @@ func SPCPRRegister(host int, cmd *api.SCSICommand) api.SAMStat { return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -1032,7 +1058,9 @@ func SPCPRReserve(host int, cmd *api.SCSICommand) api.SAMStat { scsiResOp.Save(tgtName, devUUID) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -1082,7 +1110,9 @@ func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat { } if curRes.Scope != resScope || curRes.Type != resType { - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION) return api.SAMStatCheckCondition } @@ -1113,7 +1143,9 @@ func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat { scsiResOp.Save(tgtName, devUUID) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -1167,7 +1199,9 @@ func SPCPRClear(host int, cmd *api.SCSICommand) api.SAMStat { scsiResOp.Save(tgtName, devUUID) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -1267,7 +1301,9 @@ func SPCPRPreempt(host int, cmd *api.SCSICommand) api.SAMStat { scsiResOp.Save(tgtName, devUUID) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition } @@ -1353,7 +1389,9 @@ found: scsiResOp.Save(tgtName, devUUID) return api.SAMStatGood sense: - cmd.InSDBBuffer.Resid = 0 + if cmd.InSDBBuffer != nil { + cmd.InSDBBuffer.Resid = 0 + } BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) return api.SAMStatCheckCondition }