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

View File

@@ -1,9 +1,22 @@
/*
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.
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 port
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port/iscsit"
)
import "github.com/gostor/gotgt/pkg/api"
type SCSITargetDriver interface {
Init() error
@@ -16,7 +29,6 @@ type SCSITargetDriver interface {
CreateLu(lu *api.SCSILu) error
GetLu(lun uint8) (uint64, error)
ProcessCommand(buf []byte) ([]byte, error)
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
}
@@ -58,13 +70,3 @@ func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil
}
func (fake *fakeSCSITargetDriver) ProcessCommand(buf []byte) ([]byte, error) {
return []byte(""), nil
}
func NewTargetDriver(driver string, tgt *api.SCSITarget) SCSITargetDriver {
if driver == "iscsi" {
return iscsit.NewISCSITarget(tgt)
}
return nil
}

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.

View File

@@ -4,6 +4,8 @@ import (
"bytes"
"fmt"
"strings"
"github.com/gostor/gotgt/pkg/util"
)
type OpCode int
@@ -52,6 +54,8 @@ var opCodeMap = map[OpCode]string{
OpReject: "Reject",
}
const DataPadding = 4
type ISCSICommand struct {
OpCode OpCode
RawHeader []byte
@@ -68,7 +72,7 @@ type ISCSICommand struct {
ExpStatSN uint32 // Expected status serial.
Read, Write bool
LUN uint8
LUN [8]uint8
Transit bool // Transit bit.
Cont bool // Continue bit.
CSG, NSG Stage // Current Stage, Next Stage.
@@ -102,6 +106,10 @@ func (cmd *ISCSICommand) Bytes() []byte {
return cmd.scsiCmdRespBytes()
case OpSCSIIn:
return cmd.dataInBytes()
case OpTextResp:
return cmd.textRespBytes()
case OpNoopIn:
return cmd.noopInBytes()
}
return nil
}
@@ -155,16 +163,8 @@ func ParseUint(data []byte) uint64 {
return out
}
func MarshalUint64(i uint64) []byte {
var data []byte
for j := 0; j < 8; j++ {
b := byte(i >> uint(8*(7-j)) & 0xff)
data = append(data, b)
}
return data
}
func parseHeader(data []byte) (*ISCSICommand, error) {
if len(data) != 48 {
if len(data) != BHS_SIZE {
return nil, fmt.Errorf("garbled header")
}
// TODO: sync.Pool
@@ -177,7 +177,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
m.TaskTag = uint32(ParseUint(data[16:20]))
switch m.OpCode {
case OpSCSICmd:
m.LUN = uint8(data[9])
m.LUN = [8]uint8{data[9]}
m.ExpectedDataLen = uint32(ParseUint(data[20:24]))
m.CmdSN = uint32(ParseUint(data[24:28]))
m.Read = data[1]&0x40 == 0x40
@@ -185,7 +185,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
m.CDB = data[32:48]
m.ExpStatSN = uint32(ParseUint(data[28:32]))
case OpSCSIResp:
case OpLoginReq:
case OpLoginReq, OpTextReq, OpNoopOut, OpLogoutReq:
m.Transit = m.Final
m.Cont = data[1]&0x40 == 0x40
if m.Cont && m.Transit {
@@ -229,13 +229,13 @@ func (m *ISCSICommand) scsiCmdRespBytes() []byte {
for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00)
}
@@ -259,24 +259,22 @@ func (m *ISCSICommand) dataInBytes() []byte {
}
buf.WriteByte(b)
buf.WriteByte(0x00) // 4
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
buf.WriteByte(0x00)
buf.WriteByte(byte(m.LUN))
buf.WriteByte(0x00) // 4
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
// Skip through to byte 16
for i := 0; i < 6; i++ {
buf.WriteByte(0x00)
for i := 0; i < 8; i++ {
buf.WriteByte(byte(m.LUN[i]))
}
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ {
// 11.7.4
buf.WriteByte(0xff)
}
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.DataSN))[4:])
buf.Write(MarshalUint64(uint64(m.BufferOffset))[4:])
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.DataSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.BufferOffset))[4:])
for i := 0; i < 4; i++ {
buf.WriteByte(0x00)
}
@@ -290,123 +288,79 @@ func (m *ISCSICommand) dataInBytes() []byte {
return buf.Bytes()
}
type InquiryData struct {
PeripheralQualifier int
PeripheralType int
Removable bool
Version int
SupportsACA bool
Hierarchical bool
SupportsSCC bool
HasACC bool
TargetGroupSupport int
ThirdPartyCopy bool
Protect bool
EnclosureServices bool
Multiport bool
MediaChanger bool
Vendor [8]byte
Product [16]byte
RevisionLevel [4]byte
SerialNumber uint64
}
func (id *InquiryData) bytes() []byte {
func (m *ISCSICommand) textRespBytes() []byte {
buf := &bytes.Buffer{}
buf.WriteByte(byte(OpTextResp))
var b byte
b = (uint8(id.PeripheralQualifier) << 5) & 0xe0
b |= uint8(id.PeripheralType) & 0x1f
buf.WriteByte(b)
b = 0
if id.Removable {
b = 0x80
}
buf.WriteByte(b)
buf.WriteByte(byte(id.Version))
b = 0x02
if id.SupportsACA {
b |= 0x20
}
if id.Hierarchical {
b |= 0x10
}
buf.WriteByte(b)
buf.WriteByte(0x00)
// byte 5
b = 0
if id.SupportsSCC {
if m.Final {
b |= 0x80
}
if id.HasACC {
if m.Cont {
b |= 0x40
}
b |= byte(id.TargetGroupSupport) << 4 & 0x30
if id.ThirdPartyCopy {
b |= 0x08
}
if id.Protect {
b |= 0x01
}
// byte 1
buf.WriteByte(b)
// byte 6
b = 0
if id.EnclosureServices {
b |= 0x40
}
if id.Multiport {
b |= 0x10
}
if id.MediaChanger {
b |= 0x08
}
buf.WriteByte(b)
buf.WriteByte(0x02)
buf.Write(id.Vendor[:])
buf.Write(id.Product[:])
buf.Write(id.RevisionLevel[:])
buf.Write(MarshalUint64(id.SerialNumber))
for i := 0; i < 12; i++ {
buf.WriteByte(b)
buf.WriteByte(b)
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
// Skip through to byte 12
for i := 0; i < 2*4; i++ {
buf.WriteByte(0x00)
}
data := buf.Bytes()
data[4] = byte(len(data) - 4)
return data
}
type Capacity struct {
LBA uint64
Blocksize uint32
ProtectionType uint8
PIExponent uint8
LogicalExponent uint8
ThinProvisioned bool
ThinProvReturnsZeros bool
LowestLBA uint16
}
func (c *Capacity) bytes() []byte {
// table 111
// http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
buf := &bytes.Buffer{}
buf.Write(MarshalUint64(c.LBA))
buf.Write(MarshalUint64(uint64(c.Blocksize))[4:])
var b byte
if c.ProtectionType > 0 {
b |= 0x01
b |= c.ProtectionType << 1
b &= 0x0f
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ {
buf.WriteByte(0xff)
}
buf.WriteByte(b)
b = c.PIExponent << 4
b |= c.LogicalExponent
buf.WriteByte(b)
lowLBA := MarshalUint64(uint64(c.LowestLBA))[6:]
lowLBA[0] &= 0x3f
if c.ThinProvisioned {
lowLBA[0] &= 0x80
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00)
}
if c.ThinProvReturnsZeros {
lowLBA[0] &= 0x40
rd := m.RawData
for len(rd)%4 != 0 {
rd = append(rd, 0)
}
buf.Write(rd)
return buf.Bytes()
}
func (m *ISCSICommand) noopInBytes() []byte {
buf := &bytes.Buffer{}
buf.WriteByte(byte(OpNoopIn))
var b byte
b |= 0x80
// byte 1
buf.WriteByte(b)
b = 0
buf.WriteByte(b)
buf.WriteByte(b)
buf.WriteByte(b)
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
// Skip through to byte 12
for i := 0; i < 2*4; i++ {
buf.WriteByte(0x00)
}
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ {
buf.WriteByte(0xff)
}
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00)
}
rd := m.RawData
for len(rd)%4 != 0 {
rd = append(rd, 0)
}
buf.Write(rd)
return buf.Bytes()
}

125
pkg/port/iscsit/conn.go Normal file
View File

@@ -0,0 +1,125 @@
/*
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.
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 iscsit
import (
"net"
"github.com/gostor/gotgt/pkg/api"
)
const (
CONN_STATE_FREE = 0
CONN_STATE_SECURITY = 1
CONN_STATE_SECURITY_AUTH = 2
CONN_STATE_SECURITY_DONE = 3
CONN_STATE_SECURITY_LOGIN = 4
CONN_STATE_SECURITY_FULL = 5
CONN_STATE_LOGIN = 6
CONN_STATE_LOGIN_FULL = 7
CONN_STATE_FULL = 8
CONN_STATE_KERNEL = 9
CONN_STATE_CLOSE = 10
CONN_STATE_EXIT = 11
CONN_STATE_SCSI = 12
CONN_STATE_INIT = 13
CONN_STATE_START = 14
CONN_STATE_READY = 15
)
var (
DATAIN byte = 0x01
DATAOUT byte = 0x10
)
type iscsiConnection struct {
state int
authState int
session *ISCSISession
sessionType int
sessionParam []ISCSISessionParam
tid int
cid uint16
rxIOState int
txIOState int
refcount int
conn net.Conn
initiator string
initiatorAlias string
rxBuffer []byte
txBuffer []byte
req *ISCSICommand
resp *ISCSICommand
// StatSN - the status sequence number on this connection
statSN uint32
// ExpStatSN - the expected status sequence number on this connection
expStatSN uint32
// CmdSN - the command sequence number at the target
cmdSN uint32
// ExpCmdSN - the next expected command sequence number at the target
expCmdSN uint32
// MaxCmdSN - the maximum CmdSN acceptable at the target from this initiator
maxCmdSN uint32
rxTask *iscsiTask
txTask *iscsiTask
authMethod AuthMethod
}
type taskState int
const (
taskPending taskState = 1
taskSCSI taskState = 2
)
type iscsiTask struct {
tag uint32
conn *iscsiConnection
cmd *ISCSICommand
scmd *api.SCSICommand
state taskState
}
func (c *iscsiConnection) init() {
c.state = CONN_STATE_FREE
c.refcount = 1
c.sessionParam = []ISCSISessionParam{}
for _, param := range sessionKeys {
c.sessionParam = append(c.sessionParam, ISCSISessionParam{Value: param.def})
}
}
func (c *iscsiConnection) readData(size int) ([]byte, int, error) {
var buf = make([]byte, size)
length, err := c.conn.Read(buf)
if err != nil {
return nil, -1, err
}
return buf, length, nil
}
func (c *iscsiConnection) write(resp []byte) (int, error) {
return c.conn.Write(resp)
}
func (c *iscsiConnection) close() {
c.conn.Close()
}

592
pkg/port/iscsit/iscsid.go Normal file
View File

@@ -0,0 +1,592 @@
/*
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.
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 iscsit
import (
"bytes"
"fmt"
"net"
"os"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port"
"github.com/gostor/gotgt/pkg/scsi"
"github.com/gostor/gotgt/pkg/util"
)
type ISCSITargetService struct {
SCSI *scsi.SCSITargetService
Name string
Targets map[string]*ISCSITarget
}
func init() {
port.RegisterTargetService("iscsi", NewISCSITargetService)
}
func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService, error) {
return &ISCSITargetService{
Name: "iscsi",
Targets: map[string]*ISCSITarget{},
SCSI: base,
}, nil
}
func (s *ISCSITargetService) NewTarget(target string) (port.SCSITargetDriver, error) {
if _, ok := s.Targets[target]; ok {
return nil, fmt.Errorf("target name has been existed")
}
stgt, err := s.SCSI.NewSCSITarget(len(s.Targets), "iscsi", target)
if err != nil {
return nil, err
}
tgt := newISCSITarget(stgt)
s.Targets[target] = tgt
return tgt, nil
}
func (s *ISCSITargetService) Run() error {
l, err := net.Listen("tcp", ":3260")
if err != nil {
glog.Error(err)
os.Exit(1)
}
defer l.Close()
for {
glog.Info("Listening ...")
conn, err := l.Accept()
if err != nil {
glog.Error(err)
}
glog.Info("Accepting ...")
iscsiConn := &iscsiConnection{conn: conn}
iscsiConn.init()
iscsiConn.rxIOState = IOSTATE_RX_BHS
glog.Infof("connection is connected from %s...\n", conn.RemoteAddr().String())
// start a new thread to do with this command
go s.handler(DATAIN, iscsiConn)
}
return nil
}
func (s *ISCSITargetService) handler(events byte, conn *iscsiConnection) {
if events&DATAIN != 0 {
glog.Infof("rx handler processing...")
go s.rxHandler(conn)
}
if conn.state != CONN_STATE_CLOSE && events&DATAOUT != 0 {
glog.Infof("tx handler processing...")
s.txHandler(conn)
}
if conn.state == CONN_STATE_CLOSE {
glog.Infof("iscsi connection[%d] closed", conn.cid)
conn.close()
}
}
func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
var (
hdigest uint = 0
ddigest uint = 0
final bool = false
cmd *ISCSICommand
)
if conn.state == CONN_STATE_SCSI {
hdigest = conn.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
ddigest = conn.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
}
for {
switch conn.rxIOState {
case IOSTATE_RX_BHS:
glog.Infof("rx handler: IOSTATE_RX_BHS")
buf, length, err := conn.readData(BHS_SIZE)
if err != nil {
glog.Error(err)
return
}
if length == 0 {
conn.state = CONN_STATE_CLOSE
return
}
conn.rxBuffer = buf
cmd, err = parseHeader(buf)
if err != nil {
glog.Error(err)
conn.state = CONN_STATE_CLOSE
return
}
conn.req = cmd
if length == BHS_SIZE && cmd.DataLen != 0 {
conn.rxIOState = IOSTATE_RX_INIT_AHS
break
}
glog.Infof("got command: \n%s", cmd.String())
glog.Infof("got buffer: %v", buf)
final = true
case IOSTATE_RX_INIT_AHS:
conn.rxIOState = IOSTATE_RX_DATA
break
if hdigest != 0 {
conn.rxIOState = IOSTATE_RX_INIT_HDIGEST
}
case IOSTATE_RX_DATA:
if ddigest != 0 {
conn.rxIOState = IOSTATE_RX_INIT_DDIGEST
}
if cmd == nil {
return
}
dl := ((cmd.DataLen + DataPadding - 1) / DataPadding) * DataPadding
buf, length, err := conn.readData(dl)
if err != nil {
glog.Error(err)
return
}
if length != dl {
conn.state = CONN_STATE_CLOSE
return
}
cmd.RawData = buf[:cmd.DataLen]
conn.rxBuffer = append(conn.rxBuffer, buf...)
final = true
glog.Infof("got command: \n%s", cmd.String())
default:
glog.Errorf("error %d %d\n", conn.state, conn.rxIOState)
return
}
if final {
break
}
}
if conn.state == CONN_STATE_SCSI {
s.scsiCommandHandler(conn)
} else {
conn.txIOState = IOSTATE_TX_BHS
conn.resp = &ISCSICommand{}
switch conn.req.OpCode {
case OpLoginReq:
glog.Infof("OpLoginReq")
if err := s.iscsiExecLogin(conn); err != nil {
glog.Error(err)
conn.state = CONN_STATE_CLOSE
}
case OpLogoutReq:
glog.Infof("OpLogoutReq")
if err := iscsiExecLogout(conn); err != nil {
conn.state = CONN_STATE_CLOSE
}
case OpTextReq:
glog.Infof("OpTextReq")
if err := s.iscsiExecText(conn); err != nil {
conn.state = CONN_STATE_CLOSE
}
default:
iscsiExecReject(conn)
}
glog.Infof("%#v", conn.resp.String())
s.handler(DATAOUT, conn)
}
}
func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
var (
target *ISCSITarget
cmd = conn.req
)
conn.resp = &ISCSICommand{
OpCode: OpLoginResp,
Transit: true,
CSG: cmd.CSG,
NSG: FullFeaturePhase,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
ExpCmdSN: cmd.CmdSN,
MaxCmdSN: cmd.CmdSN,
RawData: util.MarshalKVText([]util.KeyValue{
{"HeaderDigest", "None"},
{"DataDigest", "None"},
}),
}
pairs := util.ParseKVText(cmd.RawData)
if initiatorName, ok := pairs["InitiatorName"]; ok {
conn.initiator = initiatorName
}
if alias, ok := pairs["InitiatorAlias"]; ok {
conn.initiatorAlias = alias
}
targetName := pairs["TargetName"]
if sessType, ok := pairs["SessionType"]; ok {
if sessType == "Normal" {
conn.sessionType = SESSION_NORMAL
} else {
conn.sessionType = SESSION_DISCOVERY
}
}
if conn.sessionType == SESSION_DISCOVERY {
conn.tid = 0xffff
} else {
for _, t := range s.Targets {
if t.SCSITarget.Name == targetName {
target = t
break
}
}
if target == nil {
conn.state = CONN_STATE_EXIT
return fmt.Errorf("No target found with name(%s)", targetName)
}
conn.tid = target.TID
}
switch conn.state {
case CONN_STATE_FREE:
conn.state = CONN_STATE_SECURITY
case CONN_STATE_SECURITY:
}
conn.state = CONN_STATE_LOGIN_FULL
conn.expCmdSN = cmd.CmdSN
conn.statSN += 1
switch conn.sessionType {
case SESSION_NORMAL:
if conn.session == nil {
// create a new session
sess, err := s.NewISCSISession(conn)
if err != nil {
glog.Error(err)
return err
}
conn.session = sess
}
case SESSION_DISCOVERY:
}
return nil
}
func iscsiExecLogout(conn *iscsiConnection) error {
cmd := conn.req
conn.resp = &ISCSICommand{
OpCode: OpLogoutResp,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
}
if conn.session == nil {
conn.resp.ExpCmdSN = cmd.CmdSN
conn.resp.MaxCmdSN = cmd.CmdSN
} else {
conn.resp.ExpCmdSN = conn.session.ExpCmdSN
conn.resp.MaxCmdSN = conn.session.ExpCmdSN + 10
}
return nil
}
func (s *ISCSITargetService) iscsiExecText(conn *iscsiConnection) error {
var result = []util.KeyValue{}
cmd := conn.req
keys := util.ParseKVText(cmd.RawData)
if st, ok := keys["SendTargets"]; ok {
if st == "All" {
list, err := s.SCSI.GetTargetList()
if err != nil {
return err
}
for _, t := range list {
result = append(result, util.KeyValue{"TargetName", t.Name})
result = append(result, util.KeyValue{"TargetAddress", "127.0.0.1:3260,1"})
}
}
}
conn.resp = &ISCSICommand{
OpCode: OpTextResp,
Final: true,
NSG: FullFeaturePhase,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
ExpCmdSN: cmd.CmdSN,
MaxCmdSN: cmd.CmdSN,
}
conn.resp.RawData = util.MarshalKVText(result)
return nil
}
func iscsiExecNoopOut(conn *iscsiConnection) error {
cmd := conn.req
conn.resp = &ISCSICommand{
OpCode: OpNoopIn,
Final: true,
NSG: FullFeaturePhase,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
ExpCmdSN: cmd.CmdSN + 1,
MaxCmdSN: cmd.CmdSN + 10,
}
return nil
}
func iscsiExecReject(conn *iscsiConnection) error {
conn.resp = &ISCSICommand{
OpCode: OpReject,
}
return nil
}
func (s *ISCSITargetService) txHandler(conn *iscsiConnection) {
var (
hdigest uint = 0
ddigest uint = 0
final bool = false
)
if conn.state == CONN_STATE_SCSI {
hdigest = conn.sessionParam[ISCSI_PARAM_HDRDGST_EN].Value & DIGEST_CRC32C
ddigest = conn.sessionParam[ISCSI_PARAM_DATADGST_EN].Value & DIGEST_CRC32C
}
if conn.state == CONN_STATE_SCSI && conn.txTask == nil {
err := s.scsiCommandHandler(conn)
if err != nil {
glog.Error(err)
return
}
}
for {
switch conn.txIOState {
case IOSTATE_TX_BHS:
glog.V(2).Infof("ready to write response")
glog.V(2).Infof("%s", conn.resp.String())
glog.V(2).Infof("length of RawData is %d", len(conn.resp.RawData))
glog.V(2).Infof("length of resp is %d", len(conn.resp.Bytes()))
if l, err := conn.write(conn.resp.Bytes()); err != nil {
glog.Error(err)
return
} else {
conn.txIOState = IOSTATE_TX_INIT_AHS
glog.V(2).Infof("success to write %d length", l)
}
case IOSTATE_TX_INIT_AHS:
if hdigest != 0 {
conn.txIOState = IOSTATE_TX_INIT_HDIGEST
} else {
conn.txIOState = IOSTATE_TX_INIT_DATA
}
if conn.txIOState != IOSTATE_TX_AHS {
final = true
}
case IOSTATE_TX_AHS:
case IOSTATE_TX_INIT_DATA:
final = true
case IOSTATE_TX_DATA:
if ddigest != 0 {
conn.txIOState = IOSTATE_TX_INIT_DDIGEST
}
default:
glog.Errorf("error %d %d\n", conn.state, conn.txIOState)
return
}
if final {
break
}
}
switch conn.state {
case CONN_STATE_CLOSE, CONN_STATE_EXIT:
conn.state = CONN_STATE_CLOSE
case CONN_STATE_SECURITY_LOGIN:
conn.state = CONN_STATE_LOGIN
glog.Infof("CONN_STATE_LOGIN")
case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL:
if conn.sessionType == SESSION_NORMAL {
conn.state = CONN_STATE_KERNEL
glog.Infof("CONN_STATE_KERNEL")
conn.state = CONN_STATE_SCSI
glog.Infof("CONN_STATE_SCSI")
} else {
conn.state = CONN_STATE_FULL
glog.Infof("CONN_STATE_FULL")
}
conn.rxIOState = IOSTATE_RX_BHS
s.handler(DATAIN, conn)
case CONN_STATE_SCSI:
conn.txTask = nil
default:
conn.rxIOState = IOSTATE_RX_BHS
s.handler(DATAIN, conn)
}
glog.Infof("%d", conn.state)
}
func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err error) {
req := conn.req
switch req.OpCode {
case OpSCSICmd:
glog.Infof("SCSI Command processing...")
scmd := &api.SCSICommand{}
task := &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: scmd}
conn.rxTask = task
if err = s.iscsiTaskQueueHandler(task); err != nil {
return
} else {
conn.rxTask = nil
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
conn.txIOState = IOSTATE_TX_BHS
conn.statSN += 1
resp := &ISCSICommand{
Immediate: true,
Final: true,
StatSN: req.ExpStatSN,
TaskTag: req.TaskTag,
ExpCmdSN: conn.session.ExpCmdSN,
MaxCmdSN: conn.session.ExpCmdSN + 10,
Status: scmd.Result,
SCSIResponse: 0x00,
HasStatus: true,
}
switch scmd.Direction {
case api.SCSIDataRead:
resp.OpCode = OpSCSIIn
if scmd.InSDBBuffer.Buffer != nil {
buf := scmd.InSDBBuffer.Buffer.Bytes()
resp.RawData = buf
} else {
resp.RawData = []byte{}
}
case api.SCSIDataWrite:
resp.OpCode = OpSCSIResp
if scmd.InSDBBuffer.Buffer != nil {
buf := scmd.InSDBBuffer.Buffer.Bytes()
resp.RawData = buf
} else {
resp.RawData = []byte{}
}
case api.SCSIDataBidirection:
case api.SCSIDataNone:
resp.OpCode = OpSCSIResp
}
conn.resp = resp
}
case OpSCSIOut:
case OpNoopOut:
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}
conn.txIOState = IOSTATE_TX_BHS
iscsiExecNoopOut(conn)
case OpLogoutReq:
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}
conn.txIOState = IOSTATE_TX_BHS
iscsiExecLogout(conn)
case OpTextReq, OpSNACKReq:
err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode])
glog.Error(err)
return
default:
err = fmt.Errorf("Unknown op %s", opCodeMap[conn.req.OpCode])
glog.Error(err)
return
}
conn.rxIOState = IOSTATE_RX_BHS
s.handler(DATAIN|DATAOUT, conn)
return nil
}
func (s *ISCSITargetService) iscsiTaskQueueHandler(task *iscsiTask) error {
conn := task.conn
sess := conn.session
cmd := task.cmd
if cmd.Immediate {
return s.iscsiExecTask(task)
}
cmdsn := cmd.CmdSN
glog.V(2).Infof("CmdSN of command is %d, ExpCmdSN of session is %d", cmdsn, sess.ExpCmdSN)
if cmdsn == sess.ExpCmdSN {
retry:
cmdsn += 1
sess.ExpCmdSN = cmdsn
glog.Infof("session's ExpCmdSN is %d", cmdsn)
glog.Infof("process task(%d)", task.cmd.CmdSN)
if err := s.iscsiExecTask(task); err != nil {
glog.Error(err)
}
if len(sess.PendingTasks) == 0 {
return nil
}
task = sess.PendingTasks.Pop().(*iscsiTask)
cmd = task.cmd
if cmd.CmdSN != cmdsn {
sess.PendingTasks.Push(task)
return nil
}
task.state = taskSCSI
goto retry
} else {
if cmd.CmdSN < sess.ExpCmdSN {
err := fmt.Errorf("unexpected cmd serial number: (%d, %d)", cmd.CmdSN, sess.ExpCmdSN)
glog.Error(err)
return err
}
glog.Infof("add task(%d) into task queue", task.cmd.CmdSN)
// add this connection into queue and set this task as pending task
task.state = taskPending
sess.PendingTasks.Push(task)
}
return nil
}
func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
cmd := task.cmd
switch cmd.OpCode {
case OpSCSICmd:
if cmd.Read {
if cmd.Write {
task.scmd.Direction = api.SCSIDataBidirection
} else {
task.scmd.Direction = api.SCSIDataRead
}
} else {
if cmd.Write {
task.scmd.Direction = api.SCSIDataWrite
}
}
task.scmd.CommandITNID = task.conn.session.Tsih
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
task.scmd.SCBLength = len(cmd.CDB)
task.scmd.Lun = cmd.LUN
task.scmd.Tag = uint64(cmd.TaskTag)
task.state = taskSCSI
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
// add scsi target process queue
err := s.SCSI.AddCommandQueue(task.conn.session.Target.SCSITarget.TID, task.scmd)
if err != nil {
task.state = 0
}
return err
case OpLogoutReq:
case OpNoopOut:
// just do it in iscsi layer
}
return nil
}

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.
@@ -17,11 +17,38 @@ limitations under the License.
// iSCSI Target Driver
package iscsit
import (
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
import "github.com/gostor/gotgt/pkg/api"
const (
IOSTATE_FREE = iota
IOSTATE_RX_BHS
IOSTATE_RX_INIT_AHS
IOSTATE_RX_AHS
IOSTATE_RX_INIT_HDIGEST
IOSTATE_RX_HDIGEST
IOSTATE_RX_CHECK_HDIGEST
IOSTATE_RX_INIT_DATA
IOSTATE_RX_DATA
IOSTATE_RX_INIT_DDIGEST
IOSTATE_RX_DDIGEST
IOSTATE_RX_CHECK_DDIGEST
IOSTATE_RX_END
IOSTATE_TX_BHS
IOSTATE_TX_INIT_AHS
IOSTATE_TX_AHS
IOSTATE_TX_INIT_HDIGEST
IOSTATE_TX_HDIGEST
IOSTATE_TX_INIT_DATA
IOSTATE_TX_DATA
IOSTATE_TX_INIT_DDIGEST
IOSTATE_TX_DDIGEST
IOSTATE_TX_END
)
var ISCSI_OPCODE_MASK = 0x3F
type ISCSIDiscoveryMethod string
var (
@@ -38,7 +65,7 @@ type ISCSIRedirectInfo struct {
}
type ISCSITarget struct {
*api.SCSITarget
api.SCSITarget
api.SCSITargetDriverCommon
Sessions []*ISCSISession
@@ -51,9 +78,9 @@ type ISCSITarget struct {
NopCount int
}
func NewISCSITarget(target *api.SCSITarget) *ISCSITarget {
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
return &ISCSITarget{
SCSITarget: target,
SCSITarget: *target,
}
}
@@ -91,96 +118,3 @@ func (tgt *ISCSITarget) GetLu(lun uint8) (uint64, error) {
func (tgt *ISCSITarget) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil
}
func (tgt *ISCSITarget) ProcessCommand(buf []byte) ([]byte, error) {
b := make([]byte, 48) // TODO: sync.Pool
b = buf[0:48]
m, err := parseHeader(b)
if err != nil {
return nil, err
}
m.RawHeader = b
if m.DataLen > 0 {
m.RawData = buf[48:m.DataLen]
}
resp := &ISCSICommand{}
switch m.OpCode {
case OpLoginReq:
resp = &ISCSICommand{
OpCode: OpLoginResp,
Transit: true,
NSG: FullFeaturePhase,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN,
MaxCmdSN: m.CmdSN,
RawData: util.MarshalKVText(map[string]string{
"HeaderDigest": "None",
"DataDigest": "None",
}),
}
break
case OpLogoutReq:
resp = &ISCSICommand{
OpCode: OpLogoutResp,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN,
MaxCmdSN: m.CmdSN,
}
case OpSCSICmd:
resp = &ISCSICommand{
OpCode: OpSCSIResp,
Final: true,
StatSN: m.ExpStatSN,
TaskTag: m.TaskTag,
ExpCmdSN: m.CmdSN + 1,
MaxCmdSN: m.CmdSN + 10,
}
switch api.SCSICommandType(m.CDB[0]) {
case api.TEST_UNIT_READY:
// test unit ready
resp.Status = api.SAM_STAT_GOOD
resp.SCSIResponse = 0x01
break
case api.READ_CAPACITY:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
var data []byte
data = append(data, MarshalUint64(uint64(0))[4:]...)
data = append(data, MarshalUint64(uint64(0))[4:]...)
resp.RawData = data
break
case api.SERVICE_ACTION_IN:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
sa := m.CDB[1] & 0x1f
switch sa {
case 0x10:
c := &Capacity{}
resp.RawData = c.bytes()
}
break
case api.INQUIRY:
resp.OpCode = OpSCSIIn
resp.HasStatus = true
alloc := int(ParseUint(m.CDB[3:5]))
inq := &InquiryData{
Vendor: [8]byte{'1', '1', 'c', 'a', 'n', 's'},
Product: [16]byte{'c', 'o', 'f', 'f', 'e', 'e'},
RevisionLevel: [4]byte{'1', '.', '0'},
SerialNumber: 52,
}
if len(inq.bytes()) >= alloc {
resp.RawData = inq.bytes()[:alloc]
} else {
resp.RawData = inq.bytes()
}
break
default:
break
}
}
b1 := resp.Bytes()
return b1, nil
}

View File

@@ -1,6 +1,10 @@
package iscsit
import "bytes"
import (
"bytes"
"github.com/gostor/gotgt/pkg/util"
)
func (m *ISCSICommand) loginRespBytes() []byte {
// rfc7143 11.13
@@ -20,20 +24,20 @@ func (m *ISCSICommand) loginRespBytes() []byte {
buf.WriteByte(b)
b = 0
buf.WriteByte(b) // version-max
buf.WriteByte(b) // version-active
buf.WriteByte(b) // ahsLen
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
buf.Write(MarshalUint64(m.ISID)[2:])
buf.Write(MarshalUint64(uint64(m.TSIH))[6:])
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
buf.WriteByte(b) // version-max
buf.WriteByte(b) // version-active
buf.WriteByte(b) // ahsLen
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
buf.Write(util.MarshalUint64(m.ISID)[2:])
buf.Write(util.MarshalUint64(uint64(m.TSIH))[6:])
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
buf.WriteByte(b)
buf.WriteByte(b)
buf.WriteByte(b)
buf.WriteByte(b) // "reserved"
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.WriteByte(byte(m.StatusClass))
buf.WriteByte(byte(m.StatusDetail))
buf.WriteByte(b)

View File

@@ -1,6 +1,10 @@
package iscsit
import "bytes"
import (
"bytes"
"github.com/gostor/gotgt/pkg/util"
)
func (m *ISCSICommand) logoutRespBytes() []byte {
buf := &bytes.Buffer{}
@@ -11,13 +15,13 @@ func (m *ISCSICommand) logoutRespBytes() []byte {
for i := 4; i < 16; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 20; i < 24; i++ {
buf.WriteByte(0x00)
}
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 36; i < 48; i++ {
buf.WriteByte(0x00)
}

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.

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,13 +16,133 @@ limitations under the License.
package iscsit
import "crypto/rand"
import (
"fmt"
"math/rand"
"time"
)
var (
SESSION_NORMAL int = 0
SESSION_DISCOVERY int = 1
)
var DIGEST_CRC32C uint = 1 << 1
var DIGEST_NONE uint = 1 << 0
var DIGEST_ALL uint = DIGEST_NONE | DIGEST_CRC32C
var BHS_SIZE = 48
const (
MAX_QUEUE_CMD_MIN = 1
MAX_QUEUE_CMD_DEF = 128
MAX_QUEUE_CMD_MAX = 512
)
const (
ISCSI_PARAM_MAX_RECV_DLENGTH = iota
ISCSI_PARAM_HDRDGST_EN
ISCSI_PARAM_DATADGST_EN
ISCSI_PARAM_INITIAL_R2T_EN
ISCSI_PARAM_MAX_R2T
ISCSI_PARAM_IMM_DATA_EN
ISCSI_PARAM_FIRST_BURST
ISCSI_PARAM_MAX_BURST
ISCSI_PARAM_PDU_INORDER_EN
ISCSI_PARAM_DATASEQ_INORDER_EN
ISCSI_PARAM_ERL
ISCSI_PARAM_IFMARKER_EN
ISCSI_PARAM_OFMARKER_EN
ISCSI_PARAM_DEFAULTTIME2WAIT
ISCSI_PARAM_DEFAULTTIME2RETAIN
ISCSI_PARAM_OFMARKINT
ISCSI_PARAM_IFMARKINT
ISCSI_PARAM_MAXCONNECTIONS
/* iSCSI Extensions for RDMA (RFC5046) */
ISCSI_PARAM_RDMA_EXTENSIONS
ISCSI_PARAM_TARGET_RDSL
ISCSI_PARAM_INITIATOR_RDSL
ISCSI_PARAM_MAX_OUTST_PDU
/* "local" parmas, never sent to the initiator */
ISCSI_PARAM_FIRST_LOCAL
ISCSI_PARAM_MAX_XMIT_DLENGTH = ISCSI_PARAM_FIRST_LOCAL
ISCSI_PARAM_MAX_QUEUE_CMD
/* must always be last */
ISCSI_PARAM_MAX
)
type ISCSISessionParam struct {
State int
Value uint
}
/*
* The defaults here are according to the spec and must not be changed,
* otherwise the initiator may make the wrong assumption. If you want
* to change a value, edit the value in iscsi_target_create.
*
* The param MaxXmitDataSegmentLength doesn't really exist. It's a way
* to remember the RDSL of the initiator, which defaults to 8k if he has
* not told us otherwise.
*/
type iscsiSessionKeys struct {
name string
def uint
min uint
max uint
}
var sessionKeys []iscsiSessionKeys = []iscsiSessionKeys{
// ISCSI_PARAM_MAX_RECV_DLENGTH
{"MaxRecvDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_HDRDGST_EN
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
// ISCSI_PARAM_DATADGST_EN
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
// ISCSI_PARAM_INITIAL_R2T_EN
{"InitialR2T", 1, 0, 1},
// ISCSI_PARAM_MAX_R2T
{"MaxOutstandingR2T", 1, 1, 65535},
// ISCSI_PARAM_IMM_DATA_EN
{"ImmediateData", 1, 0, 1},
// ISCSI_PARAM_FIRST_BURST
{"FirstBurstLength", 65536, 512, 16777215},
// ISCSI_PARAM_MAX_BURST
{"MaxBurstLength", 262144, 512, 16777215},
// ISCSI_PARAM_PDU_INORDER_EN
{"DataPDUInOrder", 1, 0, 1},
// ISCSI_PARAM_DATASEQ_INORDER_EN
{"DataSequenceInOrder", 1, 0, 1},
// ISCSI_PARAM_ERL
{"ErrorRecoveryLevel", 0, 0, 2},
// ISCSI_PARAM_IFMARKER_EN
{"IFMarker", 0, 0, 1},
// ISCSI_PARAM_OFMARKER_EN
{"OFMarker", 0, 0, 1},
// ISCSI_PARAM_DEFAULTTIME2WAIT
{"DefaultTime2Wait", 2, 0, 3600},
// ISCSI_PARAM_DEFAULTTIME2RETAIN
{"DefaultTime2Retain", 20, 0, 3600},
// ISCSI_PARAM_OFMARKINT
{"OFMarkInt", 2048, 1, 65535},
// ISCSI_PARAM_IFMARKINT
{"IFMarkInt", 2048, 1, 65535},
// ISCSI_PARAM_MAXCONNECTIONS
{"MaxConnections", 1, 1, 65535},
// ISCSI_PARAM_RDMA_EXTENSIONS
{"RDMAExtensions", 0, 0, 1},
// ISCSI_PARAM_TARGET_RDSL
{"TargetRecvDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_INITIATOR_RDSL
{"InitiatorRecvDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_MAX_OUTST_PDU
{"MaxOutstandingUnexpectedPDUs", 0, 2, 4294967295},
// "local" parmas, never sent to the initiator
// ISCSI_PARAM_MAX_XMIT_DLENGTH
{"MaxXmitDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_MAX_QUEUE_CMD
{"MaxQueueCmd", MAX_QUEUE_CMD_DEF, MAX_QUEUE_CMD_MIN, MAX_QUEUE_CMD_MAX},
}
// Session is an iSCSI session.
type ISCSISession struct {
Refcount int
@@ -30,58 +150,112 @@ type ISCSISession struct {
InitiatorAlias string
Target *ISCSITarget
Isid uint64
Tsih uint16
Tsih uint64
ExpCmdSN uint32
// only one connection per session
Connections []*ISCSIConnection
Commands []*ISCSICommand
PendingCommands []*ISCSICommand
ExpectionCommandSN uint32
MaxQueueCommand uint32
SessionParam []ISCSISessionParam
Info string
Rdma int
Connections []*iscsiConnection
Commands []*ISCSICommand
PendingTasks taskQueue
MaxQueueCommand uint32
SessionParam []ISCSISessionParam
Info string
Rdma int
}
type ISCSIHeader struct {
type taskQueue []*iscsiTask
func (tq taskQueue) Len() int { return len(tq) }
func (tq taskQueue) Less(i, j int) bool {
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
return tq[i].cmd.CmdSN > tq[j].cmd.CmdSN
}
type ISCSIPdu struct {
Bhs ISCSIHeader
AhsSize uint
DataSize uint
func (tq taskQueue) Swap(i, j int) {
tq[i], tq[j] = tq[j], tq[i]
}
type ISCSIConnection struct {
State int
RxIostate int
TxIostate int
Refcount int
func (tq *taskQueue) Push(x interface{}) {
item := x.(*iscsiTask)
*tq = append(*tq, item)
}
Session *ISCSISession
func (tq *taskQueue) Pop() interface{} {
old := *tq
n := len(old)
item := old[n-1]
*tq = old[0 : n-1]
return item
}
TID int
CID int
Auth AuthMethod
StatSN uint32
ExpectionStatSN uint32
CommandSN uint32
ExpectionCommandSN uint32
MaxCommandSN uint32
Request ISCSIPdu
Response ISCSIPdu
// The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
// appear in all iSCSI PDUs. In addition, when used, the Initiator Task
// Tag and Logical Unit Number always appear in the same location in the
// header.
type iscsiHeader struct {
opcode uint8
flags uint8 // Final bit
rsvd2 [2]uint8
hlength uint8 // AHSs total length
dlength [3]uint8 // Data length
lun [8]uint8
itt uint8 // Initiator Task Tag
ttt uint8 // Target Task Tag
statsn uint8
expStatSN uint8
maxStatSN uint8
other [12]uint8
}
type iscsiPdu struct {
bhs iscsiHeader
ahsSize uint
dataSize uint
}
// New creates a new session.
func NewISCSISession() (*ISCSISession, error) {
var tsih uint16
b := make([]byte, 2)
if _, err := rand.Read(b); err != nil {
return nil, err
}
tsih += uint16(b[0]) << 8
tsih += uint16(b[1])
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
var (
target *ISCSITarget
tsih uint64
)
return &ISCSISession{
Tsih: tsih,
}, nil
for _, t := range s.Targets {
if t.TID == conn.tid {
target = t
break
}
}
if target == nil {
return nil, fmt.Errorf("No target found with tid(%d)", conn.tid)
}
for {
rand.Seed(int64(time.Now().UTC().Nanosecond()))
tsih = uint64(rand.Uint32())
for _, s := range target.Sessions {
if s.Tsih == tsih {
tsih = 0
break
}
}
if tsih != 0 {
break
}
}
sess := &ISCSISession{
Tsih: tsih,
Initiator: conn.initiator,
InitiatorAlias: conn.initiatorAlias,
Target: target,
Connections: []*iscsiConnection{conn},
SessionParam: conn.sessionParam,
MaxQueueCommand: uint32(conn.sessionParam[ISCSI_PARAM_MAX_QUEUE_CMD].Value),
Rdma: 0,
ExpCmdSN: conn.expCmdSN,
}
conn.session = sess
return sess, nil
}

47
pkg/port/service.go Normal file
View File

@@ -0,0 +1,47 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package port
import (
"fmt"
"github.com/gostor/gotgt/pkg/scsi"
)
type SCSITargetService interface {
Run() error
NewTarget(string) (SCSITargetDriver, error)
}
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
var registeredPlugins = map[string](TargetServiceFunc){}
func RegisterTargetService(name string, f TargetServiceFunc) {
registeredPlugins[name] = f
}
func NewTargetService(name string, s *scsi.SCSITargetService) (SCSITargetService, error) {
if name == "" {
return nil, nil
}
f, ok := registeredPlugins[name]
if !ok {
return nil, fmt.Errorf("SCSI target driver %s is not found.", name)
}
return f(s)
}