PR implementation
This commit is contained in:
@@ -18,6 +18,9 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type SCSICommandType byte
|
||||
@@ -158,7 +161,7 @@ type SCSICommand struct {
|
||||
OutSDBBuffer SCSIDataBuffer
|
||||
RelTargetPortID uint16
|
||||
// Command ITN ID
|
||||
CommandITNID uint64
|
||||
ITNexusID uuid.UUID
|
||||
Offset uint64
|
||||
TL uint32
|
||||
SCB *bytes.Buffer
|
||||
@@ -174,12 +177,8 @@ type SCSICommand struct {
|
||||
}
|
||||
|
||||
type ITNexus struct {
|
||||
ID uint64 `json:"id"`
|
||||
Ctime uint64 `json:"ctime"`
|
||||
Commands []SCSICommand `json:"-"`
|
||||
Target *SCSITarget `json:"-"`
|
||||
Host int `json:"host"`
|
||||
Info string `json:"info"`
|
||||
ID uuid.UUID `json:"id"` /*UUIDv1*/
|
||||
Tag string `json:"Tag"` /*For protocal spec identifer*/
|
||||
}
|
||||
|
||||
type ITNexusLuInfo struct {
|
||||
@@ -199,15 +198,18 @@ type TargetPortGroup struct {
|
||||
}
|
||||
|
||||
type SCSITarget struct {
|
||||
Name string `json:"name"`
|
||||
TID int `json:"tid"`
|
||||
LID int `json:"lid"`
|
||||
State SCSITargetState `json:"state"`
|
||||
Devices LUNMap `json:"-"`
|
||||
LUN0 *SCSILu `json:"-"`
|
||||
ITNexus []*ITNexus `json:"itnexus"`
|
||||
Name string `json:"name"`
|
||||
TID int `json:"tid"`
|
||||
LID int `json:"lid"`
|
||||
State SCSITargetState `json:"state"`
|
||||
Devices LUNMap `json:"-"`
|
||||
LUN0 *SCSILu `json:"-"`
|
||||
|
||||
TargetPortGroups []*TargetPortGroup `json:"tpg"`
|
||||
SCSITargetDriver interface{} `json:"-"`
|
||||
|
||||
ITNexusMutex sync.Mutex
|
||||
ITNexus map[uuid.UUID]*ITNexus `json:"itnexus"`
|
||||
}
|
||||
|
||||
type SCSITargetDriverState int
|
||||
@@ -336,6 +338,7 @@ type BackingStore interface {
|
||||
|
||||
type SCSIDeviceProtocol interface {
|
||||
PerformCommand(opcode int) interface{}
|
||||
PerformServiceAction(opcode int, action uint8) interface{}
|
||||
InitLu(lu *SCSILu) error
|
||||
ConfigLu(lu *SCSILu) error
|
||||
OnlineLu(lu *SCSILu) error
|
||||
@@ -348,6 +351,14 @@ type ModePage struct {
|
||||
Data []byte // Rest of mode page info
|
||||
}
|
||||
|
||||
type SCSIReservation struct {
|
||||
ID uuid.UUID //Internal Reservation ID
|
||||
Key uint64
|
||||
ITNexusID uuid.UUID
|
||||
Scope uint8
|
||||
Type uint8
|
||||
}
|
||||
|
||||
type SCSILu struct {
|
||||
Address uint64
|
||||
Size uint64
|
||||
@@ -355,7 +366,7 @@ type SCSILu struct {
|
||||
Path string
|
||||
BsoFlags int
|
||||
BlockShift uint
|
||||
ReserveID uint64
|
||||
ReserveID uuid.UUID
|
||||
Attrs SCSILuPhyAttribute
|
||||
ModePages []ModePage
|
||||
Storage BackingStore
|
||||
|
||||
@@ -54,7 +54,7 @@ type iscsiConnection struct {
|
||||
sessionType int
|
||||
sessionParam []ISCSISessionParam
|
||||
tid int
|
||||
cid uint16
|
||||
CID uint16
|
||||
rxIOState int
|
||||
txIOState int
|
||||
refcount int
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/gostor/gotgt/pkg/port"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type ISCSITargetService struct {
|
||||
@@ -165,7 +166,7 @@ func (s *ISCSITargetService) handler(events byte, conn *iscsiConnection) {
|
||||
s.txHandler(conn)
|
||||
}
|
||||
if conn.state == CONN_STATE_CLOSE {
|
||||
glog.Warningf("iscsi connection[%d] closed", conn.cid)
|
||||
glog.Warningf("iscsi connection[%d] closed", conn.CID)
|
||||
conn.close()
|
||||
}
|
||||
}
|
||||
@@ -324,6 +325,7 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
{"DataSequenceInOrder", "Yes"},
|
||||
}),
|
||||
}
|
||||
conn.CID = cmd.ConnID
|
||||
pairs := util.ParseKVText(cmd.RawData)
|
||||
if initiatorName, ok := pairs["InitiatorName"]; ok {
|
||||
conn.initiator = initiatorName
|
||||
@@ -376,11 +378,14 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
case SESSION_NORMAL:
|
||||
if conn.session == nil {
|
||||
// create a new session
|
||||
sess, err := s.NewISCSISession(conn)
|
||||
sess, err := s.NewISCSISession(conn, cmd.ISID)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(sess)}
|
||||
scsi.AddITNexus(&sess.Target.SCSITarget, itnexus)
|
||||
sess.ITNexusID = itnexus.ID
|
||||
conn.session = sess
|
||||
}
|
||||
case SESSION_DISCOVERY:
|
||||
@@ -804,7 +809,7 @@ func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
|
||||
task.scmd.Direction = api.SCSIDataWrite
|
||||
}
|
||||
}
|
||||
task.scmd.CommandITNID = task.conn.session.Tsih
|
||||
task.scmd.ITNexusID = task.conn.session.ITNexusID
|
||||
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
|
||||
task.scmd.SCBLength = len(cmd.CDB)
|
||||
task.scmd.Lun = cmd.LUN
|
||||
|
||||
@@ -78,8 +78,8 @@ type iSCSITPGT struct {
|
||||
type ISCSITarget struct {
|
||||
api.SCSITarget
|
||||
api.SCSITargetDriverCommon
|
||||
TPGTs map[uint16]*iSCSITPGT
|
||||
Sessions []*ISCSISession
|
||||
TPGTs map[uint16]*iSCSITPGT /* Key is a TPGT number */
|
||||
Sessions map[uint16]*ISCSISession /* Key is an TSIH */
|
||||
SessionParam []ISCSISessionParam
|
||||
Alias string
|
||||
MaxSessions int
|
||||
@@ -87,6 +87,23 @@ type ISCSITarget struct {
|
||||
Rdma int
|
||||
NopInterval int
|
||||
NopCount int
|
||||
TSIHCounter uint16
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 3720 iSCSI SSID = ISID , TPGT
|
||||
* ISID is an 6 bytes number, TPGT is an 2 bytes number
|
||||
* We combime ISID and TPGT together to be SSID
|
||||
*/
|
||||
func MakeSSID(ISID uint64, TPGT uint16) uint64 {
|
||||
SSID := ISID<<16 | uint64(TPGT)
|
||||
return SSID
|
||||
}
|
||||
|
||||
func ParseSSID(SSID uint64) (uint64, uint16) {
|
||||
TPGT := uint16(uint64(0xFFFF) & SSID)
|
||||
ISID := SSID >> 16
|
||||
return ISID, TPGT
|
||||
}
|
||||
|
||||
func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
||||
@@ -103,8 +120,10 @@ func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
||||
|
||||
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||
return &ISCSITarget{
|
||||
SCSITarget: *target,
|
||||
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||
SCSITarget: *target,
|
||||
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||
Sessions: make(map[uint16]*ISCSISession),
|
||||
TSIHCounter: 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -149,8 +151,9 @@ type ISCSISession struct {
|
||||
Initiator string
|
||||
InitiatorAlias string
|
||||
Target *ISCSITarget
|
||||
Isid uint64
|
||||
Tsih uint64
|
||||
ISID uint64
|
||||
TSIH uint64
|
||||
ITNexusID uuid.UUID
|
||||
|
||||
ExpCmdSN uint32
|
||||
// only one connection per session
|
||||
@@ -215,7 +218,7 @@ type iscsiPdu struct {
|
||||
}
|
||||
|
||||
// New creates a new session.
|
||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
|
||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection, isid uint64) (*ISCSISession, error) {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
tsih uint64
|
||||
@@ -235,7 +238,7 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
||||
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
||||
tsih = uint64(rand.Uint32())
|
||||
for _, s := range target.Sessions {
|
||||
if s.Tsih == tsih {
|
||||
if s.TSIH == tsih {
|
||||
tsih = 0
|
||||
break
|
||||
}
|
||||
@@ -246,7 +249,8 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
||||
}
|
||||
|
||||
sess := &ISCSISession{
|
||||
Tsih: tsih,
|
||||
TSIH: tsih,
|
||||
ISID: isid,
|
||||
Initiator: conn.initiator,
|
||||
InitiatorAlias: conn.initiatorAlias,
|
||||
Target: target,
|
||||
@@ -259,3 +263,14 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
||||
conn.session = sess
|
||||
return sess, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* iSCSI I_T nexus identifer = (iSCSI Initiator Name + 'i' + ISID, iSCSI Target Name + 't' + Portal Group Tag)
|
||||
*/
|
||||
func GeniSCSIITNexusID(sess *ISCSISession) string {
|
||||
strID := fmt.Sprintf("%si0x%12x,%st%d",
|
||||
sess.Initiator, sess.ISID,
|
||||
sess.Target.SCSITarget.Name,
|
||||
sess.Connections[0].tpgt)
|
||||
return strID
|
||||
}
|
||||
|
||||
@@ -20,36 +20,33 @@ import (
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
type SCSIPRServiceAction byte
|
||||
type SCSIPRType byte
|
||||
const (
|
||||
/* PERSISTENT_RESERVE_IN service action codes */
|
||||
PR_IN_READ_KEYS byte = 0x00
|
||||
PR_IN_READ_RESERVATION byte = 0x01
|
||||
PR_IN_REPORT_CAPABILITIES byte = 0x02
|
||||
PR_IN_READ_FULL_STATUS byte = 0x03
|
||||
|
||||
var (
|
||||
// PERSISTENT_RESERVE_IN service action codes
|
||||
PR_IN_READ_KEYS SCSIPRServiceAction = 0x00
|
||||
PR_IN_READ_RESERVATION SCSIPRServiceAction = 0x01
|
||||
PR_IN_REPORT_CAPABILITIES SCSIPRServiceAction = 0x02
|
||||
PR_IN_READ_FULL_STATUS SCSIPRServiceAction = 0x03
|
||||
|
||||
// PERSISTENT_RESERVE_OUT service action codes
|
||||
PR_OUT_REGISTER SCSIPRServiceAction = 0x00
|
||||
PR_OUT_RESERVE SCSIPRServiceAction = 0x01
|
||||
PR_OUT_RELEASE SCSIPRServiceAction = 0x02
|
||||
PR_OUT_CLEAR SCSIPRServiceAction = 0x03
|
||||
PR_OUT_PREEMPT SCSIPRServiceAction = 0x04
|
||||
PR_OUT_PREEMPT_AND_ABORT SCSIPRServiceAction = 0x05
|
||||
PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY SCSIPRServiceAction = 0x06
|
||||
PR_OUT_REGISTER_AND_MOVE SCSIPRServiceAction = 0x07
|
||||
/* PERSISTENT_RESERVE_OUT service action codes */
|
||||
PR_OUT_REGISTER byte = 0x00
|
||||
PR_OUT_RESERVE byte = 0x01
|
||||
PR_OUT_RELEASE byte = 0x02
|
||||
PR_OUT_CLEAR byte = 0x03
|
||||
PR_OUT_PREEMPT byte = 0x04
|
||||
PR_OUT_PREEMPT_AND_ABORT byte = 0x05
|
||||
PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY byte = 0x06
|
||||
PR_OUT_REGISTER_AND_MOVE byte = 0x07
|
||||
|
||||
// Persistent Reservation scope
|
||||
PR_LU_SCOPE byte = 0x00
|
||||
|
||||
// Persistent Reservation Type Mask format
|
||||
PR_TYPE_WRITE_EXCLUSIVE SCSIPRType = 0x01
|
||||
PR_TYPE_EXCLUSIVE_ACCESS SCSIPRType = 0x03
|
||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY SCSIPRType = 0x05
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY SCSIPRType = 0x06
|
||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG SCSIPRType = 0x07
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG SCSIPRType = 0x08
|
||||
/* Persistent Reservation Type Mask format */
|
||||
PR_TYPE_WRITE_EXCLUSIVE byte = 0x01
|
||||
PR_TYPE_EXCLUSIVE_ACCESS byte = 0x03
|
||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY byte = 0x05
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY byte = 0x06
|
||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG byte = 0x07
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG byte = 0x08
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -11,8 +11,7 @@ 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.
|
||||
*/
|
||||
limitations under the License. */
|
||||
|
||||
package scsi
|
||||
|
||||
@@ -80,6 +79,10 @@ func NewLUN0() *api.SCSILu {
|
||||
return lu
|
||||
}
|
||||
|
||||
func GetReservation(dev *api.SCSILu, nexusID uint64) *api.SCSIReservation {
|
||||
return nil
|
||||
}
|
||||
|
||||
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
||||
op := int(cmd.SCB.Bytes()[0])
|
||||
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
|
||||
|
||||
@@ -45,6 +45,16 @@ func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
|
||||
return sbc.SCSIDeviceOps[opcode]
|
||||
}
|
||||
|
||||
func (sbc SBCSCSIDeviceProtocol) PerformServiceAction(opcode int, action uint8) interface{} {
|
||||
var sa *SCSIServiceAction
|
||||
for _, sa = range sbc.SCSIDeviceOps[opcode].ServiceAction {
|
||||
if sa.ServiceAction == action {
|
||||
return sa
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
||||
// init LU's phy attribute
|
||||
lu.Attrs.DeviceType = sbc.DeviceType
|
||||
@@ -158,8 +168,23 @@ func NewSBCDevice(deviceType api.SCSIDeviceType) api.SCSIDeviceProtocol {
|
||||
|
||||
sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
|
||||
sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
|
||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
||||
|
||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||
{ServiceAction: PR_IN_READ_KEYS, CommandPerformFunc: SPCPRReadKeys},
|
||||
{ServiceAction: PR_IN_READ_RESERVATION, CommandPerformFunc: SPCPRReadReservation},
|
||||
{ServiceAction: PR_IN_REPORT_CAPABILITIES, CommandPerformFunc: SPCPRReportCapabilities},
|
||||
}, 0)
|
||||
|
||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||
{ServiceAction: PR_OUT_REGISTER, CommandPerformFunc: SPCPRRegister},
|
||||
{ServiceAction: PR_OUT_RESERVE, CommandPerformFunc: SPCPRReserve},
|
||||
{ServiceAction: PR_OUT_RELEASE, CommandPerformFunc: SPCPRRelease},
|
||||
{ServiceAction: PR_OUT_CLEAR, CommandPerformFunc: SPCPRClear},
|
||||
{ServiceAction: PR_OUT_PREEMPT, CommandPerformFunc: SPCPRPreempt},
|
||||
// {ServiceAction: PR_OUT_PREEMPT_AND_ABORT, CommandPerformFunc: SPCPRPreempt},
|
||||
{ServiceAction: PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, CommandPerformFunc: SPCPRRegister},
|
||||
{ServiceAction: PR_OUT_REGISTER_AND_MOVE, CommandPerformFunc: SPCPRRegisterAndMove},
|
||||
}, 0)
|
||||
|
||||
sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||
sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
||||
@@ -173,8 +198,10 @@ func NewSBCDevice(deviceType api.SCSIDeviceType) api.SCSIDeviceProtocol {
|
||||
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SBCServiceAction, nil, 0)
|
||||
|
||||
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||
{ServiceAction: 0x0C, CommandPerformFunc: SPCReportSupportedOperationCodes},
|
||||
}, 0)
|
||||
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
|
||||
sbc.SCSIDeviceOps[api.WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||
@@ -431,7 +458,7 @@ func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
|
||||
func SBCRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
||||
if err := deviceRelease(cmd.Target.TID, cmd.CommandITNID, lun, false); err != nil {
|
||||
if err := deviceRelease(cmd.Target.TID, cmd.ITNexusID, lun, false); err != nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type SCSITargetService struct {
|
||||
@@ -70,7 +71,7 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
||||
}
|
||||
scmd.Target = target
|
||||
for _, it := range target.ITNexus {
|
||||
if it.ID == scmd.CommandITNID {
|
||||
if uuid.Equal(it.ID, scmd.ITNexusID) {
|
||||
itn = it
|
||||
break
|
||||
}
|
||||
@@ -99,13 +100,13 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
||||
}
|
||||
|
||||
type SCSIServiceAction struct {
|
||||
ServiceAction uint32
|
||||
ServiceAction uint8
|
||||
CommandPerformFunc api.CommandFunc
|
||||
}
|
||||
|
||||
type SCSIDeviceOperation struct {
|
||||
CommandPerformFunc api.CommandFunc
|
||||
ServiceAction *SCSIServiceAction
|
||||
ServiceAction []*SCSIServiceAction
|
||||
PRConflictBits uint8
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ type BaseSCSIDeviceProtocol struct {
|
||||
SCSIDeviceOps []SCSIDeviceOperation
|
||||
}
|
||||
|
||||
func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||
func NewSCSIDeviceOperation(fn api.CommandFunc, sa []*SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||
return SCSIDeviceOperation{
|
||||
CommandPerformFunc: fn,
|
||||
ServiceAction: sa,
|
||||
|
||||
324
pkg/scsi/scsi_pr.go
Normal file
324
pkg/scsi/scsi_pr.go
Normal file
@@ -0,0 +1,324 @@
|
||||
package scsi
|
||||
|
||||
import (
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type SCSIReservationOperator interface {
|
||||
GetCurrentReservation(tgtName string, devUUID uint64) *api.SCSIReservation
|
||||
SetCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool
|
||||
IsCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool
|
||||
|
||||
GetPRGeneration(tgtName string, devUUID uint64) (uint32, bool)
|
||||
IncPRGeneration(tgtName string, devUUID uint64) bool
|
||||
|
||||
AddReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool
|
||||
GetReservation(tgtName string, devUUID uint64, ITNexusID uuid.UUID) *api.SCSIReservation
|
||||
GetReservationList(tgtName string, devUUID uint64) []*api.SCSIReservation
|
||||
DeleteAndRemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation)
|
||||
RemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation)
|
||||
RemoveAllReservation(tgtName string, devUUID uint64)
|
||||
|
||||
IsKeyExists(tgtName string, devUUID uint64, key uint64) bool
|
||||
|
||||
Save(tgtName string, devUUID uint64) bool
|
||||
}
|
||||
|
||||
var onePROperator SCSIReservationOperator
|
||||
|
||||
func GetSCSIReservationOperator() SCSIReservationOperator {
|
||||
if onePROperator == nil {
|
||||
onePROperator = &SCSISimpleReservationOperator{
|
||||
targetReservations: make(map[string]SCSILUReservationMap),
|
||||
}
|
||||
}
|
||||
return onePROperator
|
||||
}
|
||||
|
||||
type SCSILUReservation struct {
|
||||
TargetName string
|
||||
DeviceUUID uint64
|
||||
PRGeneration uint32
|
||||
Reservations []*api.SCSIReservation
|
||||
CurrentReservation *api.SCSIReservation
|
||||
}
|
||||
|
||||
type SCSILUReservationMap map[uint64]*SCSILUReservation /* device uuid as the key */
|
||||
|
||||
type SCSISimpleReservationOperator struct {
|
||||
SCSIReservationOperator
|
||||
targetReservations map[string]SCSILUReservationMap /* target name as the key*/
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) InitLUReservation(tgtName string, devUUID uint64) {
|
||||
var (
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
op.targetReservations[tgtName] = make(SCSILUReservationMap)
|
||||
targetRes = op.targetReservations[tgtName]
|
||||
}
|
||||
if _, ok = targetRes[devUUID]; !ok {
|
||||
targetRes[devUUID] = &SCSILUReservation{TargetName: tgtName,
|
||||
DeviceUUID: devUUID,
|
||||
PRGeneration: 0,
|
||||
Reservations: []*api.SCSIReservation{},
|
||||
CurrentReservation: nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) GetReservation(tgtName string, devUUID uint64, ITNexusID uuid.UUID) *api.SCSIReservation {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
SCSIRes *api.SCSIReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return nil
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return nil
|
||||
}
|
||||
for _, SCSIRes = range LURes.Reservations {
|
||||
if uuid.Equal(SCSIRes.ITNexusID, ITNexusID) {
|
||||
return SCSIRes
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) GetPRGeneration(tgtName string, devUUID uint64) (uint32, bool) {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return 0, false
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return LURes.PRGeneration, true
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) IncPRGeneration(tgtName string, devUUID uint64) bool {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return false
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return false
|
||||
}
|
||||
LURes.PRGeneration++
|
||||
return true
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) GetCurrentReservation(tgtName string, devUUID uint64) *api.SCSIReservation {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return nil
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return nil
|
||||
}
|
||||
return LURes.CurrentReservation
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) SetCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return false
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return false
|
||||
}
|
||||
LURes.CurrentReservation = res
|
||||
return true
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) GetReservationList(tgtName string, devUUID uint64) []*api.SCSIReservation {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return nil
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return nil
|
||||
}
|
||||
return LURes.Reservations
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) DeleteAndRemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) {
|
||||
var (
|
||||
i int = -1
|
||||
ok bool
|
||||
tmpRes *api.SCSIReservation
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resArray := LURes.Reservations
|
||||
curRes := LURes.CurrentReservation
|
||||
|
||||
for i, tmpRes = range resArray {
|
||||
if tmpRes == res {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i >= 0 {
|
||||
resArray[i] = resArray[len(resArray)-1]
|
||||
resArray[len(resArray)-1] = nil
|
||||
resArray = resArray[:len(resArray)-1]
|
||||
}
|
||||
|
||||
if curRes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !op.IsCurrentReservation(tgtName, devUUID, res) {
|
||||
return
|
||||
}
|
||||
|
||||
if (curRes.Type != PR_TYPE_WRITE_EXCLUSIVE_ALLREG &&
|
||||
curRes.Type != PR_TYPE_EXCLUSIVE_ACCESS_ALLREG) ||
|
||||
len(resArray) == 0 {
|
||||
curRes.Scope = 0
|
||||
curRes.Type = 0
|
||||
LURes.CurrentReservation = nil
|
||||
for i, tmpRes = range resArray {
|
||||
if tmpRes == res {
|
||||
continue
|
||||
}
|
||||
//TODO send sense code
|
||||
}
|
||||
LURes.PRGeneration++
|
||||
} else {
|
||||
for i, tmpRes = range resArray {
|
||||
if tmpRes != res {
|
||||
//kep scope and type
|
||||
LURes.CurrentReservation = tmpRes
|
||||
tmpRes.Scope = curRes.Scope
|
||||
tmpRes.Type = curRes.Type
|
||||
break
|
||||
}
|
||||
}
|
||||
LURes.PRGeneration++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) RemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) {
|
||||
var (
|
||||
i int = -1
|
||||
tmpRes *api.SCSIReservation
|
||||
)
|
||||
|
||||
resArray := op.GetReservationList(tgtName, devUUID)
|
||||
|
||||
for i, tmpRes = range resArray {
|
||||
if tmpRes == res {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i >= 0 {
|
||||
resArray[i] = resArray[len(resArray)-1]
|
||||
resArray[len(resArray)-1] = nil
|
||||
resArray = resArray[:len(resArray)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) AddReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return false
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return false
|
||||
}
|
||||
LURes.Reservations = append(LURes.Reservations, res)
|
||||
return true
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) IsKeyExists(tgtName string, devUUID uint64, key uint64) bool {
|
||||
|
||||
resList := op.GetReservationList(tgtName, devUUID)
|
||||
for _, tmpRes := range resList {
|
||||
if tmpRes.Key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) IsCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool {
|
||||
curRes := op.GetCurrentReservation(tgtName, devUUID)
|
||||
if curRes == nil {
|
||||
return false
|
||||
}
|
||||
if curRes.Type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG ||
|
||||
curRes.Type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG {
|
||||
return true
|
||||
}
|
||||
|
||||
if curRes == res {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) RemoveAllReservation(tgtName string, devUUID uint64) {
|
||||
var (
|
||||
LURes *SCSILUReservation
|
||||
targetRes SCSILUReservationMap
|
||||
ok bool
|
||||
)
|
||||
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||
return
|
||||
}
|
||||
if LURes, ok = targetRes[devUUID]; !ok {
|
||||
return
|
||||
}
|
||||
LURes.Reservations = []*api.SCSIReservation{}
|
||||
|
||||
}
|
||||
|
||||
func (op *SCSISimpleReservationOperator) Save(tgtName string, devUUID uint64) bool {
|
||||
return true
|
||||
}
|
||||
@@ -65,6 +65,8 @@ func GetTargetLUNMap(tgtName string) api.LUNMap {
|
||||
}
|
||||
|
||||
func InitSCSILUMap(config *config.Config) error {
|
||||
var simpleOp *SCSISimpleReservationOperator
|
||||
var ok bool
|
||||
globalSCSILUMap.mutex.Lock()
|
||||
defer globalSCSILUMap.mutex.Unlock()
|
||||
|
||||
@@ -83,6 +85,11 @@ func InitSCSILUMap(config *config.Config) error {
|
||||
return errors.New("LU Number must be a number")
|
||||
}
|
||||
mappingLUN(deviceID, lun, tgtName)
|
||||
//Init SCSISimpleReservationOperator
|
||||
op := GetSCSIReservationOperator()
|
||||
if simpleOp, ok = op.(*SCSISimpleReservationOperator); ok {
|
||||
simpleOp.InitLUReservation(tgtName, deviceID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
548
pkg/scsi/spc.go
548
pkg/scsi/spc.go
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
@@ -568,9 +569,7 @@ func reportOpcodeOne(cmd *api.SCSICommand, rctd int, opcode byte, rsa uint16, se
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is useful for the various commands using the SERVICE ACTION format.
|
||||
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
// TODO
|
||||
func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
scb := cmd.SCB.Bytes()
|
||||
reporting_options := scb[2] & 0x07
|
||||
opcode := scb[3]
|
||||
@@ -609,15 +608,60 @@ sense:
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
// This is useful for the various commands using the SERVICE ACTION format.
|
||||
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
|
||||
scb := cmd.SCB.Bytes()
|
||||
opcode := int(scb[0])
|
||||
action := uint8(scb[1] & 0x1F)
|
||||
serviceAction := cmd.Device.DeviceProtocol.PerformServiceAction(opcode, action)
|
||||
if serviceAction == nil {
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
} else {
|
||||
fnop := serviceAction.(*SCSIServiceAction)
|
||||
return fnop.CommandPerformFunc(host, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
allocationLength := util.GetUnalignedUint32(cmd.SCB.Bytes()[7:9])
|
||||
var (
|
||||
buf = &bytes.Buffer{}
|
||||
data []byte = []byte{}
|
||||
addBuf = &bytes.Buffer{}
|
||||
allocationLength uint16
|
||||
additionLength uint32
|
||||
)
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
PRGeneration, _ := scsiResOp.GetPRGeneration(tgtName, devUUID)
|
||||
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||
length, _ := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||
|
||||
allocationLength = uint16(length)
|
||||
if allocationLength < 8 {
|
||||
goto sense
|
||||
}
|
||||
if cmd.InSDBBuffer.Length < allocationLength {
|
||||
goto sense
|
||||
|
||||
for _, res := range resList {
|
||||
addBuf.Write(util.MarshalUint64(res.Key))
|
||||
}
|
||||
// TODO
|
||||
additionLength = uint32(len(addBuf.Bytes()))
|
||||
|
||||
buf.Write(util.MarshalUint32(PRGeneration))
|
||||
buf.Write(util.MarshalUint32(additionLength))
|
||||
buf.Write(addBuf.Bytes())
|
||||
data = buf.Bytes()
|
||||
if allocationLength < uint16(additionLength) {
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data[0:allocationLength])
|
||||
} else {
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||
}
|
||||
|
||||
cmd.InSDBBuffer.Resid = int32(additionLength)
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
@@ -625,7 +669,59 @@ sense:
|
||||
}
|
||||
|
||||
func SPCPRReadReservation(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
buf = &bytes.Buffer{}
|
||||
data []byte = []byte{}
|
||||
addBuf = &bytes.Buffer{}
|
||||
allocationLength uint16
|
||||
additionLength uint32
|
||||
)
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
PRGeneration, _ := scsiResOp.GetPRGeneration(tgtName, devUUID)
|
||||
curRes := scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||
|
||||
length, _ := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||
allocationLength = uint16(length)
|
||||
if allocationLength < 8 {
|
||||
goto sense
|
||||
}
|
||||
|
||||
if curRes == nil {
|
||||
additionLength = 0
|
||||
} else {
|
||||
addBuf.Write(util.MarshalUint64(curRes.Key))
|
||||
//Obsolete
|
||||
addBuf.WriteByte(0x00)
|
||||
addBuf.WriteByte(0x00)
|
||||
addBuf.WriteByte(0x00)
|
||||
addBuf.WriteByte(0x00)
|
||||
//Reserved
|
||||
addBuf.WriteByte(0x00)
|
||||
//SCOPE and TYPE
|
||||
scope_type := (curRes.Scope << 4) | curRes.Type
|
||||
addBuf.WriteByte(scope_type)
|
||||
additionLength = uint32(0x10)
|
||||
}
|
||||
|
||||
buf.Write(util.MarshalUint32(PRGeneration))
|
||||
buf.Write(util.MarshalUint32(additionLength))
|
||||
buf.Write(addBuf.Bytes())
|
||||
data = buf.Bytes()
|
||||
if allocationLength < uint16(additionLength) {
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data[0:allocationLength])
|
||||
} else {
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||
}
|
||||
|
||||
cmd.InSDBBuffer.Resid = int32(additionLength)
|
||||
return api.SAMStatGood
|
||||
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCPRReportCapabilities(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
@@ -672,28 +768,466 @@ sense:
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func reservationCheck(host int, cmd *api.SCSICommand) bool {
|
||||
var (
|
||||
paramLen uint32
|
||||
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
)
|
||||
length, _ := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||
paramLen = uint32(length)
|
||||
if paramLen != 24 {
|
||||
return false
|
||||
}
|
||||
spec_i_pt := uint8(buf[20] & 0x08)
|
||||
all_tg_pt := uint8(buf[20] & 0x04)
|
||||
aptpl := uint8(buf[20] & 0x01)
|
||||
/* Currently, We don't support these flags */
|
||||
if (spec_i_pt | all_tg_pt | aptpl) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func SPCPRRegister(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
scb []byte = cmd.SCB.Bytes()
|
||||
ignoreKey bool = false
|
||||
ok bool = false
|
||||
resKey uint64
|
||||
sAResKey uint64
|
||||
)
|
||||
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
res := scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||
|
||||
if scb[1] == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY {
|
||||
ignoreKey = true
|
||||
}
|
||||
ok = reservationCheck(host, cmd)
|
||||
if !ok {
|
||||
goto sense
|
||||
}
|
||||
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||
sAResKey = util.GetUnalignedUint64(buf[8:16])
|
||||
|
||||
if res != nil {
|
||||
if ignoreKey || resKey == res.Key {
|
||||
if sAResKey != 0 {
|
||||
res.Key = sAResKey
|
||||
} else {
|
||||
scsiResOp.DeleteAndRemoveReservation(tgtName, devUUID, res)
|
||||
}
|
||||
} else {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
} else {
|
||||
if ignoreKey || resKey == 0 {
|
||||
if sAResKey != 0 {
|
||||
newRes := &api.SCSIReservation{
|
||||
ID: uuid.NewV1(),
|
||||
Key: sAResKey,
|
||||
ITNexusID: cmd.ITNexusID,
|
||||
}
|
||||
scsiResOp.AddReservation(tgtName, devUUID, newRes)
|
||||
}
|
||||
} else {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
}
|
||||
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||
scsiResOp.Save(tgtName, devUUID)
|
||||
return api.SAMStatGood
|
||||
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCPRReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
scb []byte = cmd.SCB.Bytes()
|
||||
curRes *api.SCSIReservation
|
||||
res *api.SCSIReservation
|
||||
ok bool = false
|
||||
resScope uint8
|
||||
resType uint8
|
||||
)
|
||||
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
|
||||
ok = reservationCheck(host, cmd)
|
||||
if !ok {
|
||||
goto sense
|
||||
}
|
||||
|
||||
resScope = scb[2] & 0xf0 >> 4
|
||||
resType = scb[2] & 0x0f
|
||||
|
||||
switch resType {
|
||||
case PR_TYPE_WRITE_EXCLUSIVE_REGONLY,
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY,
|
||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG,
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
|
||||
break
|
||||
default:
|
||||
goto sense
|
||||
}
|
||||
if resScope != PR_LU_SCOPE {
|
||||
goto sense
|
||||
}
|
||||
|
||||
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||
if res == nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||
if curRes != nil {
|
||||
if !scsiResOp.IsCurrentReservation(tgtName, devUUID, res) {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
if curRes.Type != resType ||
|
||||
curRes.Scope != resScope {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
}
|
||||
res.Scope = resScope
|
||||
res.Type = resType
|
||||
scsiResOp.SetCurrentReservation(tgtName, devUUID, res)
|
||||
scsiResOp.Save(tgtName, devUUID)
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
scb []byte = cmd.SCB.Bytes()
|
||||
curRes *api.SCSIReservation
|
||||
res *api.SCSIReservation
|
||||
resList []*api.SCSIReservation
|
||||
ok bool = false
|
||||
resKey uint64
|
||||
resScope uint8
|
||||
resType uint8
|
||||
)
|
||||
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
|
||||
ok = reservationCheck(host, cmd)
|
||||
if !ok {
|
||||
goto sense
|
||||
}
|
||||
|
||||
resScope = scb[2] & 0xf0 >> 4
|
||||
resType = scb[2] & 0x0f
|
||||
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||
|
||||
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||
if res == nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||
if curRes == nil {
|
||||
return api.SAMStatGood
|
||||
}
|
||||
|
||||
if !scsiResOp.IsCurrentReservation(tgtName, devUUID, res) {
|
||||
return api.SAMStatGood
|
||||
}
|
||||
|
||||
if resKey != res.Key {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
if curRes.Scope != resScope || curRes.Type != resType {
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
scsiResOp.SetCurrentReservation(tgtName, devUUID, nil)
|
||||
res.Scope = 0
|
||||
res.Type = 0
|
||||
|
||||
switch resType {
|
||||
case PR_TYPE_WRITE_EXCLUSIVE,
|
||||
PR_TYPE_EXCLUSIVE_ACCESS,
|
||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY,
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY,
|
||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG,
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
|
||||
break
|
||||
default:
|
||||
goto sense
|
||||
}
|
||||
|
||||
resList = scsiResOp.GetReservationList(tgtName, devUUID)
|
||||
for _, tmpRes := range resList {
|
||||
if tmpRes.ID == res.ID {
|
||||
continue
|
||||
}
|
||||
//TODO send sense code
|
||||
}
|
||||
scsiResOp.Save(tgtName, devUUID)
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCPRClear(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
curRes *api.SCSIReservation
|
||||
res *api.SCSIReservation
|
||||
ok bool = false
|
||||
resKey uint64
|
||||
)
|
||||
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||
|
||||
ok = reservationCheck(host, cmd)
|
||||
if !ok {
|
||||
goto sense
|
||||
}
|
||||
|
||||
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||
|
||||
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||
|
||||
if res == nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
if res.Key != resKey {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||
|
||||
if curRes != nil {
|
||||
curRes.Scope = 0
|
||||
curRes.Type = 0
|
||||
scsiResOp.SetCurrentReservation(tgtName, devUUID, nil)
|
||||
}
|
||||
|
||||
for _, tmpRes := range resList {
|
||||
if tmpRes != res {
|
||||
//TODO send sense code
|
||||
}
|
||||
scsiResOp.DeleteAndRemoveReservation(tgtName, devUUID, tmpRes)
|
||||
}
|
||||
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||
scsiResOp.Save(tgtName, devUUID)
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCPRPreempt(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
scb []byte = cmd.SCB.Bytes()
|
||||
ok bool = false
|
||||
resKey uint64
|
||||
sAResKey uint64
|
||||
res *api.SCSIReservation
|
||||
curRes *api.SCSIReservation
|
||||
resReleased bool
|
||||
removeAllRes bool
|
||||
resScope uint8
|
||||
resType uint8
|
||||
)
|
||||
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||
|
||||
ok = reservationCheck(host, cmd)
|
||||
if !ok {
|
||||
goto sense
|
||||
}
|
||||
resScope = scb[2] & 0xf0 >> 4
|
||||
resType = scb[2] & 0x0f
|
||||
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||
sAResKey = util.GetUnalignedUint64(buf[8:16])
|
||||
|
||||
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||
|
||||
if res == nil {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
if res.Key != resKey {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
if sAResKey != 0 {
|
||||
ok = scsiResOp.IsKeyExists(tgtName, devUUID, sAResKey)
|
||||
if ok {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
}
|
||||
|
||||
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||
if curRes != nil {
|
||||
if curRes.Type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG ||
|
||||
curRes.Type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG {
|
||||
if sAResKey == 0 {
|
||||
if resType != curRes.Type ||
|
||||
resScope != curRes.Scope {
|
||||
resReleased = true
|
||||
}
|
||||
res.Type = resType
|
||||
res.Scope = resScope
|
||||
scsiResOp.SetCurrentReservation(tgtName, devUUID, res)
|
||||
removeAllRes = true
|
||||
}
|
||||
} else {
|
||||
if curRes.Key == resKey {
|
||||
if resType != curRes.Type ||
|
||||
resScope != curRes.Scope {
|
||||
resReleased = true
|
||||
}
|
||||
res.Type = resType
|
||||
res.Scope = resScope
|
||||
scsiResOp.SetCurrentReservation(tgtName, devUUID, res)
|
||||
} else {
|
||||
if sAResKey == 0 {
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, tmpRes := range resList {
|
||||
if tmpRes == res {
|
||||
continue
|
||||
}
|
||||
|
||||
if res.Key == resKey || removeAllRes {
|
||||
//TODO send sense code
|
||||
scsiResOp.RemoveReservation(tgtName, devUUID, res)
|
||||
} else {
|
||||
if resReleased {
|
||||
//TODO send sense code
|
||||
}
|
||||
}
|
||||
}
|
||||
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||
scsiResOp.Save(tgtName, devUUID)
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
func SPCPRRegisterAndMove(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
scb []byte = cmd.SCB.Bytes()
|
||||
resKey uint64
|
||||
sAResKey uint64
|
||||
curRes, dstReg, res *api.SCSIReservation
|
||||
paramListLen uint32
|
||||
unreg uint8
|
||||
aptpl uint8
|
||||
tpidDataLen uint32
|
||||
//idLen uint32
|
||||
)
|
||||
|
||||
tgtName := cmd.Target.Name
|
||||
devUUID := cmd.Device.UUID
|
||||
scsiResOp := GetSCSIReservationOperator()
|
||||
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||
|
||||
paramListLen = util.GetUnalignedUint32(scb[5:9])
|
||||
if paramListLen < 24 {
|
||||
goto sense
|
||||
}
|
||||
aptpl = buf[17] & 0x01
|
||||
if aptpl != 0 { /* no reported in capabilities */
|
||||
goto sense
|
||||
}
|
||||
|
||||
unreg = buf[17] & 0x02
|
||||
|
||||
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||
sAResKey = util.GetUnalignedUint64(buf[8:16])
|
||||
|
||||
tpidDataLen = util.GetUnalignedUint32(buf[20:25])
|
||||
if tpidDataLen < 24 || (tpidDataLen%4) != 0 {
|
||||
goto sense
|
||||
}
|
||||
|
||||
if (paramListLen - 24) < tpidDataLen {
|
||||
goto sense
|
||||
}
|
||||
|
||||
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||
if res == nil {
|
||||
if curRes != nil {
|
||||
return api.SAMStatReservationConflict
|
||||
} else {
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
|
||||
if scsiResOp.IsCurrentReservation(tgtName, devUUID, res) {
|
||||
return api.SAMStatGood
|
||||
}
|
||||
|
||||
if res.Key != resKey {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
if sAResKey == 0 {
|
||||
return api.SAMStatReservationConflict
|
||||
}
|
||||
|
||||
for _, dstReg = range resList {
|
||||
if dstReg.Key == sAResKey {
|
||||
goto found
|
||||
}
|
||||
}
|
||||
|
||||
goto sense
|
||||
found:
|
||||
//TODO check transportid
|
||||
scsiResOp.SetCurrentReservation(tgtName, devUUID, dstReg)
|
||||
if unreg != 0 {
|
||||
scsiResOp.RemoveReservation(tgtName, devUUID, res)
|
||||
}
|
||||
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||
scsiResOp.Save(tgtName, devUUID)
|
||||
return api.SAMStatGood
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
return api.SAMStatCheckCondition
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
|
||||
@@ -34,6 +35,7 @@ func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*ap
|
||||
Name: name,
|
||||
TID: tid,
|
||||
TargetPortGroups: []*api.TargetPortGroup{},
|
||||
ITNexus: make(map[uuid.UUID]*api.ITNexus),
|
||||
}
|
||||
tpg := &api.TargetPortGroup{0, []*api.SCSITargetPort{}}
|
||||
s.Targets = append(s.Targets, target)
|
||||
@@ -65,6 +67,25 @@ func FindTargetPort(target *api.SCSITarget, relPortID uint16) *api.SCSITargetPor
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddITNexus(target *api.SCSITarget, itnexus *api.ITNexus) bool {
|
||||
var ret bool = true
|
||||
target.ITNexusMutex.Lock()
|
||||
defer target.ITNexusMutex.Unlock()
|
||||
if _, ok := target.ITNexus[itnexus.ID]; !ok {
|
||||
target.ITNexus[itnexus.ID] = itnexus
|
||||
ret = true
|
||||
} else {
|
||||
ret = false
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func RemoveITNexus(target *api.SCSITarget, itnexus *api.ITNexus) {
|
||||
target.ITNexusMutex.Lock()
|
||||
defer target.ITNexusMutex.Unlock()
|
||||
delete(target.ITNexus, itnexus.ID)
|
||||
}
|
||||
|
||||
func deviceReserve(cmd *api.SCSICommand) error {
|
||||
var lu *api.SCSILu
|
||||
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
||||
@@ -80,15 +101,15 @@ func deviceReserve(cmd *api.SCSICommand) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if lu.ReserveID != 0 && lu.ReserveID != cmd.CommandITNID {
|
||||
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.CommandITNID)
|
||||
if !uuid.Equal(lu.ReserveID, uuid.Nil) && uuid.Equal(lu.ReserveID, cmd.ITNexusID) {
|
||||
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.ITNexusID)
|
||||
return fmt.Errorf("already reserved")
|
||||
}
|
||||
lu.ReserveID = cmd.CommandITNID
|
||||
lu.ReserveID = cmd.ITNexusID
|
||||
return nil
|
||||
}
|
||||
|
||||
func deviceRelease(tid int, itn, lun uint64, force bool) error {
|
||||
func deviceRelease(tid int, itn uuid.UUID, lun uint64, force bool) error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user