fix bugs and make it run basically
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
125
pkg/port/iscsit/conn.go
Normal 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
592
pkg/port/iscsit/iscsid.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
47
pkg/port/service.go
Normal 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)
|
||||
}
|
||||
@@ -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.
|
||||
@@ -18,6 +18,7 @@ package scsi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
)
|
||||
@@ -28,15 +29,7 @@ type BaseBackingStore struct {
|
||||
OflagsSupported int
|
||||
}
|
||||
|
||||
type BackingStore interface {
|
||||
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)
|
||||
type BackingStoreFunc func() (api.BackingStore, error)
|
||||
|
||||
var registeredPlugins = map[string](BackingStoreFunc){}
|
||||
|
||||
@@ -44,7 +37,7 @@ func RegisterBackingStore(name string, f BackingStoreFunc) {
|
||||
registeredPlugins[name] = f
|
||||
}
|
||||
|
||||
func NewBackingStore(name string) (BackingStore, error) {
|
||||
func NewBackingStore(name string) (api.BackingStore, error) {
|
||||
if name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -59,8 +52,8 @@ type fakeBackingStore struct {
|
||||
BaseBackingStore
|
||||
}
|
||||
|
||||
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string, fd *int, size *uint64) error {
|
||||
return nil
|
||||
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
|
||||
|
||||
@@ -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,40 +16,188 @@ limitations under the License.
|
||||
|
||||
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() {
|
||||
scsi.RegisterBackingStore("file", new)
|
||||
}
|
||||
|
||||
type FileBackingStore struct {
|
||||
BaseBackingStore
|
||||
scsi.BaseBackingStore
|
||||
}
|
||||
|
||||
func new() (scsi.BackingStore, error) {
|
||||
return NullBackingStore{
|
||||
Name: "file",
|
||||
DataSize: 0,
|
||||
OflagsSupported: 0,
|
||||
func new() (api.BackingStore, error) {
|
||||
return &FileBackingStore{
|
||||
BaseBackingStore: scsi.BaseBackingStore{
|
||||
Name: "file",
|
||||
DataSize: 0,
|
||||
OflagsSupported: 0,
|
||||
},
|
||||
}, 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
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) Close(dev *SCSILu) error {
|
||||
func (bs *FileBackingStore) Exit(dev *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) Init(dev *SCSILu, Opts string) error {
|
||||
return nil
|
||||
}
|
||||
func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
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
|
||||
}
|
||||
|
||||
@@ -17,45 +17,47 @@ limitations under the License.
|
||||
package backingstore
|
||||
|
||||
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() {
|
||||
scsi.RegisterBackingStore("null", new)
|
||||
scsi.RegisterBackingStore("null", newNull)
|
||||
}
|
||||
|
||||
type NullBackingStore struct {
|
||||
BaseBackingStore
|
||||
scsi.BaseBackingStore
|
||||
}
|
||||
|
||||
func new() (scsi.BackingStore, error) {
|
||||
return NullBackingStore{
|
||||
Name: "null",
|
||||
DataSize: 0,
|
||||
OflagsSupported: 0,
|
||||
func newNull() (api.BackingStore, error) {
|
||||
return &NullBackingStore{
|
||||
BaseBackingStore: scsi.BaseBackingStore{
|
||||
Name: "null",
|
||||
DataSize: 0,
|
||||
OflagsSupported: 0,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
|
||||
glog.V(1).Infof("NULL backing store open, size: %d", size)
|
||||
func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) Close(dev *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) Close(dev *SCSILu) error {
|
||||
func (bs *NullBackingStore) Init(dev *api.SCSILu, Opts string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) Init(dev *SCSILu, Opts string) error {
|
||||
func (bs *NullBackingStore) Exit(dev *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) Exit(dev *SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error {
|
||||
cmd.Result = SAM_STAT_GOOD
|
||||
func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
|
||||
cmd.Result = api.SAM_STAT_GOOD
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,19 +16,51 @@ limitations under the License.
|
||||
|
||||
package scsi
|
||||
|
||||
import "github.com/gostor/gotgt/pkg/api"
|
||||
import (
|
||||
"os"
|
||||
|
||||
type SCSILuOps struct {
|
||||
*api.SCSILu
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
)
|
||||
|
||||
DeviceProtocol SCSIDeviceProtocol
|
||||
Storage *BackingStore
|
||||
Target *api.SCSITarget
|
||||
Attrs api.SCSILuPhyAttribute
|
||||
func NewSCSILu(lun uint64, target *api.SCSITarget) (*api.SCSILu, error) {
|
||||
sbc := NewSBCDevice()
|
||||
backing, err := NewBackingStore("file")
|
||||
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
|
||||
PerformCommand CommandFunc
|
||||
FinishCommand func(*api.SCSITarget, *api.SCSICommand)
|
||||
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
||||
op := int(cmd.SCB.Bytes()[0])
|
||||
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 {
|
||||
|
||||
251
pkg/scsi/sbc.go
251
pkg/scsi/sbc.go
@@ -18,9 +18,13 @@ limitations under the License.
|
||||
package scsi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,30 +40,75 @@ type SBCSCSIDeviceProtocol struct {
|
||||
BaseSCSIDeviceProtocol
|
||||
}
|
||||
|
||||
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
||||
return nil
|
||||
func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
|
||||
return sbc.SCSIDeviceOps[opcode]
|
||||
}
|
||||
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
|
||||
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
||||
var tgt = lu.Target
|
||||
// init LU's phy attribute
|
||||
lu.Attrs.DeviceType = api.TYPE_DISK
|
||||
lu.Attrs.Qualifier = false
|
||||
lu.Attrs.Thinprovisioning = false
|
||||
lu.Attrs.Removable = false
|
||||
lu.Attrs.Readonly = false
|
||||
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
|
||||
}
|
||||
|
||||
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{
|
||||
BaseSCSIDeviceProtocol{
|
||||
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[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.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.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.MAINT_PROTOCOL_IN] = 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.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
|
||||
}
|
||||
|
||||
data := cmd.InSDBBuffer.Buffer
|
||||
data.Next(2)
|
||||
buf := cmd.InSDBBuffer.Buffer
|
||||
data := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
if buf != nil {
|
||||
data = buf.Bytes()
|
||||
}
|
||||
|
||||
if cmd.SCB.Bytes()[0] == 0x1a {
|
||||
data.WriteByte(deviceSpecific)
|
||||
data[2] = deviceSpecific
|
||||
} else {
|
||||
data.Next(1)
|
||||
data.WriteByte(deviceSpecific)
|
||||
data[3] = deviceSpecific
|
||||
}
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||
|
||||
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 {
|
||||
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 {
|
||||
@@ -224,7 +405,7 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
scb = cmd.SCB.Bytes()
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_LUN_NOT_SUPPORTED
|
||||
data = cmd.InSDBBuffer.Buffer
|
||||
data = &bytes.Buffer{}
|
||||
bshift = cmd.Device.BlockShift
|
||||
size = cmd.Device.Size >> bshift
|
||||
)
|
||||
@@ -240,9 +421,11 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
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);
|
||||
if size>>32 != 0 {
|
||||
@@ -253,8 +436,9 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
|
||||
// data[1] = __cpu_to_be32(1U << bshift);
|
||||
binary.Write(data, binary.BigEndian, uint32(1<<bshift))
|
||||
overflow:
|
||||
//overflow:
|
||||
cmd.InSDBBuffer.Resid = 8
|
||||
cmd.InSDBBuffer.Buffer = data
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
@@ -293,6 +477,12 @@ sense:
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -301,6 +491,13 @@ func SBCGetLbaStatus(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
|
||||
}
|
||||
|
||||
|
||||
127
pkg/scsi/scsi.go
127
pkg/scsi/scsi.go
@@ -16,17 +16,82 @@ limitations under the License.
|
||||
|
||||
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 {
|
||||
ServiceAction uint32
|
||||
CommandPerformFunc CommandFunc
|
||||
CommandPerformFunc api.CommandFunc
|
||||
}
|
||||
|
||||
type SCSIDeviceOperation struct {
|
||||
CommandPerformFunc CommandFunc
|
||||
CommandPerformFunc api.CommandFunc
|
||||
ServiceAction *SCSIServiceAction
|
||||
PRConflictBits uint8
|
||||
}
|
||||
@@ -36,15 +101,7 @@ type BaseSCSIDeviceProtocol struct {
|
||||
SCSIDeviceOps []SCSIDeviceOperation
|
||||
}
|
||||
|
||||
type SCSIDeviceProtocol interface {
|
||||
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 {
|
||||
func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||
return SCSIDeviceOperation{
|
||||
CommandPerformFunc: fn,
|
||||
ServiceAction: sa,
|
||||
@@ -54,6 +111,9 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
|
||||
|
||||
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
||||
senseBuffer := cmd.SenseBuffer
|
||||
if senseBuffer == nil {
|
||||
senseBuffer = &bytes.Buffer{}
|
||||
}
|
||||
if cmd.Device.Attrs.SenseFormat {
|
||||
// descriptor format
|
||||
// current, not deferred
|
||||
@@ -81,3 +141,44 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
||||
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
|
||||
}
|
||||
|
||||
172
pkg/scsi/spc.go
172
pkg/scsi/spc.go
@@ -21,7 +21,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"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 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -137,25 +172,22 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
actualLength uint32 = 8
|
||||
availLength uint32 = 0
|
||||
allocationLength uint32
|
||||
data *bytes.Buffer
|
||||
buf *bytes.Buffer = &bytes.Buffer{}
|
||||
scb *bytes.Buffer = cmd.SCB
|
||||
)
|
||||
// Get Allocation Length
|
||||
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
|
||||
if allocationLength < 16 {
|
||||
glog.Warningf("goto sense, allocationLength < 16")
|
||||
goto sense
|
||||
}
|
||||
if cmd.InSDBBuffer.Length < allocationLength {
|
||||
goto sense
|
||||
}
|
||||
data = cmd.InSDBBuffer.Buffer
|
||||
remainLength = allocationLength - 8
|
||||
availLength = 8 * uint32(len(cmd.Target.Devices))
|
||||
binary.Write(data, binary.BigEndian, availLength)
|
||||
buf.Write(util.MarshalUint32(availLength))
|
||||
cmd.InSDBBuffer.Resid = int32(actualLength)
|
||||
// Skip through to byte 8, Reserved
|
||||
for i := 0; i < 4; i++ {
|
||||
data.WriteByte(0x00)
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
|
||||
for _, lu := range cmd.Target.Devices {
|
||||
@@ -168,10 +200,11 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
}
|
||||
lun = (0x3fff & lun) << 16
|
||||
lun = uint64(lun << 32)
|
||||
binary.Write(data, binary.BigEndian, lun)
|
||||
buf.Write(util.MarshalUint64(lun))
|
||||
remainLength -= 8
|
||||
}
|
||||
}
|
||||
cmd.InSDBBuffer.Buffer = buf
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
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 {
|
||||
if err := deviceReserve(cmd); err != nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
/*
|
||||
if err := deviceReserve(cmd); err != nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
*/
|
||||
if cmd.Device.Attrs.Online {
|
||||
return api.SAMStatGood
|
||||
}
|
||||
@@ -247,7 +282,33 @@ func SPCPreventAllowMediaRemoval(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
// 6.11 - MODE SENSE(6)
|
||||
// 6.12 - MODE SENSE(10)
|
||||
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
|
||||
sense:
|
||||
BuildSenseData(cmd, key, asc)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
@@ -263,10 +324,93 @@ sense:
|
||||
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.
|
||||
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
// 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
|
||||
|
||||
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 {
|
||||
@@ -360,6 +504,7 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
allocationLength uint32
|
||||
actualLength uint32
|
||||
data = &bytes.Buffer{}
|
||||
)
|
||||
|
||||
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
|
||||
@@ -372,8 +517,11 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
} else {
|
||||
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.Buffer = data
|
||||
|
||||
// reset sense buffer in cmnd
|
||||
cmd.SenseBuffer = &bytes.Buffer{}
|
||||
|
||||
@@ -21,18 +21,28 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
"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's Name
|
||||
|
||||
// verify the low level driver
|
||||
var target = &api.SCSITarget{Name: name, TID: tid}
|
||||
var tgt = port.NewTargetDriver(driverName, target)
|
||||
target.SCSITargetDriver = tgt
|
||||
var target = &api.SCSITarget{
|
||||
Name: name,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -40,7 +50,7 @@ func deviceReserve(cmd *api.SCSICommand) error {
|
||||
var lu *api.SCSILu
|
||||
for _, dev := range cmd.Target.Devices {
|
||||
if dev.Lun == cmd.Device.Lun {
|
||||
lu = &dev
|
||||
lu = dev
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +16,16 @@ limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import "encoding/binary"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type KeyValue struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func GetUnalignedUint16(u8 []uint8) uint16 {
|
||||
return binary.BigEndian.Uint16(u8)
|
||||
@@ -52,13 +61,49 @@ func ParseKVText(txt []byte) map[string]string {
|
||||
return m
|
||||
}
|
||||
|
||||
func MarshalKVText(kv map[string]string) []byte {
|
||||
func MarshalKVText(kv []KeyValue) []byte {
|
||||
var data []byte
|
||||
for k, v := range kv {
|
||||
data = append(data, []byte(k)...)
|
||||
for _, v := range kv {
|
||||
data = append(data, []byte(v.Key)...)
|
||||
data = append(data, '=')
|
||||
data = append(data, []byte(v)...)
|
||||
data = append(data, []byte(v.Value)...)
|
||||
data = append(data, 0)
|
||||
}
|
||||
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
27
pkg/util/util_darwin.go
Normal 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
27
pkg/util/util_linux.go
Normal 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()))
|
||||
}
|
||||
Reference in New Issue
Block a user