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 package port
import ( import "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port/iscsit"
)
type SCSITargetDriver interface { type SCSITargetDriver interface {
Init() error Init() error
@@ -16,7 +29,6 @@ type SCSITargetDriver interface {
CreateLu(lu *api.SCSILu) error CreateLu(lu *api.SCSILu) error
GetLu(lun uint8) (uint64, error) GetLu(lun uint8) (uint64, error)
ProcessCommand(buf []byte) ([]byte, error)
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) 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 { func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -4,6 +4,8 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"strings" "strings"
"github.com/gostor/gotgt/pkg/util"
) )
type OpCode int type OpCode int
@@ -52,6 +54,8 @@ var opCodeMap = map[OpCode]string{
OpReject: "Reject", OpReject: "Reject",
} }
const DataPadding = 4
type ISCSICommand struct { type ISCSICommand struct {
OpCode OpCode OpCode OpCode
RawHeader []byte RawHeader []byte
@@ -68,7 +72,7 @@ type ISCSICommand struct {
ExpStatSN uint32 // Expected status serial. ExpStatSN uint32 // Expected status serial.
Read, Write bool Read, Write bool
LUN uint8 LUN [8]uint8
Transit bool // Transit bit. Transit bool // Transit bit.
Cont bool // Continue bit. Cont bool // Continue bit.
CSG, NSG Stage // Current Stage, Next Stage. CSG, NSG Stage // Current Stage, Next Stage.
@@ -102,6 +106,10 @@ func (cmd *ISCSICommand) Bytes() []byte {
return cmd.scsiCmdRespBytes() return cmd.scsiCmdRespBytes()
case OpSCSIIn: case OpSCSIIn:
return cmd.dataInBytes() return cmd.dataInBytes()
case OpTextResp:
return cmd.textRespBytes()
case OpNoopIn:
return cmd.noopInBytes()
} }
return nil return nil
} }
@@ -155,16 +163,8 @@ func ParseUint(data []byte) uint64 {
return out 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) { func parseHeader(data []byte) (*ISCSICommand, error) {
if len(data) != 48 { if len(data) != BHS_SIZE {
return nil, fmt.Errorf("garbled header") return nil, fmt.Errorf("garbled header")
} }
// TODO: sync.Pool // TODO: sync.Pool
@@ -177,7 +177,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
m.TaskTag = uint32(ParseUint(data[16:20])) m.TaskTag = uint32(ParseUint(data[16:20]))
switch m.OpCode { switch m.OpCode {
case OpSCSICmd: case OpSCSICmd:
m.LUN = uint8(data[9]) m.LUN = [8]uint8{data[9]}
m.ExpectedDataLen = uint32(ParseUint(data[20:24])) m.ExpectedDataLen = uint32(ParseUint(data[20:24]))
m.CmdSN = uint32(ParseUint(data[24:28])) m.CmdSN = uint32(ParseUint(data[24:28]))
m.Read = data[1]&0x40 == 0x40 m.Read = data[1]&0x40 == 0x40
@@ -185,7 +185,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
m.CDB = data[32:48] m.CDB = data[32:48]
m.ExpStatSN = uint32(ParseUint(data[28:32])) m.ExpStatSN = uint32(ParseUint(data[28:32]))
case OpSCSIResp: case OpSCSIResp:
case OpLoginReq: case OpLoginReq, OpTextReq, OpNoopOut, OpLogoutReq:
m.Transit = m.Final m.Transit = m.Final
m.Cont = data[1]&0x40 == 0x40 m.Cont = data[1]&0x40 == 0x40
if m.Cont && m.Transit { if m.Cont && m.Transit {
@@ -229,13 +229,13 @@ func (m *ISCSICommand) scsiCmdRespBytes() []byte {
for i := 0; i < 3*4; i++ { for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:]) buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
buf.Write(MarshalUint64(uint64(m.StatSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 0; i < 3*4; i++ { for i := 0; i < 3*4; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
@@ -259,24 +259,22 @@ func (m *ISCSICommand) dataInBytes() []byte {
} }
buf.WriteByte(b) buf.WriteByte(b)
buf.WriteByte(0x00) // 4 buf.WriteByte(0x00) // 4
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8 buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
buf.WriteByte(0x00)
buf.WriteByte(byte(m.LUN))
// Skip through to byte 16 // Skip through to byte 16
for i := 0; i < 6; i++ { for i := 0; i < 8; i++ {
buf.WriteByte(0x00) 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++ { for i := 0; i < 4; i++ {
// 11.7.4 // 11.7.4
buf.WriteByte(0xff) buf.WriteByte(0xff)
} }
buf.Write(MarshalUint64(uint64(m.StatSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.DataSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.DataSN))[4:])
buf.Write(MarshalUint64(uint64(m.BufferOffset))[4:]) buf.Write(util.MarshalUint64(uint64(m.BufferOffset))[4:])
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
@@ -290,123 +288,79 @@ func (m *ISCSICommand) dataInBytes() []byte {
return buf.Bytes() return buf.Bytes()
} }
type InquiryData struct { func (m *ISCSICommand) textRespBytes() []byte {
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 {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
buf.WriteByte(byte(OpTextResp))
var b byte var b byte
b = (uint8(id.PeripheralQualifier) << 5) & 0xe0 if m.Final {
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 {
b |= 0x80 b |= 0x80
} }
if id.HasACC { if m.Cont {
b |= 0x40 b |= 0x40
} }
b |= byte(id.TargetGroupSupport) << 4 & 0x30 // byte 1
if id.ThirdPartyCopy {
b |= 0x08
}
if id.Protect {
b |= 0x01
}
buf.WriteByte(b) buf.WriteByte(b)
// byte 6
b = 0 b = 0
if id.EnclosureServices {
b |= 0x40
}
if id.Multiport {
b |= 0x10
}
if id.MediaChanger {
b |= 0x08
}
buf.WriteByte(b) buf.WriteByte(b)
buf.WriteByte(0x02) buf.WriteByte(b)
buf.Write(id.Vendor[:]) buf.WriteByte(b)
buf.Write(id.Product[:]) buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
buf.Write(id.RevisionLevel[:]) // Skip through to byte 12
buf.Write(MarshalUint64(id.SerialNumber)) for i := 0; i < 2*4; i++ {
for i := 0; i < 12; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
data := buf.Bytes() buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
data[4] = byte(len(data) - 4) for i := 0; i < 4; i++ {
return data buf.WriteByte(0xff)
}
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.WriteByte(b) buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
b = c.PIExponent << 4 buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
b |= c.LogicalExponent buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.WriteByte(b) for i := 0; i < 3*4; i++ {
lowLBA := MarshalUint64(uint64(c.LowestLBA))[6:] buf.WriteByte(0x00)
lowLBA[0] &= 0x3f
if c.ThinProvisioned {
lowLBA[0] &= 0x80
} }
if c.ThinProvReturnsZeros { rd := m.RawData
lowLBA[0] &= 0x40 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() 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with 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 // iSCSI Target Driver
package iscsit package iscsit
import ( import "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util" 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 type ISCSIDiscoveryMethod string
var ( var (
@@ -38,7 +65,7 @@ type ISCSIRedirectInfo struct {
} }
type ISCSITarget struct { type ISCSITarget struct {
*api.SCSITarget api.SCSITarget
api.SCSITargetDriverCommon api.SCSITargetDriverCommon
Sessions []*ISCSISession Sessions []*ISCSISession
@@ -51,9 +78,9 @@ type ISCSITarget struct {
NopCount int NopCount int
} }
func NewISCSITarget(target *api.SCSITarget) *ISCSITarget { func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
return &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 { func (tgt *ISCSITarget) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
return nil 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 package iscsit
import "bytes" import (
"bytes"
"github.com/gostor/gotgt/pkg/util"
)
func (m *ISCSICommand) loginRespBytes() []byte { func (m *ISCSICommand) loginRespBytes() []byte {
// rfc7143 11.13 // rfc7143 11.13
@@ -20,20 +24,20 @@ func (m *ISCSICommand) loginRespBytes() []byte {
buf.WriteByte(b) buf.WriteByte(b)
b = 0 b = 0
buf.WriteByte(b) // version-max buf.WriteByte(b) // version-max
buf.WriteByte(b) // version-active buf.WriteByte(b) // version-active
buf.WriteByte(b) // ahsLen buf.WriteByte(b) // ahsLen
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
buf.Write(MarshalUint64(m.ISID)[2:]) buf.Write(util.MarshalUint64(m.ISID)[2:])
buf.Write(MarshalUint64(uint64(m.TSIH))[6:]) buf.Write(util.MarshalUint64(uint64(m.TSIH))[6:])
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:]) buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
buf.WriteByte(b) buf.WriteByte(b)
buf.WriteByte(b) buf.WriteByte(b)
buf.WriteByte(b) buf.WriteByte(b)
buf.WriteByte(b) // "reserved" buf.WriteByte(b) // "reserved"
buf.Write(MarshalUint64(uint64(m.StatSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
buf.WriteByte(byte(m.StatusClass)) buf.WriteByte(byte(m.StatusClass))
buf.WriteByte(byte(m.StatusDetail)) buf.WriteByte(byte(m.StatusDetail))
buf.WriteByte(b) buf.WriteByte(b)

View File

@@ -1,6 +1,10 @@
package iscsit package iscsit
import "bytes" import (
"bytes"
"github.com/gostor/gotgt/pkg/util"
)
func (m *ISCSICommand) logoutRespBytes() []byte { func (m *ISCSICommand) logoutRespBytes() []byte {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
@@ -11,13 +15,13 @@ func (m *ISCSICommand) logoutRespBytes() []byte {
for i := 4; i < 16; i++ { for i := 4; i < 16; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:]) buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
for i := 20; i < 24; i++ { for i := 20; i < 24; i++ {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
buf.Write(MarshalUint64(uint64(m.StatSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:]) buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
for i := 36; i < 48; i++ { for i := 36; i < 48; i++ {
buf.WriteByte(0x00) 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,13 +16,133 @@ limitations under the License.
package iscsit 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 { type ISCSISessionParam struct {
State int State int
Value uint 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. // Session is an iSCSI session.
type ISCSISession struct { type ISCSISession struct {
Refcount int Refcount int
@@ -30,58 +150,112 @@ type ISCSISession struct {
InitiatorAlias string InitiatorAlias string
Target *ISCSITarget Target *ISCSITarget
Isid uint64 Isid uint64
Tsih uint16 Tsih uint64
ExpCmdSN uint32
// only one connection per session // only one connection per session
Connections []*ISCSIConnection Connections []*iscsiConnection
Commands []*ISCSICommand Commands []*ISCSICommand
PendingCommands []*ISCSICommand PendingTasks taskQueue
ExpectionCommandSN uint32 MaxQueueCommand uint32
MaxQueueCommand uint32 SessionParam []ISCSISessionParam
SessionParam []ISCSISessionParam Info string
Info string Rdma int
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 { func (tq taskQueue) Swap(i, j int) {
Bhs ISCSIHeader tq[i], tq[j] = tq[j], tq[i]
AhsSize uint
DataSize uint
} }
type ISCSIConnection struct { func (tq *taskQueue) Push(x interface{}) {
State int item := x.(*iscsiTask)
RxIostate int *tq = append(*tq, item)
TxIostate int }
Refcount int
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 // The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
CID int // appear in all iSCSI PDUs. In addition, when used, the Initiator Task
Auth AuthMethod // Tag and Logical Unit Number always appear in the same location in the
StatSN uint32 // header.
ExpectionStatSN uint32 type iscsiHeader struct {
CommandSN uint32 opcode uint8
ExpectionCommandSN uint32 flags uint8 // Final bit
MaxCommandSN uint32 rsvd2 [2]uint8
Request ISCSIPdu hlength uint8 // AHSs total length
Response ISCSIPdu 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. // New creates a new session.
func NewISCSISession() (*ISCSISession, error) { func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
var tsih uint16 var (
b := make([]byte, 2) target *ISCSITarget
if _, err := rand.Read(b); err != nil { tsih uint64
return nil, err )
}
tsih += uint16(b[0]) << 8
tsih += uint16(b[1])
return &ISCSISession{ for _, t := range s.Targets {
Tsih: tsih, if t.TID == conn.tid {
}, nil 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)
}

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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package scsi
import ( import (
"fmt" "fmt"
"os"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
) )
@@ -28,15 +29,7 @@ type BaseBackingStore struct {
OflagsSupported int OflagsSupported int
} }
type BackingStore interface { type BackingStoreFunc func() (api.BackingStore, error)
Open(dev *api.SCSILu, path string, fd *int, size *uint64) error
Close(dev *api.SCSILu) error
Init(dev *api.SCSILu, Opts string) error
Exit(dev *api.SCSILu) error
CommandSubmit(cmd *api.SCSICommand) error
}
type BackingStoreFunc func() (BackingStore, error)
var registeredPlugins = map[string](BackingStoreFunc){} var registeredPlugins = map[string](BackingStoreFunc){}
@@ -44,7 +37,7 @@ func RegisterBackingStore(name string, f BackingStoreFunc) {
registeredPlugins[name] = f registeredPlugins[name] = f
} }
func NewBackingStore(name string) (BackingStore, error) { func NewBackingStore(name string) (api.BackingStore, error) {
if name == "" { if name == "" {
return nil, nil return nil, nil
} }
@@ -59,8 +52,8 @@ type fakeBackingStore struct {
BaseBackingStore BaseBackingStore
} }
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string, fd *int, size *uint64) error { func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
return nil return nil, nil
} }
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error { func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {

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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,40 +16,188 @@ limitations under the License.
package backingstore package backingstore
import "github.com/gostor/gotgt/pkg/scsi" import (
"bytes"
"fmt"
"os"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi"
"github.com/gostor/gotgt/pkg/util"
)
func init() { func init() {
scsi.RegisterBackingStore("file", new) scsi.RegisterBackingStore("file", new)
} }
type FileBackingStore struct { type FileBackingStore struct {
BaseBackingStore scsi.BaseBackingStore
} }
func new() (scsi.BackingStore, error) { func new() (api.BackingStore, error) {
return NullBackingStore{ return &FileBackingStore{
Name: "file", BaseBackingStore: scsi.BaseBackingStore{
DataSize: 0, Name: "file",
OflagsSupported: 0, DataSize: 0,
OflagsSupported: 0,
},
}, nil }, nil
} }
func (bs *FileBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error { func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm)
if err != nil {
return nil, err
}
return f, nil
}
func (bs *FileBackingStore) Close(dev *api.SCSILu) error {
return dev.File.Close()
}
func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error {
return nil return nil
} }
func (bs *FileBackingStore) Close(dev *SCSILu) error { func (bs *FileBackingStore) Exit(dev *api.SCSILu) error {
return nil return nil
} }
func (bs *FileBackingStore) Init(dev *SCSILu, Opts string) error { func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
return nil var (
} scb = cmd.SCB.Bytes()
offset = cmd.Offset
opcode = api.SCSICommandType(scb[0])
lu = cmd.Device
key = scsi.ILLEGAL_REQUEST
asc = scsi.ASC_INVALID_FIELD_IN_CDB
wbuf []byte = []byte{}
rbuf []byte = []byte{}
length int
doVerify bool = false
doWrite bool = false
)
switch opcode {
case api.ORWRITE_16:
tmpbuf := []byte{}
length, err = lu.File.ReadAt(tmpbuf, int64(offset))
if length != len(tmpbuf) {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
break
}
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(tmpbuf)
func (bs *FileBackingStore) Exit(dev *SCSILu) error { wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
return nil doWrite = true
} goto write
case api.COMPARE_AND_WRITE:
// TODO
doWrite = true
goto write
case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16:
break
case api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
doVerify = true
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16:
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
doWrite = true
goto write
case api.WRITE_SAME, api.WRITE_SAME_16:
// TODO
break
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
length, err = lu.File.ReadAt(rbuf, int64(offset))
if err != nil {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
break
}
for i := 0; i < int(cmd.TL)-length; i++ {
rbuf = append(rbuf, 0)
}
if (opcode != api.READ_6) && (scb[1]&0x10 != 0) {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
}
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf)
case api.PRE_FETCH_10, api.PRE_FETCH_16:
err = util.Fadvise(lu.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED)
if err != nil {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
}
case api.VERIFY_10, api.VERIFY_12, api.VERIFY_16:
doVerify = true
goto verify
case api.UNMAP:
// TODO
default:
break
}
write:
if doWrite {
// hack: wbuf = []byte("hello world!")
length, err = lu.File.WriteAt(wbuf, int64(offset))
if err != nil || length != len(wbuf) {
glog.Error(err)
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
goto sense
}
glog.V(2).Infof("write data at %d for length %d", offset, length)
var pg *api.ModePage
for _, p := range lu.ModePages {
if p.Pcode == 0x08 && p.SubPcode == 0 {
pg = &p
break
}
}
if pg == nil {
key = scsi.ILLEGAL_REQUEST
asc = scsi.ASC_INVALID_FIELD_IN_CDB
goto sense
}
if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) {
if err = util.Fdatasync(lu.File); err != nil {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
goto sense
}
}
if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
}
}
verify:
if doVerify {
length, err = lu.File.ReadAt(rbuf, int64(offset))
if length != len(rbuf) {
key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR
goto sense
}
if !bytes.Equal(cmd.OutSDBBuffer.Buffer.Bytes(), rbuf) {
err = fmt.Errorf("verify fail between out buffer and read buffer")
key = scsi.MISCOMPARE
asc = scsi.ASC_MISCOMPARE_DURING_VERIFY_OPERATION
goto sense
}
if scb[1]&0x10 != 0 {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
}
}
glog.Infof("io done %s", string(scb))
sense:
if err != nil {
glog.Error(err)
return err
}
_ = key
_ = asc
func (bs *FileBackingStore) CommandSubmit(cmd *SCSICommand) error {
return nil return nil
} }

View File

@@ -17,45 +17,47 @@ limitations under the License.
package backingstore package backingstore
import ( import (
"github.com/gostor/gotgt/pkg/scsi" "os"
"github.com/golang/glog" "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi"
) )
func init() { func init() {
scsi.RegisterBackingStore("null", new) scsi.RegisterBackingStore("null", newNull)
} }
type NullBackingStore struct { type NullBackingStore struct {
BaseBackingStore scsi.BaseBackingStore
} }
func new() (scsi.BackingStore, error) { func newNull() (api.BackingStore, error) {
return NullBackingStore{ return &NullBackingStore{
Name: "null", BaseBackingStore: scsi.BaseBackingStore{
DataSize: 0, Name: "null",
OflagsSupported: 0, DataSize: 0,
OflagsSupported: 0,
},
}, nil }, nil
} }
func (bs *NullBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error { func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
glog.V(1).Infof("NULL backing store open, size: %d", size) return nil, nil
}
func (bs *NullBackingStore) Close(dev *api.SCSILu) error {
return nil return nil
} }
func (bs *NullBackingStore) Close(dev *SCSILu) error { func (bs *NullBackingStore) Init(dev *api.SCSILu, Opts string) error {
return nil return nil
} }
func (bs *NullBackingStore) Init(dev *SCSILu, Opts string) error { func (bs *NullBackingStore) Exit(dev *api.SCSILu) error {
return nil return nil
} }
func (bs *NullBackingStore) Exit(dev *SCSILu) error { func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
return nil cmd.Result = api.SAM_STAT_GOOD
}
func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error {
cmd.Result = SAM_STAT_GOOD
return nil return nil
} }

View File

@@ -16,19 +16,51 @@ limitations under the License.
package scsi package scsi
import "github.com/gostor/gotgt/pkg/api" import (
"os"
type SCSILuOps struct { "github.com/gostor/gotgt/pkg/api"
*api.SCSILu )
DeviceProtocol SCSIDeviceProtocol func NewSCSILu(lun uint64, target *api.SCSITarget) (*api.SCSILu, error) {
Storage *BackingStore sbc := NewSBCDevice()
Target *api.SCSITarget backing, err := NewBackingStore("file")
Attrs api.SCSILuPhyAttribute if err != nil {
return nil, err
}
var lu = &api.SCSILu{
Lun: lun,
Target: target,
PerformCommand: luPerformCommand,
DeviceProtocol: sbc,
Storage: backing,
BlockShift: 0,
Size: 1024 * 1024 * 1024,
}
// hack this
if _, err = os.Stat("/tmp/data.txt"); err != nil && os.IsExist(err) {
os.Create("/tmp/data.txt")
}
f, err := backing.Open(lu, "/tmp/data.txt")
if err != nil {
return nil, err
}
lu.File = f
lu.DeviceProtocol.InitLu(lu)
lu.Attrs.Online = true
return lu, nil
}
// function handler for command performing and finishing func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
PerformCommand CommandFunc op := int(cmd.SCB.Bytes()[0])
FinishCommand func(*api.SCSITarget, *api.SCSICommand) fn := cmd.Device.DeviceProtocol.PerformCommand(op)
if fn != nil {
fnop := fn.(SCSIDeviceOperation)
// host := cmd.ITNexus.Host
host := 0
return fnop.CommandPerformFunc(host, cmd)
}
return api.SAMStatGood
} }
func luPreventRemoval(lu *api.SCSILu) bool { func luPreventRemoval(lu *api.SCSILu) bool {

View File

@@ -18,9 +18,13 @@ limitations under the License.
package scsi package scsi
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
) )
const ( const (
@@ -36,30 +40,75 @@ type SBCSCSIDeviceProtocol struct {
BaseSCSIDeviceProtocol BaseSCSIDeviceProtocol
} }
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error { func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
return nil return sbc.SCSIDeviceOps[opcode]
} }
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
return nil func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
} var tgt = lu.Target
func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error { // init LU's phy attribute
return nil lu.Attrs.DeviceType = api.TYPE_DISK
} lu.Attrs.Qualifier = false
func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error { lu.Attrs.Thinprovisioning = false
return nil lu.Attrs.Removable = false
} lu.Attrs.Readonly = false
func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error { lu.Attrs.SWP = false
lu.Attrs.SenseFormat = false
lu.Attrs.VendorID = "GOSTOR"
lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR %x%d", tgt.TID, lu.Lun)
lu.Attrs.SCSISN = fmt.Sprintf("beaf%d%d", tgt.TID, lu.Lun)
lu.Attrs.ProductID = "VIRTUAL-DISK"
lu.Attrs.VersionDesction = []uint16{
0x04C0, // SBC-3 no version claimed
0x0960, // iSCSI
0x0300, // SPC-3
}
if lu.BlockShift == 0 {
lu.BlockShift = api.DefaultBlockShift
}
pages := []api.ModePage{}
// Vendor uniq - However most apps seem to call for mode page 0
pages = append(pages, api.ModePage{0, 0, []byte{}})
// Disconnect page
pages = append(pages, api.ModePage{2, 0, []byte{0x80, 0x80, 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}})
// Caching Page
pages = append(pages, api.ModePage{8, 0, []byte{0x14, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}})
// Control page
pages = append(pages, api.ModePage{0x0a, 0, []byte{2, 0x10, 0, 0, 0, 0, 0, 0, 2, 0}})
// Control Extensions mode page: TCMOS:1
pages = append(pages, api.ModePage{0x0a, 1, []byte{0x04, 0x00, 0x00}})
// Informational Exceptions Control page
pages = append(pages, api.ModePage{0x1c, 0, []byte{8, 0, 0, 0, 0, 0, 0, 0, 0, 0}})
lu.ModePages = pages
return nil return nil
} }
func NewSBCDevice() SBCSCSIDeviceProtocol { func (sbc SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
return nil
}
func (sbc SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
return nil
}
func (sbc SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
return nil
}
func (sbc SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
return nil
}
func NewSBCDevice() api.SCSIDeviceProtocol {
var sbc = SBCSCSIDeviceProtocol{ var sbc = SBCSCSIDeviceProtocol{
BaseSCSIDeviceProtocol{ BaseSCSIDeviceProtocol{
Type: api.TYPE_DISK, Type: api.TYPE_DISK,
SCSIDeviceOps: make([]SCSIDeviceOperation, 256), SCSIDeviceOps: []SCSIDeviceOperation{},
}, },
} }
for i := 0; i <= 256; i++ { for i := 0; i < 256; i++ {
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)) sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
} }
sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0) sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
@@ -103,9 +152,10 @@ func NewSBCDevice() SBCSCSIDeviceProtocol {
sbc.SCSIDeviceOps[api.PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN) sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
sbc.SCSIDeviceOps[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0) sbc.SCSIDeviceOps[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0) sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SBCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0) sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0) sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN) sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN) sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
@@ -132,15 +182,18 @@ func SBCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
deviceSpecific |= 0x80 deviceSpecific |= 0x80
} }
data := cmd.InSDBBuffer.Buffer buf := cmd.InSDBBuffer.Buffer
data.Next(2) data := []byte{0x00, 0x00, 0x00, 0x00}
if buf != nil {
data = buf.Bytes()
}
if cmd.SCB.Bytes()[0] == 0x1a { if cmd.SCB.Bytes()[0] == 0x1a {
data.WriteByte(deviceSpecific) data[2] = deviceSpecific
} else { } else {
data.Next(1) data[3] = deviceSpecific
data.WriteByte(deviceSpecific)
} }
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
return api.SAMStatGood return api.SAMStatGood
} }
@@ -196,7 +249,135 @@ func SBCUnmap(host int, cmd *api.SCSICommand) api.SAMStat {
} }
func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
return api.SAMStatGood var (
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
dev = cmd.Device
scb = cmd.SCB.Bytes()
opcode = api.SCSICommandType(scb[0])
lba uint64
tl uint32
err error
)
if dev.Attrs.Removable && !dev.Attrs.Online {
key = NOT_READY
asc = ASC_MEDIUM_NOT_PRESENT
glog.Warningf("sense")
goto sense
}
switch opcode {
case api.READ_10, api.READ_12, api.READ_16, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.ORWRITE_16,
api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16, api.COMPARE_AND_WRITE:
// We only support protection information type 0
/*
if scb[1]&0xe0 != 0 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
glog.Warningf("sense")
goto sense
}
*/
if cmd.OutSDBBuffer.Buffer == nil {
cmd.OutSDBBuffer.Buffer = &bytes.Buffer{}
}
case api.WRITE_SAME, api.WRITE_SAME_16:
// We dont support resource-provisioning so ANCHOR bit == 1 is an error.
if scb[1]&0x10 != 0 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
// We only support unmap for thin provisioned LUNS
if (scb[1]&0x08 != 0) && !dev.Attrs.Thinprovisioning {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
// We only support protection information type 0
if scb[1]&0xe0 != 0 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
// LBDATA and PBDATA can not both be set
if (scb[1] & 0x06) == 0x06 {
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
goto sense
}
}
if dev.Attrs.Readonly || dev.Attrs.SWP {
switch opcode {
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.ORWRITE_16,
api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16, api.WRITE_SAME, api.WRITE_SAME_16,
api.PRE_FETCH_10, api.PRE_FETCH_16, api.COMPARE_AND_WRITE:
key = DATA_PROTECT
asc = ASC_WRITE_PROTECT
glog.Warningf("sense")
goto sense
}
}
lba = getSCSIReadWriteOffset(scb)
tl = getSCSIReadWriteCount(scb)
// Verify that we are not doing i/o beyond the end-of-lun
if tl != 0 {
if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift {
key = ILLEGAL_REQUEST
asc = ASC_LBA_OUT_OF_RANGE
glog.Warningf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift)
goto sense
}
} else {
if lba >= dev.Size>>dev.BlockShift {
key = ILLEGAL_REQUEST
asc = ASC_LBA_OUT_OF_RANGE
glog.Warningf("sense")
goto sense
}
}
cmd.Offset = lba << dev.BlockShift
cmd.TL = tl << dev.BlockShift
// Handle residuals
switch opcode {
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
/*
if (cmd->tl != scsi_get_in_length(cmd))
scsi_set_in_resid_by_actual(cmd, cmd->tl);
*/
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
/*
if (cmd->tl != scsi_get_out_length(cmd)) {
scsi_set_out_resid_by_actual(cmd, cmd->tl);
/* We need to clamp the size of the in-buffer
* so that we dont try to write > cmd->tl in the
* backend store.
*
if (cmd->tl < scsi_get_out_length(cmd)) {
scsi_set_out_length(cmd, cmd->tl);
}
}
*/
}
err = dev.Storage.CommandSubmit(cmd)
if err != nil {
glog.Error(err)
key = HARDWARE_ERROR
asc = ASC_INTERNAL_TGT_FAILURE
} else {
return api.SAMStatGood
}
sense:
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
} }
func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat { func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
@@ -224,7 +405,7 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
scb = cmd.SCB.Bytes() scb = cmd.SCB.Bytes()
key = ILLEGAL_REQUEST key = ILLEGAL_REQUEST
asc = ASC_LUN_NOT_SUPPORTED asc = ASC_LUN_NOT_SUPPORTED
data = cmd.InSDBBuffer.Buffer data = &bytes.Buffer{}
bshift = cmd.Device.BlockShift bshift = cmd.Device.BlockShift
size = cmd.Device.Size >> bshift size = cmd.Device.Size >> bshift
) )
@@ -240,9 +421,11 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
goto sense goto sense
} }
if cmd.InSDBBuffer.Length < 8 { /*
goto overflow if cmd.InSDBBuffer.Length < 8 {
} goto overflow
}
*/
// data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1); // data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
if size>>32 != 0 { if size>>32 != 0 {
@@ -253,8 +436,9 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
// data[1] = __cpu_to_be32(1U << bshift); // data[1] = __cpu_to_be32(1U << bshift);
binary.Write(data, binary.BigEndian, uint32(1<<bshift)) binary.Write(data, binary.BigEndian, uint32(1<<bshift))
overflow: //overflow:
cmd.InSDBBuffer.Resid = 8 cmd.InSDBBuffer.Resid = 8
cmd.InSDBBuffer.Buffer = data
return api.SAMStatGood return api.SAMStatGood
sense: sense:
cmd.InSDBBuffer.Resid = 0 cmd.InSDBBuffer.Resid = 0
@@ -293,6 +477,12 @@ sense:
} }
func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat { func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat {
data := &bytes.Buffer{}
data.Write(util.MarshalUint64(1))
data.Write(util.MarshalUint32(1 << cmd.Device.BlockShift))
val := (cmd.Device.Attrs.Lbppbe << 16) | cmd.Device.Attrs.LowestAlignedLBA
data.Write(util.MarshalUint32(uint32(val)))
cmd.InSDBBuffer.Buffer = data
return api.SAMStatGood return api.SAMStatGood
} }
@@ -301,6 +491,13 @@ func SBCGetLbaStatus(host int, cmd *api.SCSICommand) api.SAMStat {
} }
func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat { func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
opcode := api.SCSICommandType(cmd.SCB.Bytes()[1] & 0x1f)
switch opcode {
case api.READ_CAPACITY:
return SBCReadCapacity(host, cmd)
case api.SAI_READ_CAPACITY_16:
return SBCReadCapacity16(host, cmd)
}
return api.SAMStatGood return api.SAMStatGood
} }

View File

@@ -16,17 +16,82 @@ limitations under the License.
package scsi package scsi
import "github.com/gostor/gotgt/pkg/api" import (
"bytes"
"encoding/binary"
"fmt"
"sync"
type CommandFunc func(host int, cmd *api.SCSICommand) api.SAMStat "github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api"
)
type SCSITargetService struct {
mutex sync.RWMutex
Targets []*api.SCSITarget
}
var _instance *SCSITargetService
func NewSCSITargetService() *SCSITargetService {
if _instance == nil {
_instance = &SCSITargetService{Targets: []*api.SCSITarget{}}
}
return _instance
}
func (s *SCSITargetService) GetTargetList() ([]api.SCSITarget, error) {
result := []api.SCSITarget{}
s.mutex.RLock()
for _, t := range s.Targets {
result = append(result, *t)
}
s.mutex.RUnlock()
return result, nil
}
func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) error {
var (
target *api.SCSITarget
itn *api.ITNexus
)
s.mutex.RLock()
for _, t := range s.Targets {
if t.TID == tid {
target = t
break
}
}
s.mutex.RUnlock()
if target == nil {
return fmt.Errorf("Cannot find the target with ID(%d)", tid)
}
scmd.Target = target
for _, it := range target.ITNexus {
if it.ID == scmd.CommandITNID {
itn = it
break
}
}
scmd.ITNexus = itn
scmd.Device = target.Devices[0]
result := scmd.Device.PerformCommand(tid, scmd)
scmd.Result = result.Stat
if result.Err != nil {
glog.Error(result.Err)
return result.Err
}
return nil
}
type SCSIServiceAction struct { type SCSIServiceAction struct {
ServiceAction uint32 ServiceAction uint32
CommandPerformFunc CommandFunc CommandPerformFunc api.CommandFunc
} }
type SCSIDeviceOperation struct { type SCSIDeviceOperation struct {
CommandPerformFunc CommandFunc CommandPerformFunc api.CommandFunc
ServiceAction *SCSIServiceAction ServiceAction *SCSIServiceAction
PRConflictBits uint8 PRConflictBits uint8
} }
@@ -36,15 +101,7 @@ type BaseSCSIDeviceProtocol struct {
SCSIDeviceOps []SCSIDeviceOperation SCSIDeviceOps []SCSIDeviceOperation
} }
type SCSIDeviceProtocol interface { func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
InitLu(lu *api.SCSILu) error
ExitLu(lu *api.SCSILu) error
ConfigLu(lu *api.SCSILu) error
OnlineLu(lu *api.SCSILu) error
OfflineLu(lu *api.SCSILu) error
}
func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
return SCSIDeviceOperation{ return SCSIDeviceOperation{
CommandPerformFunc: fn, CommandPerformFunc: fn,
ServiceAction: sa, ServiceAction: sa,
@@ -54,6 +111,9 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) { func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
senseBuffer := cmd.SenseBuffer senseBuffer := cmd.SenseBuffer
if senseBuffer == nil {
senseBuffer = &bytes.Buffer{}
}
if cmd.Device.Attrs.SenseFormat { if cmd.Device.Attrs.SenseFormat {
// descriptor format // descriptor format
// current, not deferred // current, not deferred
@@ -81,3 +141,44 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
cmd.SenseLength = length + 8 cmd.SenseLength = length + 8
} }
} }
func getSCSIReadWriteOffset(scb []byte) uint64 {
var off uint64
var opcode = api.SCSICommandType(scb[0])
switch opcode {
case api.READ_6, api.WRITE_6:
off = uint64((scb[1]&0x1f))<<16 + uint64(scb[2])<<8 + uint64(scb[3])
case api.READ_10, api.PRE_FETCH_10, api.WRITE_10, api.VERIFY_10, api.WRITE_VERIFY, api.WRITE_SAME, api.SYNCHRONIZE_CACHE, api.READ_12, api.WRITE_12, api.VERIFY_12, api.WRITE_VERIFY_12:
off = uint64(binary.BigEndian.Uint32(scb[2:]))
case api.READ_16, api.PRE_FETCH_16, api.WRITE_16, api.ORWRITE_16, api.VERIFY_16, api.WRITE_VERIFY_16, api.WRITE_SAME_16, api.SYNCHRONIZE_CACHE_16, api.COMPARE_AND_WRITE:
off = binary.BigEndian.Uint64(scb[2:])
default:
}
return off
}
func getSCSIReadWriteCount(scb []byte) uint32 {
var cnt uint32
var opcode = api.SCSICommandType(scb[0])
switch opcode {
case api.READ_6, api.WRITE_6:
cnt = uint32(scb[4])
if cnt == 0 {
cnt = 256
}
case api.READ_10, api.PRE_FETCH_10, api.WRITE_10, api.VERIFY_10, api.WRITE_VERIFY, api.WRITE_SAME, api.SYNCHRONIZE_CACHE:
cnt = uint32(scb[7])<<8 | uint32(scb[8])
case api.READ_12, api.WRITE_12, api.VERIFY_12, api.WRITE_VERIFY_12:
cnt = binary.BigEndian.Uint32(scb[6:])
case api.READ_16, api.PRE_FETCH_16, api.WRITE_16, api.ORWRITE_16, api.VERIFY_16, api.WRITE_VERIFY_16, api.WRITE_SAME_16, api.SYNCHRONIZE_CACHE_16:
cnt = binary.BigEndian.Uint32(scb[10:])
case api.COMPARE_AND_WRITE:
cnt = uint32(scb[13])
default:
}
return cnt
}

View File

@@ -21,7 +21,9 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"reflect"
"github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util" "github.com/gostor/gotgt/pkg/util"
) )
@@ -128,6 +130,39 @@ func SPCLuOnline(lu *api.SCSILu) error {
} }
func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat { func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
buf := &bytes.Buffer{}
var b byte = 0x75
if reflect.DeepEqual(util.MarshalUint64(cmd.Device.Lun)[0:7], cmd.Lun[0:7]) {
b = (uint8(0) & 0x7) << 5
b |= uint8(0) & 0x1f
}
buf.WriteByte(b)
b = 0
buf.WriteByte(b)
buf.WriteByte(byte(1))
b = 0x02
buf.WriteByte(b)
buf.WriteByte(0x00)
// byte 5
b = 0
b |= byte(1) << 4 & 0x30
buf.WriteByte(b)
// byte 6
b = 0
buf.WriteByte(b)
buf.WriteByte(0x02)
buf.Write([]byte{'1', '1', 'c', 'a', 'n', 's'})
buf.WriteByte(0x00)
buf.WriteByte(0x00)
buf.Write([]byte{'c', 'o', 'f', 'f', 'e', 'e'})
for i := 0; i < 10; i++ {
buf.WriteByte(0x00)
}
buf.Write([]byte{'1', '.', '0'})
buf.WriteByte(0x00)
data := buf.Bytes()
data[4] = byte(len(data) - 4)
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
return api.SAMStatGood return api.SAMStatGood
} }
@@ -137,25 +172,22 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
actualLength uint32 = 8 actualLength uint32 = 8
availLength uint32 = 0 availLength uint32 = 0
allocationLength uint32 allocationLength uint32
data *bytes.Buffer buf *bytes.Buffer = &bytes.Buffer{}
scb *bytes.Buffer = cmd.SCB scb *bytes.Buffer = cmd.SCB
) )
// Get Allocation Length // Get Allocation Length
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10]) allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
if allocationLength < 16 { if allocationLength < 16 {
glog.Warningf("goto sense, allocationLength < 16")
goto sense goto sense
} }
if cmd.InSDBBuffer.Length < allocationLength {
goto sense
}
data = cmd.InSDBBuffer.Buffer
remainLength = allocationLength - 8 remainLength = allocationLength - 8
availLength = 8 * uint32(len(cmd.Target.Devices)) availLength = 8 * uint32(len(cmd.Target.Devices))
binary.Write(data, binary.BigEndian, availLength) buf.Write(util.MarshalUint32(availLength))
cmd.InSDBBuffer.Resid = int32(actualLength) cmd.InSDBBuffer.Resid = int32(actualLength)
// Skip through to byte 8, Reserved // Skip through to byte 8, Reserved
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
data.WriteByte(0x00) buf.WriteByte(0x00)
} }
for _, lu := range cmd.Target.Devices { for _, lu := range cmd.Target.Devices {
@@ -168,10 +200,11 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
} }
lun = (0x3fff & lun) << 16 lun = (0x3fff & lun) << 16
lun = uint64(lun << 32) lun = uint64(lun << 32)
binary.Write(data, binary.BigEndian, lun) buf.Write(util.MarshalUint64(lun))
remainLength -= 8 remainLength -= 8
} }
} }
cmd.InSDBBuffer.Buffer = buf
return api.SAMStatGood return api.SAMStatGood
sense: sense:
cmd.InSDBBuffer.Resid = 0 cmd.InSDBBuffer.Resid = 0
@@ -218,9 +251,11 @@ func SPCStartStop(host int, cmd *api.SCSICommand) api.SAMStat {
} }
func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat { func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat {
if err := deviceReserve(cmd); err != nil { /*
return api.SAMStatReservationConflict if err := deviceReserve(cmd); err != nil {
} return api.SAMStatReservationConflict
}
*/
if cmd.Device.Attrs.Online { if cmd.Device.Attrs.Online {
return api.SAMStatGood return api.SAMStatGood
} }
@@ -247,7 +282,33 @@ func SPCPreventAllowMediaRemoval(host int, cmd *api.SCSICommand) api.SAMStat {
// 6.11 - MODE SENSE(6) // 6.11 - MODE SENSE(6)
// 6.12 - MODE SENSE(10) // 6.12 - MODE SENSE(10)
func SPCModeSense(host int, cmd *api.SCSICommand) api.SAMStat { func SPCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
var (
scb = cmd.SCB.Bytes()
mode6 = (scb[0] == 0x1a)
dbd = scb[1] & 0x8 /* Disable Block Descriptors */
pcode = scb[2] & 0x3f
pctrl = (scb[2] & 0xc0) >> 6
subpcode = scb[3]
blkDesctionLen = 0
key = ILLEGAL_REQUEST
asc = ASC_INVALID_FIELD_IN_CDB
)
if dbd == 0 {
blkDesctionLen = 8
}
if pctrl == 3 {
asc = ASC_SAVING_PARMS_UNSUP
goto sense
}
_ = dbd
_ = pcode
_ = subpcode
_ = mode6
_ = blkDesctionLen
return api.SAMStatGood return api.SAMStatGood
sense:
BuildSenseData(cmd, key, asc)
return api.SAMStatCheckCondition
} }
func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat { func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
@@ -263,10 +324,93 @@ sense:
return api.SAMStatCheckCondition return api.SAMStatCheckCondition
} }
func getSCSICmdSize(opcode api.SCSICommandType) byte {
var scsi_command_size = []byte{6, 10, 10, 12, 16, 12, 10, 10}
return scsi_command_size[(byte(opcode)>>5)&7]
}
func reportOpcodesAll(cmd *api.SCSICommand, rctd int) error {
var (
data = []byte{0x00, 0x00, 0x00, 0x00}
)
for _, i := range []api.SCSICommandType{api.TEST_UNIT_READY, api.WRITE_6, api.INQUIRY, api.READ_CAPACITY, api.WRITE_10, api.WRITE_16, api.REPORT_LUNS, api.WRITE_12} {
data = append(data, byte(i))
// reserved
data = append(data, 0x00)
// service action
data = append(data, 0x00)
data = append(data, 0x00)
// reserved
data = append(data, 0x00)
// flags : no service action, possibly timeout desc
if rctd != 0 {
data = append(data, 0x02)
} else {
data = append(data, 0x00)
}
// cdb length
length := getSCSICmdSize(i)
data = append(data, (length>>8)&0xff)
data = append(data, length&0xff)
// timeout descriptor
if rctd != 0 {
// length == 0x0a
data[1] = 0x0a
for n := 0; n < 12; n++ {
data = append(data, 0x00)
}
}
}
buf := util.MarshalUint32(uint32(len(data) - 4))
buf = append(buf, data[4:]...)
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(buf)
return nil
}
func reportOpcodeOne(cmd *api.SCSICommand, rctd int, opcode byte, rsa uint16, serviceAction bool) error {
return nil
}
// This is useful for the various commands using the SERVICE ACTION format. // This is useful for the various commands using the SERVICE ACTION format.
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat { func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
// TODO // TODO
scb := cmd.SCB.Bytes()
reporting_options := scb[2] & 0x07
opcode := scb[3]
rctd := int(scb[2] & 0x80)
rsa := util.GetUnalignedUint16(scb[4:])
switch reporting_options {
case 0x00: /* report all */
glog.V(3).Infof("Service Action: report all")
err := reportOpcodesAll(cmd, rctd)
if err != nil {
glog.Error(err)
goto sense
}
case 0x01: /* report one no service action*/
glog.V(3).Infof("Service Action: report one no service action")
err := reportOpcodeOne(cmd, rctd, opcode, rsa, false)
if err != nil {
glog.Error(err)
goto sense
}
case 0x02: /* report one service action */
glog.V(3).Infof("Service Action: report one service action")
err := reportOpcodeOne(cmd, rctd, opcode, rsa, true)
if err != nil {
glog.Error(err)
goto sense
}
default:
goto sense
}
return api.SAMStatGood return api.SAMStatGood
sense:
cmd.InSDBBuffer.Resid = 0
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
return api.SAMStatCheckCondition
} }
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat { func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
@@ -360,6 +504,7 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
var ( var (
allocationLength uint32 allocationLength uint32
actualLength uint32 actualLength uint32
data = &bytes.Buffer{}
) )
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8]) allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
@@ -372,8 +517,11 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
} else { } else {
actualLength = allocationLength actualLength = allocationLength
} }
binary.Write(cmd.InSDBBuffer.Buffer, binary.BigEndian, cmd.SenseBuffer.Bytes()[0:actualLength]) if cmd.SenseBuffer != nil {
data.Write(cmd.SenseBuffer.Bytes()[0:actualLength])
}
cmd.InSDBBuffer.Resid = int32(actualLength) cmd.InSDBBuffer.Resid = int32(actualLength)
cmd.InSDBBuffer.Buffer = data
// reset sense buffer in cmnd // reset sense buffer in cmnd
cmd.SenseBuffer = &bytes.Buffer{} cmd.SenseBuffer = &bytes.Buffer{}

View File

@@ -21,18 +21,28 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/port"
) )
func NewTarget(tid int, driverName, name string) (*api.SCSITarget, error) { func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
// verify the target ID // verify the target ID
// verify the target's Name // verify the target's Name
// verify the low level driver // verify the low level driver
var target = &api.SCSITarget{Name: name, TID: tid} var target = &api.SCSITarget{
var tgt = port.NewTargetDriver(driverName, target) Name: name,
target.SCSITargetDriver = tgt TID: tid,
Devices: []*api.SCSILu{},
}
lun, err := NewSCSILu(0, target)
if err != nil {
glog.Errorf("fail to create LU: %v", err)
return nil, err
}
s.mutex.Lock()
target.Devices = append(target.Devices, lun)
s.Targets = append(s.Targets, target)
s.mutex.Unlock()
return target, nil return target, nil
} }
@@ -40,7 +50,7 @@ func deviceReserve(cmd *api.SCSICommand) error {
var lu *api.SCSILu var lu *api.SCSILu
for _, dev := range cmd.Target.Devices { for _, dev := range cmd.Target.Devices {
if dev.Lun == cmd.Device.Lun { if dev.Lun == cmd.Device.Lun {
lu = &dev lu = dev
break break
} }
} }

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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -16,7 +16,16 @@ limitations under the License.
package util package util
import "encoding/binary" import (
"encoding/binary"
"os"
"syscall"
)
type KeyValue struct {
Key string
Value string
}
func GetUnalignedUint16(u8 []uint8) uint16 { func GetUnalignedUint16(u8 []uint8) uint16 {
return binary.BigEndian.Uint16(u8) return binary.BigEndian.Uint16(u8)
@@ -52,13 +61,49 @@ func ParseKVText(txt []byte) map[string]string {
return m return m
} }
func MarshalKVText(kv map[string]string) []byte { func MarshalKVText(kv []KeyValue) []byte {
var data []byte var data []byte
for k, v := range kv { for _, v := range kv {
data = append(data, []byte(k)...) data = append(data, []byte(v.Key)...)
data = append(data, '=') data = append(data, '=')
data = append(data, []byte(v)...) data = append(data, []byte(v.Value)...)
data = append(data, 0) data = append(data, 0)
} }
return data return data
} }
func MarshalUint32(i uint32) []byte {
var data []byte
for j := 0; j < 4; j++ {
b := byte(i >> uint(4*(3-j)) & 0xff)
data = append(data, b)
}
return data
}
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
}
const (
POSIX_FADV_NORMAL = iota
POSIX_FADV_RANDOM
POSIX_FADV_SEQUENTIAL
POSIX_FADV_WILLNEED
POSIX_FADV_DONTNEED
POSIX_FADV_NOREUSE
)
func Fadvise(file *os.File, off, length int64, advice uint32) error {
// syscall.SYS_FADVISE64 = 221
_, _, err := syscall.Syscall6(221, file.Fd(), uintptr(off), uintptr(length), uintptr(advice), 0, 0)
if err != 0 {
return err
}
return nil
}

27
pkg/util/util_darwin.go Normal file
View File

@@ -0,0 +1,27 @@
// +build darwin
/*
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 util
import (
"os"
"syscall"
)
func Fdatasync(file *os.File) error {
return syscall.Fsync(int(file.Fd()))
}

27
pkg/util/util_linux.go Normal file
View File

@@ -0,0 +1,27 @@
// +build linux
/*
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 util
import (
"os"
"syscall"
)
func Fdatasync(file *os.File) error {
return syscall.Fdatasync(int(file.Fd()))
}