/* 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 scsi import ( "github.com/google/uuid" "github.com/gostor/gotgt/pkg/api" ) 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 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] LURes.Reservations = resArray } 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 ( LURes *SCSILUReservation targetRes SCSILUReservationMap tmpRes *api.SCSIReservation i int = -1 ok bool ) if targetRes, ok = op.targetReservations[tgtName]; !ok { return } if LURes, ok = targetRes[devUUID]; !ok { return } resArray := LURes.Reservations 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] LURes.Reservations = resArray } } 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 }