From c9b93c75271b470ccb22d4eed0cd8d4add03598a Mon Sep 17 00:00:00 2001 From: Le Zhang Date: Tue, 4 Oct 2016 13:39:55 +0800 Subject: [PATCH] mapping lun and fix portal management --- .travis.yml | 17 ++++--- citd.go | 16 ++++-- pkg/api/types.go | 29 ++++++----- pkg/config/config.go | 75 ++++++++++++++++++++++++--- pkg/port/iscsit/iscsid.go | 55 ++++++++++---------- pkg/port/iscsit/iscsit.go | 3 +- pkg/port/service.go | 2 +- pkg/scsi/backingstore.go | 7 ++- pkg/scsi/backingstore/common.go | 42 ++++++++++------ pkg/scsi/backingstore/null.go | 10 ++-- pkg/scsi/lun.go | 34 ++++++++----- pkg/scsi/sbc.go | 9 ++-- pkg/scsi/scsi.go | 4 ++ pkg/scsi/scsilumap.go | 89 +++++++++++++++++++++++++++++++++ pkg/scsi/spc.go | 4 +- pkg/scsi/target.go | 21 ++------ 16 files changed, 298 insertions(+), 119 deletions(-) create mode 100644 pkg/scsi/scsilumap.go diff --git a/.travis.yml b/.travis.yml index 203c682..3a42b39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ sudo: required dist: trusty +env: + - TARGET=iqn.2016-09.com.gotgt.gostor:example_tgt_0 language: go go: - - 1.4 - - 1.5 + - 1.6 install: - true @@ -23,7 +24,7 @@ script: - hack/verify-gofmt.sh - dd if=/dev/zero of=/var/tmp/disk.img bs=1024 count=10240 - mkdir ${HOME}/.gotgt - - echo '{"targets":{"test-iscsi-target":{"name":"test-iscsi-target","luns":["/var/tmp/disk.img"]}}}' > ${HOME}/.gotgt/config.json + - echo '{"storages":[{"deviceID":1000,"path":"file:/var/tmp/disk.img","online":true}],"targets":{"iqn.2016-09.com.gotgt.gostor:example_tgt_0":{"portals":["127.0.0.1"],"luns":{"0":1000}}}}' > ${HOME}/.gotgt/config.json - ./citd -v 4 1>/dev/null 2>&1 & # libiscsi test - mkdir ${HOME}/libiscsi @@ -33,11 +34,11 @@ script: - ./autogen.sh - ./configure - make - - ./test-tool/iscsi-test-cu -d --test=SCSI.Read10.Simple iscsi://127.0.0.1:3260/test-iscsi-target/0 - - ./test-tool/iscsi-test-cu -d --test=SCSI.Write10.Simple iscsi://127.0.0.1:3260/test-iscsi-target/0 - - ./utils/iscsi-ls -s iscsi://127.0.0.1:3260/test-iscsi-target - - ./utils/iscsi-inq iscsi://127.0.0.1:3260/test-iscsi-target/0 - - ./utils/iscsi-readcapacity16 iscsi://127.0.0.1:3260/test-iscsi-target/0 + - ./test-tool/iscsi-test-cu -d --test=SCSI.Read10.Simple iscsi://127.0.0.1:3260/${TARGET}/0 + - ./test-tool/iscsi-test-cu -d --test=SCSI.Write10.Simple iscsi://127.0.0.1:3260/${TARGET}/0 + - ./utils/iscsi-ls -s iscsi://127.0.0.1:3260/${TARGET} + - ./utils/iscsi-inq iscsi://127.0.0.1:3260/${TARGET}/0 + - ./utils/iscsi-readcapacity16 iscsi://127.0.0.1:3260/${TARGET}/0 # iscsi initiator test - sudo iscsiadm -m discovery -t sendtargets -p 127.0.0.1 - sudo iscsiadm -m node -L all diff --git a/citd.go b/citd.go index 38e5b7e..e0ea52e 100644 --- a/citd.go +++ b/citd.go @@ -64,18 +64,26 @@ Help Options: os.Exit(1) } - scsi := scsi.NewSCSITargetService() - t, err := port.NewTargetService(*flDriver, scsi) + err = scsi.InitSCSILUMap(config) + if err != nil { + glog.Error(err) + os.Exit(1) + } + + service := scsi.NewSCSITargetService() + t, err := port.NewTargetService(*flDriver, service) if err != nil { glog.Error(err) os.Exit(1) } iscsit := reflect.ValueOf(t) // create a new target - for _, tgt := range config.Targets { + for tgtname, tgt := range config.Targets { create := iscsit.MethodByName("NewTarget") - create.Call([]reflect.Value{reflect.ValueOf(tgt.Name), reflect.ValueOf(tgt.LUNs)}) + create.Call([]reflect.Value{reflect.ValueOf(tgtname), + reflect.ValueOf(tgt.Portals)}) } + runtime.GOMAXPROCS(runtime.NumCPU()) // run a service run := iscsit.MethodByName("Run") diff --git a/pkg/api/types.go b/pkg/api/types.go index 6be2c1b..2e5338c 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -18,7 +18,6 @@ package api import ( "bytes" "errors" - "os" ) type SCSICommandType byte @@ -193,7 +192,7 @@ type SCSITarget struct { TID int `json:"tid"` LID int `json:"lid"` State SCSITargetState `json:"state"` - Devices []*SCSILu `json:"-"` + Devices LUNMap `json:"-"` ITNexus []*ITNexus `json:"itnexus"` SCSITargetDriver interface{} `json:"-"` @@ -314,10 +313,11 @@ var ( type CommandFunc func(host int, cmd *SCSICommand) SAMStat type BackingStore interface { - Open(dev *SCSILu, path string) (*os.File, error) + Open(dev *SCSILu, path string) error Close(dev *SCSILu) error Init(dev *SCSILu, Opts string) error Exit(dev *SCSILu) error + Size(dev *SCSILu) uint64 CommandSubmit(cmd *SCSICommand) error } @@ -336,21 +336,20 @@ type ModePage struct { } type SCSILu struct { - File *os.File - Address uint64 - Size uint64 - Lun uint64 - Path string - BsoFlags int - BlockShift uint - ReserveID uint64 - Attrs SCSILuPhyAttribute - ModePages []ModePage - - Target *SCSITarget + Address uint64 + Size uint64 + Lun uint64 + Path string + BsoFlags int + BlockShift uint + ReserveID uint64 + Attrs SCSILuPhyAttribute + ModePages []ModePage Storage BackingStore DeviceProtocol SCSIDeviceProtocol PerformCommand CommandFunc FinishCommand func(*SCSITarget, *SCSICommand) } + +type LUNMap map[uint64]*SCSILu diff --git a/pkg/config/config.go b/pkg/config/config.go index 20bccf2..580ec37 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,6 +25,58 @@ import ( "github.com/gostor/gotgt/pkg/homedir" ) +/* +Format of configuration file + +{ + "storages": [ + { + "deviceID": integer, uniqu device id, + "path": string, :", + "online": bool, online/offline + }, + ], + "targets": { + : { + "portals": [ + + ], + "luns": { + : + } + } + } +} + +Example of the configuration file + +{ + "storages": [ + { + "deviceID": 1000, + "path": "file:/tmp/image", + "online": true + }, + { + "deviceID": 2000, + "path": "ceph:/rbd/image", + "online": true + } + ], + "targets": { + "iqn.2016-09.com.gotgt.gostor:example_tgt_0": { + "portals": [ + "192.168.1.1" + ], + "luns": { + "1": 1000 + "2": 2000 + } + } + } +} +*/ + const ( // ConfigFileName is the name of config file ConfigFileName = "config.json" @@ -32,17 +84,23 @@ const ( var ( configDir = os.Getenv("GOSTOR_CONFIG") + config *Config ) +type BackendStorage struct { + DeviceID uint64 `json:"deviceID"` + Path string `json:"path"` + Online bool `json:"online"` +} + type Target struct { - Name string `json:"name"` - Portals []string `json:"portals"` - LUNs []string `json:"luns"` + Portals []string `json:"portals"` + LUNs map[string]uint64 `json:"luns"` } type Config struct { - Storage string `json:"storage"` - Targets map[string]Target `json:"targets"` + Storages []BackendStorage `json:"storages"` + Targets map[string]Target `json:"targets"` } func init() { @@ -54,6 +112,10 @@ func init() { // ConfigDir returns the directory the configuration file is stored in func ConfigDir() string { return configDir + +} +func GetConfig() *Config { + return config } // Load reads the configuration files in the given directory and return values. @@ -63,8 +125,7 @@ func Load(configDir string) (*Config, error) { } filename := filepath.Join(configDir, ConfigFileName) - config := &Config{ - Storage: "file", + config = &Config{ Targets: make(map[string]Target), } diff --git a/pkg/port/iscsit/iscsid.go b/pkg/port/iscsit/iscsid.go index 5116f74..773c5d0 100644 --- a/pkg/port/iscsit/iscsid.go +++ b/pkg/port/iscsit/iscsid.go @@ -22,7 +22,6 @@ import ( "net" "os" "runtime/debug" - "strings" "github.com/golang/glog" "github.com/gostor/gotgt/pkg/api" @@ -35,7 +34,6 @@ type ISCSITargetService struct { SCSI *scsi.SCSITargetService Name string Targets map[string]*ISCSITarget - Portals map[string]struct{} } func init() { @@ -46,40 +44,44 @@ func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService return &ISCSITargetService{ Name: "iscsi", Targets: map[string]*ISCSITarget{}, - Portals: map[string]struct{}{}, SCSI: base, }, nil } -func (s *ISCSITargetService) NewTarget(target string, luns []string) (port.SCSITargetDriver, error) { +func (s *ISCSITargetService) NewTarget(target string, portals []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, luns) + stgt, err := s.SCSI.NewSCSITarget(len(s.Targets), "iscsi", target) if err != nil { return nil, err } tgt := newISCSITarget(stgt) s.Targets[target] = tgt - + for _, portal := range portals { + s.AddNewPortal(target, portal) + } return tgt, nil } -func (s *ISCSITargetService) AddNewPortal(portals []string) error { - for _, p := range portals { - if !strings.Contains(p, ":") { - p = p + ":3260" - } - s.Portals[p] = struct{}{} +func (s *ISCSITargetService) AddNewPortal(tgtName string, portal string) error { + target := s.Targets[tgtName] + tgtPortals := target.Portals + _, ok := tgtPortals[portal] + if !ok { + tgtPortals[portal] = struct{}{} } return nil } -func (s *ISCSITargetService) HasPortal(portal string) bool { - if len(s.Portals) == 0 { +func (s *ISCSITargetService) HasPortal(tgtName string, portal string) bool { + target := s.Targets[tgtName] + tgtPortals := target.Portals + + if len(tgtPortals) == 0 { return true } - _, ok := s.Portals[portal] + _, ok := tgtPortals[portal] return ok } @@ -98,10 +100,7 @@ func (s *ISCSITargetService) Run() error { glog.Error(err) continue } - if !s.HasPortal(conn.LocalAddr().String()) { - glog.Errorf("unexpected portal") - continue - } + glog.Info(conn.LocalAddr().String()) glog.Info("Accepting ...") iscsiConn := &iscsiConnection{conn: conn} iscsiConn.init() @@ -363,14 +362,18 @@ func (s *ISCSITargetService) iscsiExecText(conn *iscsiConnection) error { 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 name, tgt := range s.Targets { + glog.V(2).Infof("iscsi target:", name) + glog.V(2).Infof("iscsi target portals:", tgt.Portals) + } - for _, t := range list { - result = append(result, util.KeyValue{"TargetName", t.Name}) - //result = append(result, util.KeyValue{"TargetAddress", "172.16.69.1:3260,1"}) - result = append(result, util.KeyValue{"TargetAddress", "127.0.0.1:3260,1"}) + + for name, tgt := range s.Targets { + result = append(result, util.KeyValue{"TargetName", name}) + for portal := range tgt.Portals { + result = append(result, util.KeyValue{"TargetAddress", portal + ",1"}) + } } } } diff --git a/pkg/port/iscsit/iscsit.go b/pkg/port/iscsit/iscsit.go index 9698710..a862812 100644 --- a/pkg/port/iscsit/iscsit.go +++ b/pkg/port/iscsit/iscsit.go @@ -67,7 +67,7 @@ type ISCSIRedirectInfo struct { type ISCSITarget struct { api.SCSITarget api.SCSITargetDriverCommon - + Portals map[string]struct{} Sessions []*ISCSISession SessionParam []ISCSISessionParam Alias string @@ -81,6 +81,7 @@ type ISCSITarget struct { func newISCSITarget(target *api.SCSITarget) *ISCSITarget { return &ISCSITarget{ SCSITarget: *target, + Portals: make(map[string]struct{}), } } diff --git a/pkg/port/service.go b/pkg/port/service.go index 5fb556e..93a8a43 100644 --- a/pkg/port/service.go +++ b/pkg/port/service.go @@ -25,7 +25,7 @@ import ( type SCSITargetService interface { Run() error NewTarget(string, []string) (SCSITargetDriver, error) - AddNewPortal([]string) error + AddNewPortal(string, string) error } type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error) diff --git a/pkg/scsi/backingstore.go b/pkg/scsi/backingstore.go index a06ab68..bdc0f11 100644 --- a/pkg/scsi/backingstore.go +++ b/pkg/scsi/backingstore.go @@ -18,14 +18,13 @@ package scsi import ( "fmt" - "os" "github.com/gostor/gotgt/pkg/api" ) type BaseBackingStore struct { Name string - DataSize int + DataSize uint64 OflagsSupported int } @@ -52,8 +51,8 @@ type fakeBackingStore struct { BaseBackingStore } -func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) { - return nil, nil +func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) error { + return nil } func (fake *fakeBackingStore) Close(dev *api.SCSILu) error { diff --git a/pkg/scsi/backingstore/common.go b/pkg/scsi/backingstore/common.go index dcd4ff4..9cacad1 100644 --- a/pkg/scsi/backingstore/common.go +++ b/pkg/scsi/backingstore/common.go @@ -34,6 +34,7 @@ func init() { type FileBackingStore struct { scsi.BaseBackingStore + File *os.File } func new() (api.BackingStore, error) { @@ -46,16 +47,22 @@ func new() (api.BackingStore, error) { }, nil } -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 +func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) error { + + if finfo, err := os.Stat(path); err != nil { + return err + } else { + bs.DataSize = uint64(finfo.Size()) } - return f, nil + + f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm) + + bs.File = f + return err } func (bs *FileBackingStore) Close(dev *api.SCSILu) error { - return dev.File.Close() + return bs.File.Close() } func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error { @@ -66,6 +73,9 @@ func (bs *FileBackingStore) Exit(dev *api.SCSILu) error { return nil } +func (bs *FileBackingStore) Size(dev *api.SCSILu) uint64 { + return bs.DataSize +} func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { var ( scb = cmd.SCB.Bytes() @@ -83,7 +93,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { switch opcode { case api.ORWRITE_16: tmpbuf := []byte{} - length, err = lu.File.ReadAt(tmpbuf, int64(offset)) + length, err = bs.File.ReadAt(tmpbuf, int64(offset)) if length != len(tmpbuf) { key = scsi.MEDIUM_ERROR asc = scsi.ASC_READ_ERROR @@ -99,7 +109,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { doWrite = true goto write case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16: - if err = util.Fdatasync(lu.File); err != nil { + if err = util.Fdatasync(bs.File); err != nil { panic(err) } break @@ -113,7 +123,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { // TODO break case api.READ_6, api.READ_10, api.READ_12, api.READ_16: - length, err = lu.File.ReadAt(rbuf, int64(offset)) + length, err = bs.File.ReadAt(rbuf, int64(offset)) if err != nil && err != io.EOF { key = scsi.MEDIUM_ERROR asc = scsi.ASC_READ_ERROR @@ -124,11 +134,11 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { } if (opcode != api.READ_6) && (scb[1]&0x10 != 0) { - util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE) + util.Fadvise(bs.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) + err = util.Fadvise(bs.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED) if err != nil { key = scsi.MEDIUM_ERROR asc = scsi.ASC_READ_ERROR @@ -144,7 +154,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { write: if doWrite { // hack: wbuf = []byte("hello world!") - length, err = lu.File.WriteAt(wbuf, int64(offset)) + length, err = bs.File.WriteAt(wbuf, int64(offset)) if err != nil || length != len(wbuf) { glog.Error(err) key = scsi.MEDIUM_ERROR @@ -165,7 +175,7 @@ write: goto sense } if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) { - if err = util.Fdatasync(lu.File); err != nil { + if err = util.Fdatasync(bs.File); err != nil { key = scsi.MEDIUM_ERROR asc = scsi.ASC_READ_ERROR goto sense @@ -173,12 +183,12 @@ write: } if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) { - util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE) + util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE) } } verify: if doVerify { - length, err = lu.File.ReadAt(rbuf, int64(offset)) + length, err = bs.File.ReadAt(rbuf, int64(offset)) if length != len(rbuf) { key = scsi.MEDIUM_ERROR asc = scsi.ASC_READ_ERROR @@ -191,7 +201,7 @@ verify: goto sense } if scb[1]&0x10 != 0 { - util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED) + util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED) } } glog.Infof("io done %s", string(scb)) diff --git a/pkg/scsi/backingstore/null.go b/pkg/scsi/backingstore/null.go index ae5ad8b..57bb1ff 100644 --- a/pkg/scsi/backingstore/null.go +++ b/pkg/scsi/backingstore/null.go @@ -17,8 +17,6 @@ limitations under the License. package backingstore import ( - "os" - "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/scsi" ) @@ -41,8 +39,8 @@ func newNull() (api.BackingStore, error) { }, nil } -func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) { - return nil, nil +func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) error { + return nil } func (bs *NullBackingStore) Close(dev *api.SCSILu) error { @@ -57,6 +55,10 @@ func (bs *NullBackingStore) Exit(dev *api.SCSILu) error { return nil } +func (bs *NullBackingStore) Size(dev *api.SCSILu) uint64 { + return 0 +} + func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error { cmd.Result = api.SAM_STAT_GOOD return nil diff --git a/pkg/scsi/lun.go b/pkg/scsi/lun.go index 44af86e..bd71f4d 100644 --- a/pkg/scsi/lun.go +++ b/pkg/scsi/lun.go @@ -17,36 +17,44 @@ limitations under the License. package scsi import ( - "os" + "errors" + "strings" "github.com/gostor/gotgt/pkg/api" ) -func NewSCSILu(lun uint64, target *api.SCSITarget, file string) (*api.SCSILu, error) { +/* + * path format :/absolute/file/path + */ + +func NewSCSILu(device_uuid uint64, path string) (*api.SCSILu, error) { + + pathinfo := strings.SplitN(path, ":", 2) + if len(pathinfo) < 2 { + return nil, errors.New("invalid device path string") + } + backendType := pathinfo[0] + backendPath := pathinfo[1] + sbc := NewSBCDevice() - backing, err := NewBackingStore("file") + backing, err := NewBackingStore(backendType) if err != nil { return nil, err } + var lu = &api.SCSILu{ - Lun: lun, - Target: target, + Lun: 0, PerformCommand: luPerformCommand, DeviceProtocol: sbc, Storage: backing, BlockShift: api.DefaultBlockShift, } - // hack this - if finfo, err := os.Stat(file); err != nil { - return nil, err - } else { - lu.Size = uint64(finfo.Size()) - } - f, err := backing.Open(lu, file) + + err = backing.Open(lu, backendPath) if err != nil { return nil, err } - lu.File = f + lu.Size = backing.Size(lu) lu.DeviceProtocol.InitLu(lu) lu.Attrs.Online = true lu.Attrs.Lbppbe = 3 diff --git a/pkg/scsi/sbc.go b/pkg/scsi/sbc.go index 43f9645..2c10e1d 100644 --- a/pkg/scsi/sbc.go +++ b/pkg/scsi/sbc.go @@ -45,7 +45,6 @@ func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} { } func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error { - var tgt = lu.Target // init LU's phy attribute lu.Attrs.DeviceType = api.TYPE_DISK lu.Attrs.Qualifier = false @@ -55,8 +54,12 @@ func (sbc SBCSCSIDeviceProtocol) InitLu(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.SCSIID = fmt.Sprintf("GOSTOR %x%d", tgt.TID, lu.Lun) + lu.Attrs.SCSISN = fmt.Sprintf("beaf%d%d", tgt.TID, lu.Lun) + */ + lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR%d", lu.Lun) + lu.Attrs.SCSISN = fmt.Sprintf("beaf%d", lu.Lun) lu.Attrs.ProductID = "VIRTUAL-DISK" lu.Attrs.VersionDesction = []uint16{ 0x04C0, // SBC-3 no version claimed diff --git a/pkg/scsi/scsi.go b/pkg/scsi/scsi.go index 27f1e79..8825119 100644 --- a/pkg/scsi/scsi.go +++ b/pkg/scsi/scsi.go @@ -75,7 +75,11 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro } scmd.ITNexus = itn + /* + * TODO: scmd.Device = target.Devices[util.GetUnalignedUint64(scmd.Lun[:])] + */ scmd.Device = target.Devices[0] + glog.V(2).Infof("scsi opcode: 0x%x, LUN: %d:", int(scmd.SCB.Bytes()[0]), binary.LittleEndian.Uint64(scmd.Lun[:])) result := scmd.Device.PerformCommand(tid, scmd) scmd.Result = result.Stat if result.Err != nil { diff --git a/pkg/scsi/scsilumap.go b/pkg/scsi/scsilumap.go new file mode 100644 index 0000000..dba465d --- /dev/null +++ b/pkg/scsi/scsilumap.go @@ -0,0 +1,89 @@ +/* +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 scsi + +import ( + "errors" + "strconv" + "sync" + + "github.com/gostor/gotgt/pkg/api" + "github.com/gostor/gotgt/pkg/config" +) + +type BackendType string + +type SCSILUMap struct { + mutex sync.RWMutex + AllDevices api.LUNMap /* use UUID as the key for all LUs*/ + TargetsLUNMap map[string]api.LUNMap /* use target name as the key for target's LUN map*/ +} + +var globalSCSILUMap = SCSILUMap{AllDevices: make(api.LUNMap), TargetsLUNMap: make(map[string]api.LUNMap)} + +func mappingLUN(deviceID uint64, lun uint64, target string) { + + device := globalSCSILUMap.AllDevices[deviceID] + lunMap := globalSCSILUMap.TargetsLUNMap[target] + if lunMap == nil { + globalSCSILUMap.TargetsLUNMap[target] = make(api.LUNMap) + lunMap = globalSCSILUMap.TargetsLUNMap[target] + } + lunMap[lun] = device +} + +func GetLU(tgtName string, LUN uint64) *api.SCSILu { + globalSCSILUMap.mutex.RLock() + defer globalSCSILUMap.mutex.RUnlock() + + lunMap := globalSCSILUMap.TargetsLUNMap[tgtName] + lun := lunMap[LUN] + + return lun +} + +func GetTargetLUNMap(tgtName string) api.LUNMap { + globalSCSILUMap.mutex.RLock() + defer globalSCSILUMap.mutex.RUnlock() + + lunMap := globalSCSILUMap.TargetsLUNMap[tgtName] + return lunMap +} + +func InitSCSILUMap(config *config.Config) error { + globalSCSILUMap.mutex.Lock() + defer globalSCSILUMap.mutex.Unlock() + + for _, bs := range config.Storages { + lu, err := NewSCSILu(bs.DeviceID, bs.Path) + if err != nil { + return errors.New("Init SCSI LU map error.") + } + globalSCSILUMap.AllDevices[bs.DeviceID] = lu + } + + for tgtName, tgt := range config.Targets { + for lunstr, deviceID := range tgt.LUNs { + lun, err := strconv.ParseUint(lunstr, 10, 64) + if err != nil { + return errors.New("LU Number must be a number") + } + mappingLUN(deviceID, lun, tgtName) + } + } + return nil +} diff --git a/pkg/scsi/spc.go b/pkg/scsi/spc.go index 65cbc0d..b4ffa69 100644 --- a/pkg/scsi/spc.go +++ b/pkg/scsi/spc.go @@ -146,6 +146,7 @@ func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat { b = (uint8(0) & 0x7) << 5 b |= uint8(0) & 0x1f } + fmt.Println(cmd.Device.Lun, *(*uint64)(unsafe.Pointer(&cmd.Lun))) if cmd.Device.Lun != *(*uint64)(unsafe.Pointer(&cmd.Lun)) { goto sense } @@ -235,7 +236,8 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat { buf.WriteByte(0x00) } - for _, lu := range cmd.Target.Devices { + for lunumber, lu := range cmd.Target.Devices { + fmt.Println("LUN:", lunumber) if remainLength > 0 { lun := lu.Lun if lun > 0xff { diff --git a/pkg/scsi/target.go b/pkg/scsi/target.go index e6c6a2b..fc0271b 100644 --- a/pkg/scsi/target.go +++ b/pkg/scsi/target.go @@ -23,30 +23,19 @@ import ( "github.com/gostor/gotgt/pkg/api" ) -func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string, luns []string) (*api.SCSITarget, error) { +func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) { // verify the target ID // verify the target's Name // verify the low level driver var target = &api.SCSITarget{ - Name: name, - TID: tid, - Devices: []*api.SCSILu{}, + Name: name, + TID: tid, } - var devices = []*api.SCSILu{} - for i, ln := range luns { - lun, err := NewSCSILu(uint64(i), target, ln) - if err != nil { - glog.Errorf("fail to create LU: %v", err) - return nil, err - } - devices = append(devices, lun) - } - s.mutex.Lock() - target.Devices = devices s.Targets = append(s.Targets, target) - s.mutex.Unlock() + target.Devices = GetTargetLUNMap(target.Name) + return target, nil }