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
|
package port
|
||||||
|
|
||||||
import (
|
import "github.com/gostor/gotgt/pkg/api"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
|
||||||
"github.com/gostor/gotgt/pkg/port/iscsit"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SCSITargetDriver interface {
|
type SCSITargetDriver interface {
|
||||||
Init() error
|
Init() error
|
||||||
@@ -16,7 +29,6 @@ type SCSITargetDriver interface {
|
|||||||
CreateLu(lu *api.SCSILu) error
|
CreateLu(lu *api.SCSILu) error
|
||||||
GetLu(lun uint8) (uint64, error)
|
GetLu(lun uint8) (uint64, error)
|
||||||
|
|
||||||
ProcessCommand(buf []byte) ([]byte, error)
|
|
||||||
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
|
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,13 +70,3 @@ func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
|
|||||||
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
|
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (fake *fakeSCSITargetDriver) ProcessCommand(buf []byte) ([]byte, error) {
|
|
||||||
return []byte(""), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTargetDriver(driver string, tgt *api.SCSITarget) SCSITargetDriver {
|
|
||||||
if driver == "iscsi" {
|
|
||||||
return iscsit.NewISCSITarget(tgt)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015 The GoStor Authors All rights reserved.
|
Copyright 2016 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OpCode int
|
type OpCode int
|
||||||
@@ -52,6 +54,8 @@ var opCodeMap = map[OpCode]string{
|
|||||||
OpReject: "Reject",
|
OpReject: "Reject",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DataPadding = 4
|
||||||
|
|
||||||
type ISCSICommand struct {
|
type ISCSICommand struct {
|
||||||
OpCode OpCode
|
OpCode OpCode
|
||||||
RawHeader []byte
|
RawHeader []byte
|
||||||
@@ -68,7 +72,7 @@ type ISCSICommand struct {
|
|||||||
ExpStatSN uint32 // Expected status serial.
|
ExpStatSN uint32 // Expected status serial.
|
||||||
|
|
||||||
Read, Write bool
|
Read, Write bool
|
||||||
LUN uint8
|
LUN [8]uint8
|
||||||
Transit bool // Transit bit.
|
Transit bool // Transit bit.
|
||||||
Cont bool // Continue bit.
|
Cont bool // Continue bit.
|
||||||
CSG, NSG Stage // Current Stage, Next Stage.
|
CSG, NSG Stage // Current Stage, Next Stage.
|
||||||
@@ -102,6 +106,10 @@ func (cmd *ISCSICommand) Bytes() []byte {
|
|||||||
return cmd.scsiCmdRespBytes()
|
return cmd.scsiCmdRespBytes()
|
||||||
case OpSCSIIn:
|
case OpSCSIIn:
|
||||||
return cmd.dataInBytes()
|
return cmd.dataInBytes()
|
||||||
|
case OpTextResp:
|
||||||
|
return cmd.textRespBytes()
|
||||||
|
case OpNoopIn:
|
||||||
|
return cmd.noopInBytes()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -155,16 +163,8 @@ func ParseUint(data []byte) uint64 {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func MarshalUint64(i uint64) []byte {
|
|
||||||
var data []byte
|
|
||||||
for j := 0; j < 8; j++ {
|
|
||||||
b := byte(i >> uint(8*(7-j)) & 0xff)
|
|
||||||
data = append(data, b)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
func parseHeader(data []byte) (*ISCSICommand, error) {
|
func parseHeader(data []byte) (*ISCSICommand, error) {
|
||||||
if len(data) != 48 {
|
if len(data) != BHS_SIZE {
|
||||||
return nil, fmt.Errorf("garbled header")
|
return nil, fmt.Errorf("garbled header")
|
||||||
}
|
}
|
||||||
// TODO: sync.Pool
|
// TODO: sync.Pool
|
||||||
@@ -177,7 +177,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
|
|||||||
m.TaskTag = uint32(ParseUint(data[16:20]))
|
m.TaskTag = uint32(ParseUint(data[16:20]))
|
||||||
switch m.OpCode {
|
switch m.OpCode {
|
||||||
case OpSCSICmd:
|
case OpSCSICmd:
|
||||||
m.LUN = uint8(data[9])
|
m.LUN = [8]uint8{data[9]}
|
||||||
m.ExpectedDataLen = uint32(ParseUint(data[20:24]))
|
m.ExpectedDataLen = uint32(ParseUint(data[20:24]))
|
||||||
m.CmdSN = uint32(ParseUint(data[24:28]))
|
m.CmdSN = uint32(ParseUint(data[24:28]))
|
||||||
m.Read = data[1]&0x40 == 0x40
|
m.Read = data[1]&0x40 == 0x40
|
||||||
@@ -185,7 +185,7 @@ func parseHeader(data []byte) (*ISCSICommand, error) {
|
|||||||
m.CDB = data[32:48]
|
m.CDB = data[32:48]
|
||||||
m.ExpStatSN = uint32(ParseUint(data[28:32]))
|
m.ExpStatSN = uint32(ParseUint(data[28:32]))
|
||||||
case OpSCSIResp:
|
case OpSCSIResp:
|
||||||
case OpLoginReq:
|
case OpLoginReq, OpTextReq, OpNoopOut, OpLogoutReq:
|
||||||
m.Transit = m.Final
|
m.Transit = m.Final
|
||||||
m.Cont = data[1]&0x40 == 0x40
|
m.Cont = data[1]&0x40 == 0x40
|
||||||
if m.Cont && m.Transit {
|
if m.Cont && m.Transit {
|
||||||
@@ -229,13 +229,13 @@ func (m *ISCSICommand) scsiCmdRespBytes() []byte {
|
|||||||
for i := 0; i < 3*4; i++ {
|
for i := 0; i < 3*4; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||||
for i := 0; i < 3*4; i++ {
|
for i := 0; i < 3*4; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
@@ -259,24 +259,22 @@ func (m *ISCSICommand) dataInBytes() []byte {
|
|||||||
}
|
}
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
|
|
||||||
buf.WriteByte(0x00) // 4
|
buf.WriteByte(0x00) // 4
|
||||||
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||||
buf.WriteByte(0x00)
|
|
||||||
buf.WriteByte(byte(m.LUN))
|
|
||||||
// Skip through to byte 16
|
// Skip through to byte 16
|
||||||
for i := 0; i < 6; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(byte(m.LUN[i]))
|
||||||
}
|
}
|
||||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
// 11.7.4
|
// 11.7.4
|
||||||
buf.WriteByte(0xff)
|
buf.WriteByte(0xff)
|
||||||
}
|
}
|
||||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.DataSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.DataSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.BufferOffset))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.BufferOffset))[4:])
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
@@ -290,123 +288,79 @@ func (m *ISCSICommand) dataInBytes() []byte {
|
|||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
type InquiryData struct {
|
func (m *ISCSICommand) textRespBytes() []byte {
|
||||||
PeripheralQualifier int
|
|
||||||
PeripheralType int
|
|
||||||
Removable bool
|
|
||||||
Version int
|
|
||||||
SupportsACA bool
|
|
||||||
Hierarchical bool
|
|
||||||
SupportsSCC bool
|
|
||||||
HasACC bool
|
|
||||||
TargetGroupSupport int
|
|
||||||
ThirdPartyCopy bool
|
|
||||||
Protect bool
|
|
||||||
EnclosureServices bool
|
|
||||||
Multiport bool
|
|
||||||
MediaChanger bool
|
|
||||||
Vendor [8]byte
|
|
||||||
Product [16]byte
|
|
||||||
RevisionLevel [4]byte
|
|
||||||
SerialNumber uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (id *InquiryData) bytes() []byte {
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
buf.WriteByte(byte(OpTextResp))
|
||||||
var b byte
|
var b byte
|
||||||
b = (uint8(id.PeripheralQualifier) << 5) & 0xe0
|
if m.Final {
|
||||||
b |= uint8(id.PeripheralType) & 0x1f
|
|
||||||
buf.WriteByte(b)
|
|
||||||
b = 0
|
|
||||||
if id.Removable {
|
|
||||||
b = 0x80
|
|
||||||
}
|
|
||||||
buf.WriteByte(b)
|
|
||||||
buf.WriteByte(byte(id.Version))
|
|
||||||
b = 0x02
|
|
||||||
if id.SupportsACA {
|
|
||||||
b |= 0x20
|
|
||||||
}
|
|
||||||
if id.Hierarchical {
|
|
||||||
b |= 0x10
|
|
||||||
}
|
|
||||||
buf.WriteByte(b)
|
|
||||||
buf.WriteByte(0x00)
|
|
||||||
// byte 5
|
|
||||||
b = 0
|
|
||||||
if id.SupportsSCC {
|
|
||||||
b |= 0x80
|
b |= 0x80
|
||||||
}
|
}
|
||||||
if id.HasACC {
|
if m.Cont {
|
||||||
b |= 0x40
|
b |= 0x40
|
||||||
}
|
}
|
||||||
b |= byte(id.TargetGroupSupport) << 4 & 0x30
|
// byte 1
|
||||||
if id.ThirdPartyCopy {
|
|
||||||
b |= 0x08
|
|
||||||
}
|
|
||||||
if id.Protect {
|
|
||||||
b |= 0x01
|
|
||||||
}
|
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
// byte 6
|
|
||||||
b = 0
|
b = 0
|
||||||
if id.EnclosureServices {
|
|
||||||
b |= 0x40
|
|
||||||
}
|
|
||||||
if id.Multiport {
|
|
||||||
b |= 0x10
|
|
||||||
}
|
|
||||||
if id.MediaChanger {
|
|
||||||
b |= 0x08
|
|
||||||
}
|
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
buf.WriteByte(0x02)
|
buf.WriteByte(b)
|
||||||
buf.Write(id.Vendor[:])
|
buf.WriteByte(b)
|
||||||
buf.Write(id.Product[:])
|
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||||
buf.Write(id.RevisionLevel[:])
|
// Skip through to byte 12
|
||||||
buf.Write(MarshalUint64(id.SerialNumber))
|
for i := 0; i < 2*4; i++ {
|
||||||
for i := 0; i < 12; i++ {
|
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
data := buf.Bytes()
|
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||||
data[4] = byte(len(data) - 4)
|
for i := 0; i < 4; i++ {
|
||||||
return data
|
buf.WriteByte(0xff)
|
||||||
}
|
|
||||||
|
|
||||||
type Capacity struct {
|
|
||||||
LBA uint64
|
|
||||||
Blocksize uint32
|
|
||||||
ProtectionType uint8
|
|
||||||
PIExponent uint8
|
|
||||||
LogicalExponent uint8
|
|
||||||
ThinProvisioned bool
|
|
||||||
ThinProvReturnsZeros bool
|
|
||||||
LowestLBA uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Capacity) bytes() []byte {
|
|
||||||
// table 111
|
|
||||||
// http://www.seagate.com/staticfiles/support/disc/manuals/Interface%20manuals/100293068c.pdf
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
buf.Write(MarshalUint64(c.LBA))
|
|
||||||
buf.Write(MarshalUint64(uint64(c.Blocksize))[4:])
|
|
||||||
var b byte
|
|
||||||
if c.ProtectionType > 0 {
|
|
||||||
b |= 0x01
|
|
||||||
b |= c.ProtectionType << 1
|
|
||||||
b &= 0x0f
|
|
||||||
}
|
}
|
||||||
buf.WriteByte(b)
|
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||||
b = c.PIExponent << 4
|
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||||
b |= c.LogicalExponent
|
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||||
buf.WriteByte(b)
|
for i := 0; i < 3*4; i++ {
|
||||||
lowLBA := MarshalUint64(uint64(c.LowestLBA))[6:]
|
buf.WriteByte(0x00)
|
||||||
lowLBA[0] &= 0x3f
|
|
||||||
if c.ThinProvisioned {
|
|
||||||
lowLBA[0] &= 0x80
|
|
||||||
}
|
}
|
||||||
if c.ThinProvReturnsZeros {
|
rd := m.RawData
|
||||||
lowLBA[0] &= 0x40
|
for len(rd)%4 != 0 {
|
||||||
|
rd = append(rd, 0)
|
||||||
}
|
}
|
||||||
|
buf.Write(rd)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ISCSICommand) noopInBytes() []byte {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
|
||||||
|
buf.WriteByte(byte(OpNoopIn))
|
||||||
|
var b byte
|
||||||
|
b |= 0x80
|
||||||
|
// byte 1
|
||||||
|
buf.WriteByte(b)
|
||||||
|
|
||||||
|
b = 0
|
||||||
|
buf.WriteByte(b)
|
||||||
|
buf.WriteByte(b)
|
||||||
|
buf.WriteByte(b)
|
||||||
|
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||||
|
// Skip through to byte 12
|
||||||
|
for i := 0; i < 2*4; i++ {
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
buf.WriteByte(0xff)
|
||||||
|
}
|
||||||
|
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||||
|
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||||
|
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||||
|
for i := 0; i < 3*4; i++ {
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
rd := m.RawData
|
||||||
|
for len(rd)%4 != 0 {
|
||||||
|
rd = append(rd, 0)
|
||||||
|
}
|
||||||
|
buf.Write(rd)
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|||||||
125
pkg/port/iscsit/conn.go
Normal file
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -17,11 +17,38 @@ limitations under the License.
|
|||||||
// iSCSI Target Driver
|
// iSCSI Target Driver
|
||||||
package iscsit
|
package iscsit
|
||||||
|
|
||||||
import (
|
import "github.com/gostor/gotgt/pkg/api"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
const (
|
||||||
|
IOSTATE_FREE = iota
|
||||||
|
|
||||||
|
IOSTATE_RX_BHS
|
||||||
|
IOSTATE_RX_INIT_AHS
|
||||||
|
IOSTATE_RX_AHS
|
||||||
|
IOSTATE_RX_INIT_HDIGEST
|
||||||
|
IOSTATE_RX_HDIGEST
|
||||||
|
IOSTATE_RX_CHECK_HDIGEST
|
||||||
|
IOSTATE_RX_INIT_DATA
|
||||||
|
IOSTATE_RX_DATA
|
||||||
|
IOSTATE_RX_INIT_DDIGEST
|
||||||
|
IOSTATE_RX_DDIGEST
|
||||||
|
IOSTATE_RX_CHECK_DDIGEST
|
||||||
|
IOSTATE_RX_END
|
||||||
|
|
||||||
|
IOSTATE_TX_BHS
|
||||||
|
IOSTATE_TX_INIT_AHS
|
||||||
|
IOSTATE_TX_AHS
|
||||||
|
IOSTATE_TX_INIT_HDIGEST
|
||||||
|
IOSTATE_TX_HDIGEST
|
||||||
|
IOSTATE_TX_INIT_DATA
|
||||||
|
IOSTATE_TX_DATA
|
||||||
|
IOSTATE_TX_INIT_DDIGEST
|
||||||
|
IOSTATE_TX_DDIGEST
|
||||||
|
IOSTATE_TX_END
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ISCSI_OPCODE_MASK = 0x3F
|
||||||
|
|
||||||
type ISCSIDiscoveryMethod string
|
type ISCSIDiscoveryMethod string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -38,7 +65,7 @@ type ISCSIRedirectInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ISCSITarget struct {
|
type ISCSITarget struct {
|
||||||
*api.SCSITarget
|
api.SCSITarget
|
||||||
api.SCSITargetDriverCommon
|
api.SCSITargetDriverCommon
|
||||||
|
|
||||||
Sessions []*ISCSISession
|
Sessions []*ISCSISession
|
||||||
@@ -51,9 +78,9 @@ type ISCSITarget struct {
|
|||||||
NopCount int
|
NopCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||||
return &ISCSITarget{
|
return &ISCSITarget{
|
||||||
SCSITarget: target,
|
SCSITarget: *target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,96 +118,3 @@ func (tgt *ISCSITarget) GetLu(lun uint8) (uint64, error) {
|
|||||||
func (tgt *ISCSITarget) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
|
func (tgt *ISCSITarget) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (tgt *ISCSITarget) ProcessCommand(buf []byte) ([]byte, error) {
|
|
||||||
b := make([]byte, 48) // TODO: sync.Pool
|
|
||||||
b = buf[0:48]
|
|
||||||
m, err := parseHeader(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.RawHeader = b
|
|
||||||
if m.DataLen > 0 {
|
|
||||||
m.RawData = buf[48:m.DataLen]
|
|
||||||
}
|
|
||||||
resp := &ISCSICommand{}
|
|
||||||
switch m.OpCode {
|
|
||||||
case OpLoginReq:
|
|
||||||
resp = &ISCSICommand{
|
|
||||||
OpCode: OpLoginResp,
|
|
||||||
Transit: true,
|
|
||||||
NSG: FullFeaturePhase,
|
|
||||||
StatSN: m.ExpStatSN,
|
|
||||||
TaskTag: m.TaskTag,
|
|
||||||
ExpCmdSN: m.CmdSN,
|
|
||||||
MaxCmdSN: m.CmdSN,
|
|
||||||
RawData: util.MarshalKVText(map[string]string{
|
|
||||||
"HeaderDigest": "None",
|
|
||||||
"DataDigest": "None",
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case OpLogoutReq:
|
|
||||||
resp = &ISCSICommand{
|
|
||||||
OpCode: OpLogoutResp,
|
|
||||||
StatSN: m.ExpStatSN,
|
|
||||||
TaskTag: m.TaskTag,
|
|
||||||
ExpCmdSN: m.CmdSN,
|
|
||||||
MaxCmdSN: m.CmdSN,
|
|
||||||
}
|
|
||||||
case OpSCSICmd:
|
|
||||||
resp = &ISCSICommand{
|
|
||||||
OpCode: OpSCSIResp,
|
|
||||||
Final: true,
|
|
||||||
StatSN: m.ExpStatSN,
|
|
||||||
TaskTag: m.TaskTag,
|
|
||||||
ExpCmdSN: m.CmdSN + 1,
|
|
||||||
MaxCmdSN: m.CmdSN + 10,
|
|
||||||
}
|
|
||||||
switch api.SCSICommandType(m.CDB[0]) {
|
|
||||||
case api.TEST_UNIT_READY:
|
|
||||||
// test unit ready
|
|
||||||
resp.Status = api.SAM_STAT_GOOD
|
|
||||||
resp.SCSIResponse = 0x01
|
|
||||||
break
|
|
||||||
case api.READ_CAPACITY:
|
|
||||||
resp.OpCode = OpSCSIIn
|
|
||||||
resp.HasStatus = true
|
|
||||||
var data []byte
|
|
||||||
data = append(data, MarshalUint64(uint64(0))[4:]...)
|
|
||||||
data = append(data, MarshalUint64(uint64(0))[4:]...)
|
|
||||||
resp.RawData = data
|
|
||||||
break
|
|
||||||
case api.SERVICE_ACTION_IN:
|
|
||||||
resp.OpCode = OpSCSIIn
|
|
||||||
resp.HasStatus = true
|
|
||||||
sa := m.CDB[1] & 0x1f
|
|
||||||
switch sa {
|
|
||||||
case 0x10:
|
|
||||||
c := &Capacity{}
|
|
||||||
resp.RawData = c.bytes()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case api.INQUIRY:
|
|
||||||
resp.OpCode = OpSCSIIn
|
|
||||||
resp.HasStatus = true
|
|
||||||
alloc := int(ParseUint(m.CDB[3:5]))
|
|
||||||
inq := &InquiryData{
|
|
||||||
Vendor: [8]byte{'1', '1', 'c', 'a', 'n', 's'},
|
|
||||||
Product: [16]byte{'c', 'o', 'f', 'f', 'e', 'e'},
|
|
||||||
RevisionLevel: [4]byte{'1', '.', '0'},
|
|
||||||
SerialNumber: 52,
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(inq.bytes()) >= alloc {
|
|
||||||
resp.RawData = inq.bytes()[:alloc]
|
|
||||||
} else {
|
|
||||||
resp.RawData = inq.bytes()
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b1 := resp.Bytes()
|
|
||||||
return b1, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package iscsit
|
package iscsit
|
||||||
|
|
||||||
import "bytes"
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
func (m *ISCSICommand) loginRespBytes() []byte {
|
func (m *ISCSICommand) loginRespBytes() []byte {
|
||||||
// rfc7143 11.13
|
// rfc7143 11.13
|
||||||
@@ -20,20 +24,20 @@ func (m *ISCSICommand) loginRespBytes() []byte {
|
|||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
|
|
||||||
b = 0
|
b = 0
|
||||||
buf.WriteByte(b) // version-max
|
buf.WriteByte(b) // version-max
|
||||||
buf.WriteByte(b) // version-active
|
buf.WriteByte(b) // version-active
|
||||||
buf.WriteByte(b) // ahsLen
|
buf.WriteByte(b) // ahsLen
|
||||||
buf.Write(MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
|
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // data segment length, no padding
|
||||||
buf.Write(MarshalUint64(m.ISID)[2:])
|
buf.Write(util.MarshalUint64(m.ISID)[2:])
|
||||||
buf.Write(MarshalUint64(uint64(m.TSIH))[6:])
|
buf.Write(util.MarshalUint64(uint64(m.TSIH))[6:])
|
||||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
buf.WriteByte(b) // "reserved"
|
buf.WriteByte(b) // "reserved"
|
||||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||||
buf.WriteByte(byte(m.StatusClass))
|
buf.WriteByte(byte(m.StatusClass))
|
||||||
buf.WriteByte(byte(m.StatusDetail))
|
buf.WriteByte(byte(m.StatusDetail))
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package iscsit
|
package iscsit
|
||||||
|
|
||||||
import "bytes"
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
func (m *ISCSICommand) logoutRespBytes() []byte {
|
func (m *ISCSICommand) logoutRespBytes() []byte {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
@@ -11,13 +15,13 @@ func (m *ISCSICommand) logoutRespBytes() []byte {
|
|||||||
for i := 4; i < 16; i++ {
|
for i := 4; i < 16; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
buf.Write(MarshalUint64(uint64(m.TaskTag))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.TaskTag))[4:])
|
||||||
for i := 20; i < 24; i++ {
|
for i := 20; i < 24; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
buf.Write(MarshalUint64(uint64(m.StatSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.StatSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.ExpCmdSN))[4:])
|
||||||
buf.Write(MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
buf.Write(util.MarshalUint64(uint64(m.MaxCmdSN))[4:])
|
||||||
for i := 36; i < 48; i++ {
|
for i := 36; i < 48; i++ {
|
||||||
buf.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015 The GoStor Authors All rights reserved.
|
Copyright 2016 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015 The GoStor Authors All rights reserved.
|
Copyright 2016 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,13 +16,133 @@ limitations under the License.
|
|||||||
|
|
||||||
package iscsit
|
package iscsit
|
||||||
|
|
||||||
import "crypto/rand"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SESSION_NORMAL int = 0
|
||||||
|
SESSION_DISCOVERY int = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var DIGEST_CRC32C uint = 1 << 1
|
||||||
|
var DIGEST_NONE uint = 1 << 0
|
||||||
|
var DIGEST_ALL uint = DIGEST_NONE | DIGEST_CRC32C
|
||||||
|
var BHS_SIZE = 48
|
||||||
|
|
||||||
|
const (
|
||||||
|
MAX_QUEUE_CMD_MIN = 1
|
||||||
|
MAX_QUEUE_CMD_DEF = 128
|
||||||
|
MAX_QUEUE_CMD_MAX = 512
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ISCSI_PARAM_MAX_RECV_DLENGTH = iota
|
||||||
|
ISCSI_PARAM_HDRDGST_EN
|
||||||
|
ISCSI_PARAM_DATADGST_EN
|
||||||
|
ISCSI_PARAM_INITIAL_R2T_EN
|
||||||
|
ISCSI_PARAM_MAX_R2T
|
||||||
|
ISCSI_PARAM_IMM_DATA_EN
|
||||||
|
ISCSI_PARAM_FIRST_BURST
|
||||||
|
ISCSI_PARAM_MAX_BURST
|
||||||
|
ISCSI_PARAM_PDU_INORDER_EN
|
||||||
|
ISCSI_PARAM_DATASEQ_INORDER_EN
|
||||||
|
ISCSI_PARAM_ERL
|
||||||
|
ISCSI_PARAM_IFMARKER_EN
|
||||||
|
ISCSI_PARAM_OFMARKER_EN
|
||||||
|
ISCSI_PARAM_DEFAULTTIME2WAIT
|
||||||
|
ISCSI_PARAM_DEFAULTTIME2RETAIN
|
||||||
|
ISCSI_PARAM_OFMARKINT
|
||||||
|
ISCSI_PARAM_IFMARKINT
|
||||||
|
ISCSI_PARAM_MAXCONNECTIONS
|
||||||
|
/* iSCSI Extensions for RDMA (RFC5046) */
|
||||||
|
ISCSI_PARAM_RDMA_EXTENSIONS
|
||||||
|
ISCSI_PARAM_TARGET_RDSL
|
||||||
|
ISCSI_PARAM_INITIATOR_RDSL
|
||||||
|
ISCSI_PARAM_MAX_OUTST_PDU
|
||||||
|
/* "local" parmas, never sent to the initiator */
|
||||||
|
ISCSI_PARAM_FIRST_LOCAL
|
||||||
|
ISCSI_PARAM_MAX_XMIT_DLENGTH = ISCSI_PARAM_FIRST_LOCAL
|
||||||
|
ISCSI_PARAM_MAX_QUEUE_CMD
|
||||||
|
/* must always be last */
|
||||||
|
ISCSI_PARAM_MAX
|
||||||
|
)
|
||||||
|
|
||||||
type ISCSISessionParam struct {
|
type ISCSISessionParam struct {
|
||||||
State int
|
State int
|
||||||
Value uint
|
Value uint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The defaults here are according to the spec and must not be changed,
|
||||||
|
* otherwise the initiator may make the wrong assumption. If you want
|
||||||
|
* to change a value, edit the value in iscsi_target_create.
|
||||||
|
*
|
||||||
|
* The param MaxXmitDataSegmentLength doesn't really exist. It's a way
|
||||||
|
* to remember the RDSL of the initiator, which defaults to 8k if he has
|
||||||
|
* not told us otherwise.
|
||||||
|
*/
|
||||||
|
type iscsiSessionKeys struct {
|
||||||
|
name string
|
||||||
|
def uint
|
||||||
|
min uint
|
||||||
|
max uint
|
||||||
|
}
|
||||||
|
|
||||||
|
var sessionKeys []iscsiSessionKeys = []iscsiSessionKeys{
|
||||||
|
// ISCSI_PARAM_MAX_RECV_DLENGTH
|
||||||
|
{"MaxRecvDataSegmentLength", 8192, 512, 16777215},
|
||||||
|
// ISCSI_PARAM_HDRDGST_EN
|
||||||
|
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
|
||||||
|
// ISCSI_PARAM_DATADGST_EN
|
||||||
|
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
|
||||||
|
// ISCSI_PARAM_INITIAL_R2T_EN
|
||||||
|
{"InitialR2T", 1, 0, 1},
|
||||||
|
// ISCSI_PARAM_MAX_R2T
|
||||||
|
{"MaxOutstandingR2T", 1, 1, 65535},
|
||||||
|
// ISCSI_PARAM_IMM_DATA_EN
|
||||||
|
{"ImmediateData", 1, 0, 1},
|
||||||
|
// ISCSI_PARAM_FIRST_BURST
|
||||||
|
{"FirstBurstLength", 65536, 512, 16777215},
|
||||||
|
// ISCSI_PARAM_MAX_BURST
|
||||||
|
{"MaxBurstLength", 262144, 512, 16777215},
|
||||||
|
// ISCSI_PARAM_PDU_INORDER_EN
|
||||||
|
{"DataPDUInOrder", 1, 0, 1},
|
||||||
|
// ISCSI_PARAM_DATASEQ_INORDER_EN
|
||||||
|
{"DataSequenceInOrder", 1, 0, 1},
|
||||||
|
// ISCSI_PARAM_ERL
|
||||||
|
{"ErrorRecoveryLevel", 0, 0, 2},
|
||||||
|
// ISCSI_PARAM_IFMARKER_EN
|
||||||
|
{"IFMarker", 0, 0, 1},
|
||||||
|
// ISCSI_PARAM_OFMARKER_EN
|
||||||
|
{"OFMarker", 0, 0, 1},
|
||||||
|
// ISCSI_PARAM_DEFAULTTIME2WAIT
|
||||||
|
{"DefaultTime2Wait", 2, 0, 3600},
|
||||||
|
// ISCSI_PARAM_DEFAULTTIME2RETAIN
|
||||||
|
{"DefaultTime2Retain", 20, 0, 3600},
|
||||||
|
// ISCSI_PARAM_OFMARKINT
|
||||||
|
{"OFMarkInt", 2048, 1, 65535},
|
||||||
|
// ISCSI_PARAM_IFMARKINT
|
||||||
|
{"IFMarkInt", 2048, 1, 65535},
|
||||||
|
// ISCSI_PARAM_MAXCONNECTIONS
|
||||||
|
{"MaxConnections", 1, 1, 65535},
|
||||||
|
// ISCSI_PARAM_RDMA_EXTENSIONS
|
||||||
|
{"RDMAExtensions", 0, 0, 1},
|
||||||
|
// ISCSI_PARAM_TARGET_RDSL
|
||||||
|
{"TargetRecvDataSegmentLength", 8192, 512, 16777215},
|
||||||
|
// ISCSI_PARAM_INITIATOR_RDSL
|
||||||
|
{"InitiatorRecvDataSegmentLength", 8192, 512, 16777215},
|
||||||
|
// ISCSI_PARAM_MAX_OUTST_PDU
|
||||||
|
{"MaxOutstandingUnexpectedPDUs", 0, 2, 4294967295},
|
||||||
|
// "local" parmas, never sent to the initiator
|
||||||
|
// ISCSI_PARAM_MAX_XMIT_DLENGTH
|
||||||
|
{"MaxXmitDataSegmentLength", 8192, 512, 16777215},
|
||||||
|
// ISCSI_PARAM_MAX_QUEUE_CMD
|
||||||
|
{"MaxQueueCmd", MAX_QUEUE_CMD_DEF, MAX_QUEUE_CMD_MIN, MAX_QUEUE_CMD_MAX},
|
||||||
|
}
|
||||||
|
|
||||||
// Session is an iSCSI session.
|
// Session is an iSCSI session.
|
||||||
type ISCSISession struct {
|
type ISCSISession struct {
|
||||||
Refcount int
|
Refcount int
|
||||||
@@ -30,58 +150,112 @@ type ISCSISession struct {
|
|||||||
InitiatorAlias string
|
InitiatorAlias string
|
||||||
Target *ISCSITarget
|
Target *ISCSITarget
|
||||||
Isid uint64
|
Isid uint64
|
||||||
Tsih uint16
|
Tsih uint64
|
||||||
|
|
||||||
|
ExpCmdSN uint32
|
||||||
// only one connection per session
|
// only one connection per session
|
||||||
Connections []*ISCSIConnection
|
Connections []*iscsiConnection
|
||||||
Commands []*ISCSICommand
|
Commands []*ISCSICommand
|
||||||
PendingCommands []*ISCSICommand
|
PendingTasks taskQueue
|
||||||
ExpectionCommandSN uint32
|
MaxQueueCommand uint32
|
||||||
MaxQueueCommand uint32
|
SessionParam []ISCSISessionParam
|
||||||
SessionParam []ISCSISessionParam
|
Info string
|
||||||
Info string
|
Rdma int
|
||||||
Rdma int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ISCSIHeader struct {
|
type taskQueue []*iscsiTask
|
||||||
|
|
||||||
|
func (tq taskQueue) Len() int { return len(tq) }
|
||||||
|
|
||||||
|
func (tq taskQueue) Less(i, j int) bool {
|
||||||
|
// We want Pop to give us the highest, not lowest, priority so we use greater than here.
|
||||||
|
return tq[i].cmd.CmdSN > tq[j].cmd.CmdSN
|
||||||
}
|
}
|
||||||
|
|
||||||
type ISCSIPdu struct {
|
func (tq taskQueue) Swap(i, j int) {
|
||||||
Bhs ISCSIHeader
|
tq[i], tq[j] = tq[j], tq[i]
|
||||||
AhsSize uint
|
|
||||||
DataSize uint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ISCSIConnection struct {
|
func (tq *taskQueue) Push(x interface{}) {
|
||||||
State int
|
item := x.(*iscsiTask)
|
||||||
RxIostate int
|
*tq = append(*tq, item)
|
||||||
TxIostate int
|
}
|
||||||
Refcount int
|
|
||||||
|
|
||||||
Session *ISCSISession
|
func (tq *taskQueue) Pop() interface{} {
|
||||||
|
old := *tq
|
||||||
|
n := len(old)
|
||||||
|
item := old[n-1]
|
||||||
|
*tq = old[0 : n-1]
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
TID int
|
// The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
|
||||||
CID int
|
// appear in all iSCSI PDUs. In addition, when used, the Initiator Task
|
||||||
Auth AuthMethod
|
// Tag and Logical Unit Number always appear in the same location in the
|
||||||
StatSN uint32
|
// header.
|
||||||
ExpectionStatSN uint32
|
type iscsiHeader struct {
|
||||||
CommandSN uint32
|
opcode uint8
|
||||||
ExpectionCommandSN uint32
|
flags uint8 // Final bit
|
||||||
MaxCommandSN uint32
|
rsvd2 [2]uint8
|
||||||
Request ISCSIPdu
|
hlength uint8 // AHSs total length
|
||||||
Response ISCSIPdu
|
dlength [3]uint8 // Data length
|
||||||
|
lun [8]uint8
|
||||||
|
itt uint8 // Initiator Task Tag
|
||||||
|
ttt uint8 // Target Task Tag
|
||||||
|
statsn uint8
|
||||||
|
expStatSN uint8
|
||||||
|
maxStatSN uint8
|
||||||
|
other [12]uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type iscsiPdu struct {
|
||||||
|
bhs iscsiHeader
|
||||||
|
ahsSize uint
|
||||||
|
dataSize uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new session.
|
// New creates a new session.
|
||||||
func NewISCSISession() (*ISCSISession, error) {
|
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
|
||||||
var tsih uint16
|
var (
|
||||||
b := make([]byte, 2)
|
target *ISCSITarget
|
||||||
if _, err := rand.Read(b); err != nil {
|
tsih uint64
|
||||||
return nil, err
|
)
|
||||||
}
|
|
||||||
tsih += uint16(b[0]) << 8
|
|
||||||
tsih += uint16(b[1])
|
|
||||||
|
|
||||||
return &ISCSISession{
|
for _, t := range s.Targets {
|
||||||
Tsih: tsih,
|
if t.TID == conn.tid {
|
||||||
}, nil
|
target = t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if target == nil {
|
||||||
|
return nil, fmt.Errorf("No target found with tid(%d)", conn.tid)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
||||||
|
tsih = uint64(rand.Uint32())
|
||||||
|
for _, s := range target.Sessions {
|
||||||
|
if s.Tsih == tsih {
|
||||||
|
tsih = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tsih != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := &ISCSISession{
|
||||||
|
Tsih: tsih,
|
||||||
|
Initiator: conn.initiator,
|
||||||
|
InitiatorAlias: conn.initiatorAlias,
|
||||||
|
Target: target,
|
||||||
|
Connections: []*iscsiConnection{conn},
|
||||||
|
SessionParam: conn.sessionParam,
|
||||||
|
MaxQueueCommand: uint32(conn.sessionParam[ISCSI_PARAM_MAX_QUEUE_CMD].Value),
|
||||||
|
Rdma: 0,
|
||||||
|
ExpCmdSN: conn.expCmdSN,
|
||||||
|
}
|
||||||
|
conn.session = sess
|
||||||
|
return sess, nil
|
||||||
}
|
}
|
||||||
|
|||||||
47
pkg/port/service.go
Normal file
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -18,6 +18,7 @@ package scsi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
)
|
)
|
||||||
@@ -28,15 +29,7 @@ type BaseBackingStore struct {
|
|||||||
OflagsSupported int
|
OflagsSupported int
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackingStore interface {
|
type BackingStoreFunc func() (api.BackingStore, error)
|
||||||
Open(dev *api.SCSILu, path string, fd *int, size *uint64) error
|
|
||||||
Close(dev *api.SCSILu) error
|
|
||||||
Init(dev *api.SCSILu, Opts string) error
|
|
||||||
Exit(dev *api.SCSILu) error
|
|
||||||
CommandSubmit(cmd *api.SCSICommand) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type BackingStoreFunc func() (BackingStore, error)
|
|
||||||
|
|
||||||
var registeredPlugins = map[string](BackingStoreFunc){}
|
var registeredPlugins = map[string](BackingStoreFunc){}
|
||||||
|
|
||||||
@@ -44,7 +37,7 @@ func RegisterBackingStore(name string, f BackingStoreFunc) {
|
|||||||
registeredPlugins[name] = f
|
registeredPlugins[name] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBackingStore(name string) (BackingStore, error) {
|
func NewBackingStore(name string) (api.BackingStore, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -59,8 +52,8 @@ type fakeBackingStore struct {
|
|||||||
BaseBackingStore
|
BaseBackingStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string, fd *int, size *uint64) error {
|
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
|
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015 The GoStor Authors All rights reserved.
|
Copyright 2016 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,40 +16,188 @@ limitations under the License.
|
|||||||
|
|
||||||
package backingstore
|
package backingstore
|
||||||
|
|
||||||
import "github.com/gostor/gotgt/pkg/scsi"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
scsi.RegisterBackingStore("file", new)
|
scsi.RegisterBackingStore("file", new)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileBackingStore struct {
|
type FileBackingStore struct {
|
||||||
BaseBackingStore
|
scsi.BaseBackingStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func new() (scsi.BackingStore, error) {
|
func new() (api.BackingStore, error) {
|
||||||
return NullBackingStore{
|
return &FileBackingStore{
|
||||||
Name: "file",
|
BaseBackingStore: scsi.BaseBackingStore{
|
||||||
DataSize: 0,
|
Name: "file",
|
||||||
OflagsSupported: 0,
|
DataSize: 0,
|
||||||
|
OflagsSupported: 0,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *FileBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
|
func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
|
||||||
|
f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *FileBackingStore) Close(dev *api.SCSILu) error {
|
||||||
|
return dev.File.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *FileBackingStore) Close(dev *SCSILu) error {
|
func (bs *FileBackingStore) Exit(dev *api.SCSILu) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *FileBackingStore) Init(dev *SCSILu, Opts string) error {
|
func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
|
||||||
return nil
|
var (
|
||||||
}
|
scb = cmd.SCB.Bytes()
|
||||||
|
offset = cmd.Offset
|
||||||
|
opcode = api.SCSICommandType(scb[0])
|
||||||
|
lu = cmd.Device
|
||||||
|
key = scsi.ILLEGAL_REQUEST
|
||||||
|
asc = scsi.ASC_INVALID_FIELD_IN_CDB
|
||||||
|
wbuf []byte = []byte{}
|
||||||
|
rbuf []byte = []byte{}
|
||||||
|
length int
|
||||||
|
doVerify bool = false
|
||||||
|
doWrite bool = false
|
||||||
|
)
|
||||||
|
switch opcode {
|
||||||
|
case api.ORWRITE_16:
|
||||||
|
tmpbuf := []byte{}
|
||||||
|
length, err = lu.File.ReadAt(tmpbuf, int64(offset))
|
||||||
|
if length != len(tmpbuf) {
|
||||||
|
key = scsi.MEDIUM_ERROR
|
||||||
|
asc = scsi.ASC_READ_ERROR
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(tmpbuf)
|
||||||
|
|
||||||
func (bs *FileBackingStore) Exit(dev *SCSILu) error {
|
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
return nil
|
doWrite = true
|
||||||
}
|
goto write
|
||||||
|
case api.COMPARE_AND_WRITE:
|
||||||
|
// TODO
|
||||||
|
doWrite = true
|
||||||
|
goto write
|
||||||
|
case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16:
|
||||||
|
break
|
||||||
|
case api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
|
||||||
|
doVerify = true
|
||||||
|
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16:
|
||||||
|
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
doWrite = true
|
||||||
|
goto write
|
||||||
|
case api.WRITE_SAME, api.WRITE_SAME_16:
|
||||||
|
// TODO
|
||||||
|
break
|
||||||
|
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
|
||||||
|
length, err = lu.File.ReadAt(rbuf, int64(offset))
|
||||||
|
if err != nil {
|
||||||
|
key = scsi.MEDIUM_ERROR
|
||||||
|
asc = scsi.ASC_READ_ERROR
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i := 0; i < int(cmd.TL)-length; i++ {
|
||||||
|
rbuf = append(rbuf, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode != api.READ_6) && (scb[1]&0x10 != 0) {
|
||||||
|
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
|
||||||
|
}
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf)
|
||||||
|
case api.PRE_FETCH_10, api.PRE_FETCH_16:
|
||||||
|
err = util.Fadvise(lu.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED)
|
||||||
|
if err != nil {
|
||||||
|
key = scsi.MEDIUM_ERROR
|
||||||
|
asc = scsi.ASC_READ_ERROR
|
||||||
|
}
|
||||||
|
case api.VERIFY_10, api.VERIFY_12, api.VERIFY_16:
|
||||||
|
doVerify = true
|
||||||
|
goto verify
|
||||||
|
case api.UNMAP:
|
||||||
|
// TODO
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
write:
|
||||||
|
if doWrite {
|
||||||
|
// hack: wbuf = []byte("hello world!")
|
||||||
|
length, err = lu.File.WriteAt(wbuf, int64(offset))
|
||||||
|
if err != nil || length != len(wbuf) {
|
||||||
|
glog.Error(err)
|
||||||
|
key = scsi.MEDIUM_ERROR
|
||||||
|
asc = scsi.ASC_READ_ERROR
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
glog.V(2).Infof("write data at %d for length %d", offset, length)
|
||||||
|
var pg *api.ModePage
|
||||||
|
for _, p := range lu.ModePages {
|
||||||
|
if p.Pcode == 0x08 && p.SubPcode == 0 {
|
||||||
|
pg = &p
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pg == nil {
|
||||||
|
key = scsi.ILLEGAL_REQUEST
|
||||||
|
asc = scsi.ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) {
|
||||||
|
if err = util.Fdatasync(lu.File); err != nil {
|
||||||
|
key = scsi.MEDIUM_ERROR
|
||||||
|
asc = scsi.ASC_READ_ERROR
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) {
|
||||||
|
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verify:
|
||||||
|
if doVerify {
|
||||||
|
length, err = lu.File.ReadAt(rbuf, int64(offset))
|
||||||
|
if length != len(rbuf) {
|
||||||
|
key = scsi.MEDIUM_ERROR
|
||||||
|
asc = scsi.ASC_READ_ERROR
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if !bytes.Equal(cmd.OutSDBBuffer.Buffer.Bytes(), rbuf) {
|
||||||
|
err = fmt.Errorf("verify fail between out buffer and read buffer")
|
||||||
|
key = scsi.MISCOMPARE
|
||||||
|
asc = scsi.ASC_MISCOMPARE_DURING_VERIFY_OPERATION
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if scb[1]&0x10 != 0 {
|
||||||
|
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glog.Infof("io done %s", string(scb))
|
||||||
|
sense:
|
||||||
|
if err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_ = key
|
||||||
|
_ = asc
|
||||||
|
|
||||||
func (bs *FileBackingStore) CommandSubmit(cmd *SCSICommand) error {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,45 +17,47 @@ limitations under the License.
|
|||||||
package backingstore
|
package backingstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gostor/gotgt/pkg/scsi"
|
"os"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
scsi.RegisterBackingStore("null", new)
|
scsi.RegisterBackingStore("null", newNull)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NullBackingStore struct {
|
type NullBackingStore struct {
|
||||||
BaseBackingStore
|
scsi.BaseBackingStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func new() (scsi.BackingStore, error) {
|
func newNull() (api.BackingStore, error) {
|
||||||
return NullBackingStore{
|
return &NullBackingStore{
|
||||||
Name: "null",
|
BaseBackingStore: scsi.BaseBackingStore{
|
||||||
DataSize: 0,
|
Name: "null",
|
||||||
OflagsSupported: 0,
|
DataSize: 0,
|
||||||
|
OflagsSupported: 0,
|
||||||
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *NullBackingStore) Open(dev *SCSILu, path string, fd *int, size *uint64) error {
|
func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) {
|
||||||
glog.V(1).Infof("NULL backing store open, size: %d", size)
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *NullBackingStore) Close(dev *api.SCSILu) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *NullBackingStore) Close(dev *SCSILu) error {
|
func (bs *NullBackingStore) Init(dev *api.SCSILu, Opts string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *NullBackingStore) Init(dev *SCSILu, Opts string) error {
|
func (bs *NullBackingStore) Exit(dev *api.SCSILu) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bs *NullBackingStore) Exit(dev *SCSILu) error {
|
func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
|
||||||
return nil
|
cmd.Result = api.SAM_STAT_GOOD
|
||||||
}
|
|
||||||
|
|
||||||
func (bs *NullBackingStore) CommandSubmit(cmd *SCSICommand) error {
|
|
||||||
cmd.Result = SAM_STAT_GOOD
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,51 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
import "github.com/gostor/gotgt/pkg/api"
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
type SCSILuOps struct {
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
*api.SCSILu
|
)
|
||||||
|
|
||||||
DeviceProtocol SCSIDeviceProtocol
|
func NewSCSILu(lun uint64, target *api.SCSITarget) (*api.SCSILu, error) {
|
||||||
Storage *BackingStore
|
sbc := NewSBCDevice()
|
||||||
Target *api.SCSITarget
|
backing, err := NewBackingStore("file")
|
||||||
Attrs api.SCSILuPhyAttribute
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var lu = &api.SCSILu{
|
||||||
|
Lun: lun,
|
||||||
|
Target: target,
|
||||||
|
PerformCommand: luPerformCommand,
|
||||||
|
DeviceProtocol: sbc,
|
||||||
|
Storage: backing,
|
||||||
|
BlockShift: 0,
|
||||||
|
Size: 1024 * 1024 * 1024,
|
||||||
|
}
|
||||||
|
// hack this
|
||||||
|
if _, err = os.Stat("/tmp/data.txt"); err != nil && os.IsExist(err) {
|
||||||
|
os.Create("/tmp/data.txt")
|
||||||
|
}
|
||||||
|
f, err := backing.Open(lu, "/tmp/data.txt")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lu.File = f
|
||||||
|
lu.DeviceProtocol.InitLu(lu)
|
||||||
|
lu.Attrs.Online = true
|
||||||
|
return lu, nil
|
||||||
|
}
|
||||||
|
|
||||||
// function handler for command performing and finishing
|
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
PerformCommand CommandFunc
|
op := int(cmd.SCB.Bytes()[0])
|
||||||
FinishCommand func(*api.SCSITarget, *api.SCSICommand)
|
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
|
||||||
|
if fn != nil {
|
||||||
|
fnop := fn.(SCSIDeviceOperation)
|
||||||
|
// host := cmd.ITNexus.Host
|
||||||
|
host := 0
|
||||||
|
return fnop.CommandPerformFunc(host, cmd)
|
||||||
|
}
|
||||||
|
return api.SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
func luPreventRemoval(lu *api.SCSILu) bool {
|
func luPreventRemoval(lu *api.SCSILu) bool {
|
||||||
|
|||||||
251
pkg/scsi/sbc.go
251
pkg/scsi/sbc.go
@@ -18,9 +18,13 @@ limitations under the License.
|
|||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -36,30 +40,75 @@ type SBCSCSIDeviceProtocol struct {
|
|||||||
BaseSCSIDeviceProtocol
|
BaseSCSIDeviceProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sbc *SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
|
||||||
return nil
|
return sbc.SCSIDeviceOps[opcode]
|
||||||
}
|
}
|
||||||
func (sbc *SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
|
|
||||||
return nil
|
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
||||||
}
|
var tgt = lu.Target
|
||||||
func (sbc *SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
|
// init LU's phy attribute
|
||||||
return nil
|
lu.Attrs.DeviceType = api.TYPE_DISK
|
||||||
}
|
lu.Attrs.Qualifier = false
|
||||||
func (sbc *SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
|
lu.Attrs.Thinprovisioning = false
|
||||||
return nil
|
lu.Attrs.Removable = false
|
||||||
}
|
lu.Attrs.Readonly = false
|
||||||
func (sbc *SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
|
lu.Attrs.SWP = false
|
||||||
|
lu.Attrs.SenseFormat = false
|
||||||
|
lu.Attrs.VendorID = "GOSTOR"
|
||||||
|
lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR %x%d", tgt.TID, lu.Lun)
|
||||||
|
lu.Attrs.SCSISN = fmt.Sprintf("beaf%d%d", tgt.TID, lu.Lun)
|
||||||
|
lu.Attrs.ProductID = "VIRTUAL-DISK"
|
||||||
|
lu.Attrs.VersionDesction = []uint16{
|
||||||
|
0x04C0, // SBC-3 no version claimed
|
||||||
|
0x0960, // iSCSI
|
||||||
|
0x0300, // SPC-3
|
||||||
|
}
|
||||||
|
if lu.BlockShift == 0 {
|
||||||
|
lu.BlockShift = api.DefaultBlockShift
|
||||||
|
}
|
||||||
|
pages := []api.ModePage{}
|
||||||
|
// Vendor uniq - However most apps seem to call for mode page 0
|
||||||
|
pages = append(pages, api.ModePage{0, 0, []byte{}})
|
||||||
|
// Disconnect page
|
||||||
|
pages = append(pages, api.ModePage{2, 0, []byte{0x80, 0x80, 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}})
|
||||||
|
// Caching Page
|
||||||
|
pages = append(pages, api.ModePage{8, 0, []byte{0x14, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}})
|
||||||
|
|
||||||
|
// Control page
|
||||||
|
pages = append(pages, api.ModePage{0x0a, 0, []byte{2, 0x10, 0, 0, 0, 0, 0, 0, 2, 0}})
|
||||||
|
|
||||||
|
// Control Extensions mode page: TCMOS:1
|
||||||
|
pages = append(pages, api.ModePage{0x0a, 1, []byte{0x04, 0x00, 0x00}})
|
||||||
|
// Informational Exceptions Control page
|
||||||
|
pages = append(pages, api.ModePage{0x1c, 0, []byte{8, 0, 0, 0, 0, 0, 0, 0, 0, 0}})
|
||||||
|
lu.ModePages = pages
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSBCDevice() SBCSCSIDeviceProtocol {
|
func (sbc SBCSCSIDeviceProtocol) ConfigLu(lu *api.SCSILu) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc SBCSCSIDeviceProtocol) OnlineLu(lu *api.SCSILu) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc SBCSCSIDeviceProtocol) OfflineLu(lu *api.SCSILu) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc SBCSCSIDeviceProtocol) ExitLu(lu *api.SCSILu) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSBCDevice() api.SCSIDeviceProtocol {
|
||||||
var sbc = SBCSCSIDeviceProtocol{
|
var sbc = SBCSCSIDeviceProtocol{
|
||||||
BaseSCSIDeviceProtocol{
|
BaseSCSIDeviceProtocol{
|
||||||
Type: api.TYPE_DISK,
|
Type: api.TYPE_DISK,
|
||||||
SCSIDeviceOps: make([]SCSIDeviceOperation, 256),
|
SCSIDeviceOps: []SCSIDeviceOperation{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := 0; i <= 256; i++ {
|
for i := 0; i < 256; i++ {
|
||||||
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
|
sbc.SCSIDeviceOps = append(sbc.SCSIDeviceOps, NewSCSIDeviceOperation(SPCIllegalOp, nil, 0))
|
||||||
}
|
}
|
||||||
sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
|
sbc.SCSIDeviceOps[api.TEST_UNIT_READY] = NewSCSIDeviceOperation(SPCTestUnit, nil, 0)
|
||||||
@@ -103,9 +152,10 @@ func NewSBCDevice() SBCSCSIDeviceProtocol {
|
|||||||
sbc.SCSIDeviceOps[api.PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.PRE_FETCH_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.SYNCHRONIZE_CACHE_16] = NewSCSIDeviceOperation(SBCSyncCache, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
||||||
sbc.SCSIDeviceOps[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
|
sbc.SCSIDeviceOps[api.WRITE_SAME_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, 0)
|
||||||
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SBCServiceAction, nil, 0)
|
||||||
|
|
||||||
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
|
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
|
||||||
|
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
||||||
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
||||||
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
|
||||||
@@ -132,15 +182,18 @@ func SBCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
deviceSpecific |= 0x80
|
deviceSpecific |= 0x80
|
||||||
}
|
}
|
||||||
|
|
||||||
data := cmd.InSDBBuffer.Buffer
|
buf := cmd.InSDBBuffer.Buffer
|
||||||
data.Next(2)
|
data := []byte{0x00, 0x00, 0x00, 0x00}
|
||||||
|
if buf != nil {
|
||||||
|
data = buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
if cmd.SCB.Bytes()[0] == 0x1a {
|
if cmd.SCB.Bytes()[0] == 0x1a {
|
||||||
data.WriteByte(deviceSpecific)
|
data[2] = deviceSpecific
|
||||||
} else {
|
} else {
|
||||||
data.Next(1)
|
data[3] = deviceSpecific
|
||||||
data.WriteByte(deviceSpecific)
|
|
||||||
}
|
}
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||||
|
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
}
|
}
|
||||||
@@ -196,7 +249,135 @@ func SBCUnmap(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
return api.SAMStatGood
|
var (
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
dev = cmd.Device
|
||||||
|
scb = cmd.SCB.Bytes()
|
||||||
|
opcode = api.SCSICommandType(scb[0])
|
||||||
|
lba uint64
|
||||||
|
tl uint32
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if dev.Attrs.Removable && !dev.Attrs.Online {
|
||||||
|
key = NOT_READY
|
||||||
|
asc = ASC_MEDIUM_NOT_PRESENT
|
||||||
|
glog.Warningf("sense")
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
switch opcode {
|
||||||
|
case api.READ_10, api.READ_12, api.READ_16, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.ORWRITE_16,
|
||||||
|
api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16, api.COMPARE_AND_WRITE:
|
||||||
|
// We only support protection information type 0
|
||||||
|
/*
|
||||||
|
if scb[1]&0xe0 != 0 {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
glog.Warningf("sense")
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if cmd.OutSDBBuffer.Buffer == nil {
|
||||||
|
cmd.OutSDBBuffer.Buffer = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
case api.WRITE_SAME, api.WRITE_SAME_16:
|
||||||
|
// We dont support resource-provisioning so ANCHOR bit == 1 is an error.
|
||||||
|
if scb[1]&0x10 != 0 {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
// We only support unmap for thin provisioned LUNS
|
||||||
|
if (scb[1]&0x08 != 0) && !dev.Attrs.Thinprovisioning {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
// We only support protection information type 0
|
||||||
|
if scb[1]&0xe0 != 0 {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
// LBDATA and PBDATA can not both be set
|
||||||
|
if (scb[1] & 0x06) == 0x06 {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dev.Attrs.Readonly || dev.Attrs.SWP {
|
||||||
|
switch opcode {
|
||||||
|
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.ORWRITE_16,
|
||||||
|
api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16, api.WRITE_SAME, api.WRITE_SAME_16,
|
||||||
|
api.PRE_FETCH_10, api.PRE_FETCH_16, api.COMPARE_AND_WRITE:
|
||||||
|
key = DATA_PROTECT
|
||||||
|
asc = ASC_WRITE_PROTECT
|
||||||
|
glog.Warningf("sense")
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lba = getSCSIReadWriteOffset(scb)
|
||||||
|
tl = getSCSIReadWriteCount(scb)
|
||||||
|
|
||||||
|
// Verify that we are not doing i/o beyond the end-of-lun
|
||||||
|
if tl != 0 {
|
||||||
|
if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_LBA_OUT_OF_RANGE
|
||||||
|
glog.Warningf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift)
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if lba >= dev.Size>>dev.BlockShift {
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_LBA_OUT_OF_RANGE
|
||||||
|
glog.Warningf("sense")
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Offset = lba << dev.BlockShift
|
||||||
|
cmd.TL = tl << dev.BlockShift
|
||||||
|
|
||||||
|
// Handle residuals
|
||||||
|
switch opcode {
|
||||||
|
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
|
||||||
|
/*
|
||||||
|
if (cmd->tl != scsi_get_in_length(cmd))
|
||||||
|
scsi_set_in_resid_by_actual(cmd, cmd->tl);
|
||||||
|
*/
|
||||||
|
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16, api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
|
||||||
|
/*
|
||||||
|
if (cmd->tl != scsi_get_out_length(cmd)) {
|
||||||
|
scsi_set_out_resid_by_actual(cmd, cmd->tl);
|
||||||
|
|
||||||
|
/* We need to clamp the size of the in-buffer
|
||||||
|
* so that we dont try to write > cmd->tl in the
|
||||||
|
* backend store.
|
||||||
|
*
|
||||||
|
if (cmd->tl < scsi_get_out_length(cmd)) {
|
||||||
|
scsi_set_out_length(cmd, cmd->tl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dev.Storage.CommandSubmit(cmd)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
key = HARDWARE_ERROR
|
||||||
|
asc = ASC_INTERNAL_TGT_FAILURE
|
||||||
|
} else {
|
||||||
|
return api.SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
sense:
|
||||||
|
BuildSenseData(cmd, key, asc)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
@@ -224,7 +405,7 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
scb = cmd.SCB.Bytes()
|
scb = cmd.SCB.Bytes()
|
||||||
key = ILLEGAL_REQUEST
|
key = ILLEGAL_REQUEST
|
||||||
asc = ASC_LUN_NOT_SUPPORTED
|
asc = ASC_LUN_NOT_SUPPORTED
|
||||||
data = cmd.InSDBBuffer.Buffer
|
data = &bytes.Buffer{}
|
||||||
bshift = cmd.Device.BlockShift
|
bshift = cmd.Device.BlockShift
|
||||||
size = cmd.Device.Size >> bshift
|
size = cmd.Device.Size >> bshift
|
||||||
)
|
)
|
||||||
@@ -240,9 +421,11 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
goto sense
|
goto sense
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.InSDBBuffer.Length < 8 {
|
/*
|
||||||
goto overflow
|
if cmd.InSDBBuffer.Length < 8 {
|
||||||
}
|
goto overflow
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
|
// data[0] = (size >> 32) ? __cpu_to_be32(0xffffffff) : __cpu_to_be32(size - 1);
|
||||||
if size>>32 != 0 {
|
if size>>32 != 0 {
|
||||||
@@ -253,8 +436,9 @@ func SBCReadCapacity(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
|
|
||||||
// data[1] = __cpu_to_be32(1U << bshift);
|
// data[1] = __cpu_to_be32(1U << bshift);
|
||||||
binary.Write(data, binary.BigEndian, uint32(1<<bshift))
|
binary.Write(data, binary.BigEndian, uint32(1<<bshift))
|
||||||
overflow:
|
//overflow:
|
||||||
cmd.InSDBBuffer.Resid = 8
|
cmd.InSDBBuffer.Resid = 8
|
||||||
|
cmd.InSDBBuffer.Buffer = data
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
sense:
|
sense:
|
||||||
cmd.InSDBBuffer.Resid = 0
|
cmd.InSDBBuffer.Resid = 0
|
||||||
@@ -293,6 +477,12 @@ sense:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SBCReadCapacity16(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
data := &bytes.Buffer{}
|
||||||
|
data.Write(util.MarshalUint64(1))
|
||||||
|
data.Write(util.MarshalUint32(1 << cmd.Device.BlockShift))
|
||||||
|
val := (cmd.Device.Attrs.Lbppbe << 16) | cmd.Device.Attrs.LowestAlignedLBA
|
||||||
|
data.Write(util.MarshalUint32(uint32(val)))
|
||||||
|
cmd.InSDBBuffer.Buffer = data
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,6 +491,13 @@ func SBCGetLbaStatus(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SBCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
opcode := api.SCSICommandType(cmd.SCB.Bytes()[1] & 0x1f)
|
||||||
|
switch opcode {
|
||||||
|
case api.READ_CAPACITY:
|
||||||
|
return SBCReadCapacity(host, cmd)
|
||||||
|
case api.SAI_READ_CAPACITY_16:
|
||||||
|
return SBCReadCapacity16(host, cmd)
|
||||||
|
}
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
127
pkg/scsi/scsi.go
127
pkg/scsi/scsi.go
@@ -16,17 +16,82 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
import "github.com/gostor/gotgt/pkg/api"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
type CommandFunc func(host int, cmd *api.SCSICommand) api.SAMStat
|
"github.com/golang/glog"
|
||||||
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SCSITargetService struct {
|
||||||
|
mutex sync.RWMutex
|
||||||
|
Targets []*api.SCSITarget
|
||||||
|
}
|
||||||
|
|
||||||
|
var _instance *SCSITargetService
|
||||||
|
|
||||||
|
func NewSCSITargetService() *SCSITargetService {
|
||||||
|
if _instance == nil {
|
||||||
|
_instance = &SCSITargetService{Targets: []*api.SCSITarget{}}
|
||||||
|
}
|
||||||
|
return _instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SCSITargetService) GetTargetList() ([]api.SCSITarget, error) {
|
||||||
|
result := []api.SCSITarget{}
|
||||||
|
s.mutex.RLock()
|
||||||
|
for _, t := range s.Targets {
|
||||||
|
result = append(result, *t)
|
||||||
|
}
|
||||||
|
s.mutex.RUnlock()
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) error {
|
||||||
|
var (
|
||||||
|
target *api.SCSITarget
|
||||||
|
itn *api.ITNexus
|
||||||
|
)
|
||||||
|
s.mutex.RLock()
|
||||||
|
for _, t := range s.Targets {
|
||||||
|
if t.TID == tid {
|
||||||
|
target = t
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.mutex.RUnlock()
|
||||||
|
if target == nil {
|
||||||
|
return fmt.Errorf("Cannot find the target with ID(%d)", tid)
|
||||||
|
}
|
||||||
|
scmd.Target = target
|
||||||
|
for _, it := range target.ITNexus {
|
||||||
|
if it.ID == scmd.CommandITNID {
|
||||||
|
itn = it
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scmd.ITNexus = itn
|
||||||
|
|
||||||
|
scmd.Device = target.Devices[0]
|
||||||
|
result := scmd.Device.PerformCommand(tid, scmd)
|
||||||
|
scmd.Result = result.Stat
|
||||||
|
if result.Err != nil {
|
||||||
|
glog.Error(result.Err)
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type SCSIServiceAction struct {
|
type SCSIServiceAction struct {
|
||||||
ServiceAction uint32
|
ServiceAction uint32
|
||||||
CommandPerformFunc CommandFunc
|
CommandPerformFunc api.CommandFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type SCSIDeviceOperation struct {
|
type SCSIDeviceOperation struct {
|
||||||
CommandPerformFunc CommandFunc
|
CommandPerformFunc api.CommandFunc
|
||||||
ServiceAction *SCSIServiceAction
|
ServiceAction *SCSIServiceAction
|
||||||
PRConflictBits uint8
|
PRConflictBits uint8
|
||||||
}
|
}
|
||||||
@@ -36,15 +101,7 @@ type BaseSCSIDeviceProtocol struct {
|
|||||||
SCSIDeviceOps []SCSIDeviceOperation
|
SCSIDeviceOps []SCSIDeviceOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
type SCSIDeviceProtocol interface {
|
func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||||
InitLu(lu *api.SCSILu) error
|
|
||||||
ExitLu(lu *api.SCSILu) error
|
|
||||||
ConfigLu(lu *api.SCSILu) error
|
|
||||||
OnlineLu(lu *api.SCSILu) error
|
|
||||||
OfflineLu(lu *api.SCSILu) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
|
||||||
return SCSIDeviceOperation{
|
return SCSIDeviceOperation{
|
||||||
CommandPerformFunc: fn,
|
CommandPerformFunc: fn,
|
||||||
ServiceAction: sa,
|
ServiceAction: sa,
|
||||||
@@ -54,6 +111,9 @@ func NewSCSIDeviceOperation(fn CommandFunc, sa *SCSIServiceAction, pr uint8) SCS
|
|||||||
|
|
||||||
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
||||||
senseBuffer := cmd.SenseBuffer
|
senseBuffer := cmd.SenseBuffer
|
||||||
|
if senseBuffer == nil {
|
||||||
|
senseBuffer = &bytes.Buffer{}
|
||||||
|
}
|
||||||
if cmd.Device.Attrs.SenseFormat {
|
if cmd.Device.Attrs.SenseFormat {
|
||||||
// descriptor format
|
// descriptor format
|
||||||
// current, not deferred
|
// current, not deferred
|
||||||
@@ -81,3 +141,44 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
|||||||
cmd.SenseLength = length + 8
|
cmd.SenseLength = length + 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSCSIReadWriteOffset(scb []byte) uint64 {
|
||||||
|
var off uint64
|
||||||
|
var opcode = api.SCSICommandType(scb[0])
|
||||||
|
|
||||||
|
switch opcode {
|
||||||
|
case api.READ_6, api.WRITE_6:
|
||||||
|
off = uint64((scb[1]&0x1f))<<16 + uint64(scb[2])<<8 + uint64(scb[3])
|
||||||
|
case api.READ_10, api.PRE_FETCH_10, api.WRITE_10, api.VERIFY_10, api.WRITE_VERIFY, api.WRITE_SAME, api.SYNCHRONIZE_CACHE, api.READ_12, api.WRITE_12, api.VERIFY_12, api.WRITE_VERIFY_12:
|
||||||
|
off = uint64(binary.BigEndian.Uint32(scb[2:]))
|
||||||
|
case api.READ_16, api.PRE_FETCH_16, api.WRITE_16, api.ORWRITE_16, api.VERIFY_16, api.WRITE_VERIFY_16, api.WRITE_SAME_16, api.SYNCHRONIZE_CACHE_16, api.COMPARE_AND_WRITE:
|
||||||
|
off = binary.BigEndian.Uint64(scb[2:])
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return off
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSCSIReadWriteCount(scb []byte) uint32 {
|
||||||
|
var cnt uint32
|
||||||
|
var opcode = api.SCSICommandType(scb[0])
|
||||||
|
|
||||||
|
switch opcode {
|
||||||
|
case api.READ_6, api.WRITE_6:
|
||||||
|
cnt = uint32(scb[4])
|
||||||
|
if cnt == 0 {
|
||||||
|
cnt = 256
|
||||||
|
}
|
||||||
|
case api.READ_10, api.PRE_FETCH_10, api.WRITE_10, api.VERIFY_10, api.WRITE_VERIFY, api.WRITE_SAME, api.SYNCHRONIZE_CACHE:
|
||||||
|
cnt = uint32(scb[7])<<8 | uint32(scb[8])
|
||||||
|
case api.READ_12, api.WRITE_12, api.VERIFY_12, api.WRITE_VERIFY_12:
|
||||||
|
cnt = binary.BigEndian.Uint32(scb[6:])
|
||||||
|
case api.READ_16, api.PRE_FETCH_16, api.WRITE_16, api.ORWRITE_16, api.VERIFY_16, api.WRITE_VERIFY_16, api.WRITE_SAME_16, api.SYNCHRONIZE_CACHE_16:
|
||||||
|
cnt = binary.BigEndian.Uint32(scb[10:])
|
||||||
|
case api.COMPARE_AND_WRITE:
|
||||||
|
cnt = uint32(scb[13])
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
|||||||
172
pkg/scsi/spc.go
172
pkg/scsi/spc.go
@@ -21,7 +21,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
)
|
)
|
||||||
@@ -128,6 +130,39 @@ func SPCLuOnline(lu *api.SCSILu) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
var b byte = 0x75
|
||||||
|
if reflect.DeepEqual(util.MarshalUint64(cmd.Device.Lun)[0:7], cmd.Lun[0:7]) {
|
||||||
|
b = (uint8(0) & 0x7) << 5
|
||||||
|
b |= uint8(0) & 0x1f
|
||||||
|
}
|
||||||
|
buf.WriteByte(b)
|
||||||
|
b = 0
|
||||||
|
buf.WriteByte(b)
|
||||||
|
buf.WriteByte(byte(1))
|
||||||
|
b = 0x02
|
||||||
|
buf.WriteByte(b)
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
// byte 5
|
||||||
|
b = 0
|
||||||
|
b |= byte(1) << 4 & 0x30
|
||||||
|
buf.WriteByte(b)
|
||||||
|
// byte 6
|
||||||
|
b = 0
|
||||||
|
buf.WriteByte(b)
|
||||||
|
buf.WriteByte(0x02)
|
||||||
|
buf.Write([]byte{'1', '1', 'c', 'a', 'n', 's'})
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
buf.Write([]byte{'c', 'o', 'f', 'f', 'e', 'e'})
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
}
|
||||||
|
buf.Write([]byte{'1', '.', '0'})
|
||||||
|
buf.WriteByte(0x00)
|
||||||
|
data := buf.Bytes()
|
||||||
|
data[4] = byte(len(data) - 4)
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,25 +172,22 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
actualLength uint32 = 8
|
actualLength uint32 = 8
|
||||||
availLength uint32 = 0
|
availLength uint32 = 0
|
||||||
allocationLength uint32
|
allocationLength uint32
|
||||||
data *bytes.Buffer
|
buf *bytes.Buffer = &bytes.Buffer{}
|
||||||
scb *bytes.Buffer = cmd.SCB
|
scb *bytes.Buffer = cmd.SCB
|
||||||
)
|
)
|
||||||
// Get Allocation Length
|
// Get Allocation Length
|
||||||
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
|
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
|
||||||
if allocationLength < 16 {
|
if allocationLength < 16 {
|
||||||
|
glog.Warningf("goto sense, allocationLength < 16")
|
||||||
goto sense
|
goto sense
|
||||||
}
|
}
|
||||||
if cmd.InSDBBuffer.Length < allocationLength {
|
|
||||||
goto sense
|
|
||||||
}
|
|
||||||
data = cmd.InSDBBuffer.Buffer
|
|
||||||
remainLength = allocationLength - 8
|
remainLength = allocationLength - 8
|
||||||
availLength = 8 * uint32(len(cmd.Target.Devices))
|
availLength = 8 * uint32(len(cmd.Target.Devices))
|
||||||
binary.Write(data, binary.BigEndian, availLength)
|
buf.Write(util.MarshalUint32(availLength))
|
||||||
cmd.InSDBBuffer.Resid = int32(actualLength)
|
cmd.InSDBBuffer.Resid = int32(actualLength)
|
||||||
// Skip through to byte 8, Reserved
|
// Skip through to byte 8, Reserved
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
data.WriteByte(0x00)
|
buf.WriteByte(0x00)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, lu := range cmd.Target.Devices {
|
for _, lu := range cmd.Target.Devices {
|
||||||
@@ -168,10 +200,11 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
}
|
}
|
||||||
lun = (0x3fff & lun) << 16
|
lun = (0x3fff & lun) << 16
|
||||||
lun = uint64(lun << 32)
|
lun = uint64(lun << 32)
|
||||||
binary.Write(data, binary.BigEndian, lun)
|
buf.Write(util.MarshalUint64(lun))
|
||||||
remainLength -= 8
|
remainLength -= 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cmd.InSDBBuffer.Buffer = buf
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
sense:
|
sense:
|
||||||
cmd.InSDBBuffer.Resid = 0
|
cmd.InSDBBuffer.Resid = 0
|
||||||
@@ -218,9 +251,11 @@ func SPCStartStop(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCTestUnit(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
if err := deviceReserve(cmd); err != nil {
|
/*
|
||||||
return api.SAMStatReservationConflict
|
if err := deviceReserve(cmd); err != nil {
|
||||||
}
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
*/
|
||||||
if cmd.Device.Attrs.Online {
|
if cmd.Device.Attrs.Online {
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
}
|
}
|
||||||
@@ -247,7 +282,33 @@ func SPCPreventAllowMediaRemoval(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
// 6.11 - MODE SENSE(6)
|
// 6.11 - MODE SENSE(6)
|
||||||
// 6.12 - MODE SENSE(10)
|
// 6.12 - MODE SENSE(10)
|
||||||
func SPCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCModeSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
scb = cmd.SCB.Bytes()
|
||||||
|
mode6 = (scb[0] == 0x1a)
|
||||||
|
dbd = scb[1] & 0x8 /* Disable Block Descriptors */
|
||||||
|
pcode = scb[2] & 0x3f
|
||||||
|
pctrl = (scb[2] & 0xc0) >> 6
|
||||||
|
subpcode = scb[3]
|
||||||
|
blkDesctionLen = 0
|
||||||
|
key = ILLEGAL_REQUEST
|
||||||
|
asc = ASC_INVALID_FIELD_IN_CDB
|
||||||
|
)
|
||||||
|
if dbd == 0 {
|
||||||
|
blkDesctionLen = 8
|
||||||
|
}
|
||||||
|
if pctrl == 3 {
|
||||||
|
asc = ASC_SAVING_PARMS_UNSUP
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
_ = dbd
|
||||||
|
_ = pcode
|
||||||
|
_ = subpcode
|
||||||
|
_ = mode6
|
||||||
|
_ = blkDesctionLen
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
sense:
|
||||||
|
BuildSenseData(cmd, key, asc)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCSendDiagnostics(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
@@ -263,10 +324,93 @@ sense:
|
|||||||
return api.SAMStatCheckCondition
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSCSICmdSize(opcode api.SCSICommandType) byte {
|
||||||
|
var scsi_command_size = []byte{6, 10, 10, 12, 16, 12, 10, 10}
|
||||||
|
|
||||||
|
return scsi_command_size[(byte(opcode)>>5)&7]
|
||||||
|
}
|
||||||
|
|
||||||
|
func reportOpcodesAll(cmd *api.SCSICommand, rctd int) error {
|
||||||
|
var (
|
||||||
|
data = []byte{0x00, 0x00, 0x00, 0x00}
|
||||||
|
)
|
||||||
|
for _, i := range []api.SCSICommandType{api.TEST_UNIT_READY, api.WRITE_6, api.INQUIRY, api.READ_CAPACITY, api.WRITE_10, api.WRITE_16, api.REPORT_LUNS, api.WRITE_12} {
|
||||||
|
data = append(data, byte(i))
|
||||||
|
// reserved
|
||||||
|
data = append(data, 0x00)
|
||||||
|
// service action
|
||||||
|
data = append(data, 0x00)
|
||||||
|
data = append(data, 0x00)
|
||||||
|
// reserved
|
||||||
|
data = append(data, 0x00)
|
||||||
|
// flags : no service action, possibly timeout desc
|
||||||
|
if rctd != 0 {
|
||||||
|
data = append(data, 0x02)
|
||||||
|
} else {
|
||||||
|
data = append(data, 0x00)
|
||||||
|
}
|
||||||
|
// cdb length
|
||||||
|
length := getSCSICmdSize(i)
|
||||||
|
data = append(data, (length>>8)&0xff)
|
||||||
|
data = append(data, length&0xff)
|
||||||
|
// timeout descriptor
|
||||||
|
if rctd != 0 {
|
||||||
|
// length == 0x0a
|
||||||
|
data[1] = 0x0a
|
||||||
|
for n := 0; n < 12; n++ {
|
||||||
|
data = append(data, 0x00)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf := util.MarshalUint32(uint32(len(data) - 4))
|
||||||
|
buf = append(buf, data[4:]...)
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(buf)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reportOpcodeOne(cmd *api.SCSICommand, rctd int, opcode byte, rsa uint16, serviceAction bool) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// This is useful for the various commands using the SERVICE ACTION format.
|
// This is useful for the various commands using the SERVICE ACTION format.
|
||||||
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
// TODO
|
// TODO
|
||||||
|
scb := cmd.SCB.Bytes()
|
||||||
|
reporting_options := scb[2] & 0x07
|
||||||
|
opcode := scb[3]
|
||||||
|
rctd := int(scb[2] & 0x80)
|
||||||
|
rsa := util.GetUnalignedUint16(scb[4:])
|
||||||
|
switch reporting_options {
|
||||||
|
case 0x00: /* report all */
|
||||||
|
glog.V(3).Infof("Service Action: report all")
|
||||||
|
err := reportOpcodesAll(cmd, rctd)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
case 0x01: /* report one no service action*/
|
||||||
|
glog.V(3).Infof("Service Action: report one no service action")
|
||||||
|
err := reportOpcodeOne(cmd, rctd, opcode, rsa, false)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
case 0x02: /* report one service action */
|
||||||
|
glog.V(3).Infof("Service Action: report one service action")
|
||||||
|
err := reportOpcodeOne(cmd, rctd, opcode, rsa, true)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
@@ -360,6 +504,7 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
var (
|
var (
|
||||||
allocationLength uint32
|
allocationLength uint32
|
||||||
actualLength uint32
|
actualLength uint32
|
||||||
|
data = &bytes.Buffer{}
|
||||||
)
|
)
|
||||||
|
|
||||||
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
|
allocationLength = util.GetUnalignedUint32(cmd.SCB.Bytes()[4:8])
|
||||||
@@ -372,8 +517,11 @@ func SPCRequestSense(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
} else {
|
} else {
|
||||||
actualLength = allocationLength
|
actualLength = allocationLength
|
||||||
}
|
}
|
||||||
binary.Write(cmd.InSDBBuffer.Buffer, binary.BigEndian, cmd.SenseBuffer.Bytes()[0:actualLength])
|
if cmd.SenseBuffer != nil {
|
||||||
|
data.Write(cmd.SenseBuffer.Bytes()[0:actualLength])
|
||||||
|
}
|
||||||
cmd.InSDBBuffer.Resid = int32(actualLength)
|
cmd.InSDBBuffer.Resid = int32(actualLength)
|
||||||
|
cmd.InSDBBuffer.Buffer = data
|
||||||
|
|
||||||
// reset sense buffer in cmnd
|
// reset sense buffer in cmnd
|
||||||
cmd.SenseBuffer = &bytes.Buffer{}
|
cmd.SenseBuffer = &bytes.Buffer{}
|
||||||
|
|||||||
@@ -21,18 +21,28 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
"github.com/gostor/gotgt/pkg/port"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTarget(tid int, driverName, name string) (*api.SCSITarget, error) {
|
func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
|
||||||
// verify the target ID
|
// verify the target ID
|
||||||
|
|
||||||
// verify the target's Name
|
// verify the target's Name
|
||||||
|
|
||||||
// verify the low level driver
|
// verify the low level driver
|
||||||
var target = &api.SCSITarget{Name: name, TID: tid}
|
var target = &api.SCSITarget{
|
||||||
var tgt = port.NewTargetDriver(driverName, target)
|
Name: name,
|
||||||
target.SCSITargetDriver = tgt
|
TID: tid,
|
||||||
|
Devices: []*api.SCSILu{},
|
||||||
|
}
|
||||||
|
lun, err := NewSCSILu(0, target)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("fail to create LU: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.mutex.Lock()
|
||||||
|
target.Devices = append(target.Devices, lun)
|
||||||
|
s.Targets = append(s.Targets, target)
|
||||||
|
s.mutex.Unlock()
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +50,7 @@ func deviceReserve(cmd *api.SCSICommand) error {
|
|||||||
var lu *api.SCSILu
|
var lu *api.SCSILu
|
||||||
for _, dev := range cmd.Target.Devices {
|
for _, dev := range cmd.Target.Devices {
|
||||||
if dev.Lun == cmd.Device.Lun {
|
if dev.Lun == cmd.Device.Lun {
|
||||||
lu = &dev
|
lu = dev
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015 The GoStor Authors All rights reserved.
|
Copyright 2016 The GoStor Authors All rights reserved.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -16,7 +16,16 @@ limitations under the License.
|
|||||||
|
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import "encoding/binary"
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyValue struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
func GetUnalignedUint16(u8 []uint8) uint16 {
|
func GetUnalignedUint16(u8 []uint8) uint16 {
|
||||||
return binary.BigEndian.Uint16(u8)
|
return binary.BigEndian.Uint16(u8)
|
||||||
@@ -52,13 +61,49 @@ func ParseKVText(txt []byte) map[string]string {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func MarshalKVText(kv map[string]string) []byte {
|
func MarshalKVText(kv []KeyValue) []byte {
|
||||||
var data []byte
|
var data []byte
|
||||||
for k, v := range kv {
|
for _, v := range kv {
|
||||||
data = append(data, []byte(k)...)
|
data = append(data, []byte(v.Key)...)
|
||||||
data = append(data, '=')
|
data = append(data, '=')
|
||||||
data = append(data, []byte(v)...)
|
data = append(data, []byte(v.Value)...)
|
||||||
data = append(data, 0)
|
data = append(data, 0)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MarshalUint32(i uint32) []byte {
|
||||||
|
var data []byte
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
b := byte(i >> uint(4*(3-j)) & 0xff)
|
||||||
|
data = append(data, b)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshalUint64(i uint64) []byte {
|
||||||
|
var data []byte
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
b := byte(i >> uint(8*(7-j)) & 0xff)
|
||||||
|
data = append(data, b)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
POSIX_FADV_NORMAL = iota
|
||||||
|
POSIX_FADV_RANDOM
|
||||||
|
POSIX_FADV_SEQUENTIAL
|
||||||
|
POSIX_FADV_WILLNEED
|
||||||
|
POSIX_FADV_DONTNEED
|
||||||
|
POSIX_FADV_NOREUSE
|
||||||
|
)
|
||||||
|
|
||||||
|
func Fadvise(file *os.File, off, length int64, advice uint32) error {
|
||||||
|
// syscall.SYS_FADVISE64 = 221
|
||||||
|
_, _, err := syscall.Syscall6(221, file.Fd(), uintptr(off), uintptr(length), uintptr(advice), 0, 0)
|
||||||
|
if err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
27
pkg/util/util_darwin.go
Normal file
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