From 7a2b08ff8054d843de4e3e352bff19ac1e074f96 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Mon, 12 Jun 2017 21:06:57 +0800 Subject: [PATCH] support iscsi task function --- .travis.yml | 1 + pkg/api/types.go | 11 ++++- pkg/port/iscsit/cmd.go | 19 ++++++-- pkg/port/iscsit/conn.go | 63 +++++++++++++++++++++++---- pkg/port/iscsit/iscsid.go | 89 +++++++++++++++++++++++++++----------- pkg/port/iscsit/iscsit.go | 6 ++- pkg/port/iscsit/session.go | 1 + pkg/port/iscsit/task.go | 55 +++++++++++++++++++++++ pkg/scsi/lun.go | 1 + 9 files changed, 206 insertions(+), 40 deletions(-) create mode 100644 pkg/port/iscsit/task.go diff --git a/.travis.yml b/.travis.yml index a007614..1ae2fdf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ script: - ./autogen.sh - ./configure - make + - ./test-tool/iscsi-test-cu -d -A -V --test=iSCSI.iSCSITMF iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=SCSI.TestUnitReady iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=SCSI.ReadCapacity10 iscsi://127.0.0.1:3260/${TARGET}/0 - ./test-tool/iscsi-test-cu -d -A --test=SCSI.ReadCapacity16 iscsi://127.0.0.1:3260/${TARGET}/0 diff --git a/pkg/api/types.go b/pkg/api/types.go index c97020a..938478c 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -151,11 +151,20 @@ type SCSIDataBuffer struct { Resid int32 } +type SCSICommandState uint64 + +var ( + SCSICommandQueued SCSICommandState = 1 + SCSICommandProcessed SCSICommandState = 2 + SCSICommandAsync SCSICommandState = 3 + SCSICommandNotLast SCSICommandState = 4 +) + type SCSICommand struct { Target *SCSITarget DeviceID uint64 Device *SCSILu - State uint64 + State SCSICommandState Direction SCSIDataDirection InSDBBuffer SCSIDataBuffer OutSDBBuffer SCSIDataBuffer diff --git a/pkg/port/iscsit/cmd.go b/pkg/port/iscsit/cmd.go index 10ce684..2778c38 100644 --- a/pkg/port/iscsit/cmd.go +++ b/pkg/port/iscsit/cmd.go @@ -56,6 +56,12 @@ var opCodeMap = map[OpCode]string{ const DataPadding = 4 +type ISCSITaskManagementFunc struct { + Result byte + TaskFunc uint32 + ReferencedTaskTag uint32 +} + type ISCSICommand struct { OpCode OpCode RawHeader []byte @@ -99,6 +105,9 @@ type ISCSICommand struct { Status byte SCSIResponse byte + // Task request + ISCSITaskManagementFunc + // R2T R2TSN uint32 DesiredLength uint32 @@ -187,13 +196,13 @@ func parseHeader(data []byte) (*ISCSICommand, error) { // TODO: sync.Pool m := &ISCSICommand{} m.Immediate = 0x40&data[0] == 0x40 - m.OpCode = OpCode(data[0] & 0x3f) + m.OpCode = OpCode(data[0] & ISCSI_OPCODE_MASK) m.Final = 0x80&data[1] == 0x80 m.AHSLen = int(data[4]) * 4 m.DataLen = int(ParseUint(data[5:8])) m.TaskTag = uint32(ParseUint(data[16:20])) switch m.OpCode { - case OpSCSICmd, OpSCSITaskReq: + case OpSCSICmd: m.LUN = [8]byte{data[9]} m.ExpectedDataLen = uint32(ParseUint(data[20:24])) m.CmdSN = uint32(ParseUint(data[24:28])) @@ -201,6 +210,10 @@ func parseHeader(data []byte) (*ISCSICommand, error) { m.Write = data[1]&0x20 == 0x20 m.CDB = data[32:48] m.ExpStatSN = uint32(ParseUint(data[28:32])) + fallthrough + case OpSCSITaskReq: + m.ReferencedTaskTag = uint32(ParseUint(data[20:24])) + m.TaskFunc = uint32(data[1] & ISCSI_FLAG_TM_FUNC_MASK) case OpSCSIResp: case OpSCSIOut: m.LUN = [8]byte{data[9]} @@ -399,7 +412,7 @@ func (m *ISCSICommand) scsiTMFRespBytes() []byte { buf := &bytes.Buffer{} buf.WriteByte(byte(OpSCSITaskResp)) buf.WriteByte(0x80) - buf.WriteByte(0x00) + buf.WriteByte(m.Result) buf.WriteByte(0x00) // Skip through to byte 16 diff --git a/pkg/port/iscsit/conn.go b/pkg/port/iscsit/conn.go index f9b0775..44942ab 100644 --- a/pkg/port/iscsit/conn.go +++ b/pkg/port/iscsit/conn.go @@ -93,11 +93,12 @@ const ( ) type iscsiTask struct { - tag uint32 - conn *iscsiConnection - cmd *ISCSICommand - scmd *api.SCSICommand - state taskState + tag uint32 + conn *iscsiConnection + cmd *ISCSICommand + scmd *api.SCSICommand + state taskState + result byte offset int r2tCount int @@ -143,11 +144,13 @@ func (conn *iscsiConnection) ReInstatement(newConn *iscsiConnection) { conn.conn = newConn.conn } -func (conn *iscsiConnection) buildRespPackage(oc OpCode) error { +func (conn *iscsiConnection) buildRespPackage(oc OpCode, task *iscsiTask) error { conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}} conn.txIOState = IOSTATE_TX_BHS conn.statSN += 1 - task := conn.rxTask + if task == nil { + task = conn.rxTask + } resp := &ISCSICommand{ StatSN: conn.req.ExpStatSN, TaskTag: conn.req.TaskTag, @@ -182,11 +185,17 @@ func (conn *iscsiConnection) buildRespPackage(oc OpCode) error { if scmd.Result != 0 && scmd.SenseBuffer != nil { resp.RawData = scmd.SenseBuffer.Bytes() } - case OpNoopIn, OpSCSITaskResp, OpReject: + case OpNoopIn, OpReject: resp.OpCode = oc resp.Final = true resp.NSG = FullFeaturePhase resp.ExpCmdSN = conn.req.CmdSN + 1 + case OpSCSITaskResp: + resp.OpCode = oc + resp.Final = true + resp.NSG = FullFeaturePhase + resp.ExpCmdSN = conn.req.CmdSN + 1 + resp.Result = task.result case OpLoginResp: resp.OpCode = OpLoginResp resp.Transit = conn.loginParam.tgtTrans @@ -209,3 +218,41 @@ func (conn *iscsiConnection) buildRespPackage(oc OpCode) error { conn.resp = resp return nil } + +func (conn *iscsiConnection) State() string { + switch conn.state { + case CONN_STATE_FREE: + return "free" + case CONN_STATE_SECURITY: + return "begin security" + case CONN_STATE_SECURITY_AUTH: + return "security auth" + case CONN_STATE_SECURITY_DONE: + return "done security" + case CONN_STATE_SECURITY_LOGIN: + return "security login" + case CONN_STATE_SECURITY_FULL: + return "security full" + case CONN_STATE_LOGIN: + return "begin login" + case CONN_STATE_LOGIN_FULL: + return "done login" + case CONN_STATE_FULL: + return "full feature" + case CONN_STATE_KERNEL: + return "kernel" + case CONN_STATE_CLOSE: + return "close" + case CONN_STATE_EXIT: + return "exit" + case CONN_STATE_SCSI: + return "scsi" + case CONN_STATE_INIT: + return "init" + case CONN_STATE_START: + return "start" + case CONN_STATE_READY: + return "ready" + } + return "" +} diff --git a/pkg/port/iscsit/iscsid.go b/pkg/port/iscsit/iscsid.go index 6feda45..ed988b9 100644 --- a/pkg/port/iscsit/iscsid.go +++ b/pkg/port/iscsit/iscsid.go @@ -1,5 +1,5 @@ /* -Copyright 2016 The GoStor Authors All rights reserved. +Copyright 2017 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. @@ -45,12 +45,12 @@ type ISCSITargetDriver struct { } func init() { - scsi.RegisterTargetDriver("iscsi", NewISCSITargetDriver) + scsi.RegisterTargetDriver(iSCSIDriverName, NewISCSITargetDriver) } func NewISCSITargetDriver(base *scsi.SCSITargetService) (scsi.SCSITargetDriver, error) { return &ISCSITargetDriver{ - Name: "iscsi", + Name: iSCSIDriverName, iSCSITargets: map[string]*ISCSITarget{}, SCSI: base, TSIHPool: map[uint16]bool{0: true, 65535: true}, @@ -111,11 +111,11 @@ func (s *ISCSITargetDriver) AddiSCSIPortal(tgtName string, tpgt uint16, portal s ) if target, ok = s.iSCSITargets[tgtName]; !ok { - return fmt.Errorf("no target %s", tgtName) + return fmt.Errorf("No such target: %s", tgtName) } if tpgtInfo, ok = target.TPGTs[tpgt]; !ok { - return fmt.Errorf("no tpgt %d", tpgt) + return fmt.Errorf("No such TPGT: %d", tpgt) } tgtPortals := tpgtInfo.Portals @@ -356,7 +356,7 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error { conn.state = CONN_STATE_LOGIN } - return conn.buildRespPackage(OpLoginResp) + return conn.buildRespPackage(OpLoginResp, nil) } func iscsiExecLogout(conn *iscsiConnection) error { @@ -411,19 +411,15 @@ func (s *ISCSITargetDriver) iscsiExecText(conn *iscsiConnection) error { } func iscsiExecNoopOut(conn *iscsiConnection) error { - return conn.buildRespPackage(OpNoopIn) -} - -func iscsiExecTMFunction(conn *iscsiConnection) error { - return conn.buildRespPackage(OpSCSITaskResp) + return conn.buildRespPackage(OpNoopIn, nil) } func iscsiExecReject(conn *iscsiConnection) error { - return conn.buildRespPackage(OpReject) + return conn.buildRespPackage(OpReject, nil) } func iscsiExecR2T(conn *iscsiConnection) error { - return conn.buildRespPackage(OpReady) + return conn.buildRespPackage(OpReady, nil) } func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) { @@ -483,27 +479,21 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) { } } - log.Debugf("connection state: %d", conn.state) + log.Debugf("connection state: %v", conn.State()) switch conn.state { case CONN_STATE_CLOSE, CONN_STATE_EXIT: - log.Warnf("set connection to close") conn.state = CONN_STATE_CLOSE case CONN_STATE_SECURITY_LOGIN: conn.state = CONN_STATE_LOGIN - log.Debugf("CONN_STATE_LOGIN") case CONN_STATE_LOGIN: - log.Debugf("CONN_STATE_LOGIN") conn.rxIOState = IOSTATE_RX_BHS s.handler(DATAIN, conn) case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL: if conn.session.SessionType == SESSION_NORMAL { conn.state = CONN_STATE_KERNEL - log.Debugf("CONN_STATE_KERNEL") conn.state = CONN_STATE_SCSI - log.Debugf("CONN_STATE_SCSI") } else { conn.state = CONN_STATE_FULL - log.Debugf("CONN_STATE_FULL") } conn.rxIOState = IOSTATE_RX_BHS s.handler(DATAIN, conn) @@ -560,17 +550,19 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error return } else { if scmd.Direction == api.SCSIDataRead { - conn.buildRespPackage(OpSCSIIn) + conn.buildRespPackage(OpSCSIIn, task) } else { - conn.buildRespPackage(OpSCSIResp) + conn.buildRespPackage(OpSCSIResp, task) } conn.rxTask = nil } case OpSCSITaskReq: // task management function - conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, state: taskPending} - conn.txIOState = IOSTATE_TX_BHS - iscsiExecTMFunction(conn) + task := &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: nil} + conn.rxTask = task + if err = s.iscsiTaskQueueHandler(task); err != nil { + return + } case OpSCSIOut: log.Debugf("iSCSI Data-out processing...") var task *iscsiTask @@ -612,7 +604,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error if err = s.iscsiExecTask(task); err != nil { return } else { - conn.buildRespPackage(OpSCSIResp) + conn.buildRespPackage(OpSCSIResp, task) conn.rxTask = nil } case OpNoopOut: @@ -657,13 +649,16 @@ func (s *ISCSITargetDriver) iscsiTaskQueueHandler(task *iscsiTask) error { if len(sess.PendingTasks) == 0 { return nil } + sess.PendingTasksMutex.Lock() task = sess.PendingTasks.Pop().(*iscsiTask) cmd = task.cmd if cmd.CmdSN != cmdsn { sess.PendingTasks.Push(task) + sess.PendingTasksMutex.Unlock() return nil } task.state = taskSCSI + sess.PendingTasksMutex.Unlock() goto retry } else { if cmd.CmdSN < sess.ExpCmdSN { @@ -673,8 +668,10 @@ func (s *ISCSITargetDriver) iscsiTaskQueueHandler(task *iscsiTask) error { } log.Debugf("add task(%d) into task queue", task.cmd.CmdSN) // add this connection into queue and set this task as pending task + sess.PendingTasksMutex.Lock() task.state = taskPending sess.PendingTasks.Push(task) + sess.PendingTasksMutex.Unlock() return fmt.Errorf("pending") } @@ -716,6 +713,46 @@ func (s *ISCSITargetDriver) iscsiExecTask(task *iscsiTask) error { case OpNoopOut: // just do it in iscsi layer + case OpSCSITaskReq: + sess := task.conn.session + switch cmd.TaskFunc { + case ISCSI_TM_FUNC_ABORT_TASK: + stask := &iscsiTask{} + sess.PendingTasksMutex.Lock() + for i, t := range sess.PendingTasks { + if cmd.ReferencedTaskTag == t.tag { + stask = sess.PendingTasks[i] + sess.PendingTasks = append(sess.PendingTasks[:i], sess.PendingTasks[i+1:]...) + break + } + } + sess.PendingTasksMutex.Unlock() + if stask == nil { + task.result = ISCSI_TMF_RSP_NO_TASK + } else { + // abort this task + log.Debugf("abort the task[%v]", stask.tag) + stask.scmd.Result = api.SAM_STAT_TASK_ABORTED + stask.conn.buildRespPackage(OpSCSIResp, stask) + stask.conn.rxTask = nil + s.handler(DATAOUT, stask.conn) + task.result = ISCSI_TMF_RSP_COMPLETE + } + case ISCSI_TM_FUNC_ABORT_TASK_SET: + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + case ISCSI_TM_FUNC_CLEAR_ACA: + fallthrough + case ISCSI_TM_FUNC_CLEAR_TASK_SET: + 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 } diff --git a/pkg/port/iscsit/iscsit.go b/pkg/port/iscsit/iscsit.go index 0c4714f..9b0d445 100644 --- a/pkg/port/iscsit/iscsit.go +++ b/pkg/port/iscsit/iscsit.go @@ -25,6 +25,8 @@ import ( "github.com/gostor/gotgt/pkg/api" ) +const iSCSIDriverName = "iscsi" + const ( IOSTATE_FREE = iota @@ -53,7 +55,7 @@ const ( IOSTATE_TX_END ) -var ISCSI_OPCODE_MASK = 0x3F +var ISCSI_OPCODE_MASK byte = 0x3F type ISCSIDiscoveryMethod string @@ -95,7 +97,7 @@ type ISCSITarget struct { /* * RFC 3720 iSCSI SSID = ISID , TPGT * ISID is an 6 bytes number, TPGT is an 2 bytes number - * We combime ISID and TPGT together to be SSID + * We combine ISID and TPGT together to be SSID */ func MakeSSID(ISID uint64, TPGT uint16) uint64 { SSID := ISID<<16 | uint64(TPGT) diff --git a/pkg/port/iscsit/session.go b/pkg/port/iscsit/session.go index 1cf285e..1c1705e 100644 --- a/pkg/port/iscsit/session.go +++ b/pkg/port/iscsit/session.go @@ -253,6 +253,7 @@ type ISCSISession struct { ConnectionsRWMutex sync.RWMutex Commands []*ISCSICommand PendingTasks taskQueue + PendingTasksMutex sync.RWMutex MaxQueueCommand uint32 SessionParam ISCSISessionParamList Info string diff --git a/pkg/port/iscsit/task.go b/pkg/port/iscsit/task.go new file mode 100644 index 0000000..8d39642 --- /dev/null +++ b/pkg/port/iscsit/task.go @@ -0,0 +1,55 @@ +/* +Copyright 2017 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. +*/ + +// iSCSI task management +package iscsit + +const ( + ISCSI_FLAG_TM_FUNC_MASK byte = 0x7F + + // Function values + // aborts the task identified by the Referenced Task Tag field + ISCSI_TM_FUNC_ABORT_TASK = 1 + // aborts all Tasks issued via this session on the logical unit + ISCSI_TM_FUNC_ABORT_TASK_SET = 2 + // clears the Auto Contingent Allegiance condition + ISCSI_TM_FUNC_CLEAR_ACA = 3 + // aborts all Tasks in the appropriate task set as defined by the TST field in the Control mode page + ISCSI_TM_FUNC_CLEAR_TASK_SET = 4 + ISCSI_TM_FUNC_LOGICAL_UNIT_RESET = 5 + ISCSI_TM_FUNC_TARGET_WARM_RESET = 6 + ISCSI_TM_FUNC_TARGET_COLD_RESET = 7 + // reassigns connection allegiance for the task identified by the Referenced Task Tag field to this connection, thus resuming the iSCSI exchanges for the task + ISCSI_TM_FUNC_TASK_REASSIGN = 8 + + // Response values + // Function complete + ISCSI_TMF_RSP_COMPLETE = 0x00 + // Task does not exist + ISCSI_TMF_RSP_NO_TASK = 0x01 + // LUN does not exist + ISCSI_TMF_RSP_NO_LUN = 0x02 + // Task still allegiant + ISCSI_TMF_RSP_TASK_ALLEGIANT = 0x03 + // Task allegiance reassignment not supported + ISCSI_TMF_RSP_NO_FAILOVER = 0x04 + // Task management function not supported + ISCSI_TMF_RSP_NOT_SUPPORTED = 0x05 + // Function authorization failed + ISCSI_TMF_RSP_AUTH_FAILED = 0x06 + // Function rejected + ISCSI_TMF_RSP_REJECTED = 0xff +) diff --git a/pkg/scsi/lun.go b/pkg/scsi/lun.go index 9a381f4..23f2664 100644 --- a/pkg/scsi/lun.go +++ b/pkg/scsi/lun.go @@ -89,6 +89,7 @@ func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat { fnop := fn.(SCSIDeviceOperation) // TODO host := cmd.ITNexus.Host host := 0 + cmd.State = api.SCSICommandProcessed return fnop.CommandPerformFunc(host, cmd) } return api.SAMStatGood