Files
gotgt/pkg/port/iscsit/session.go
Le Zhang 547faf684d iSCSI/SCSI multi port/ALUA support
fix ALUA flag issue
fix NNA flag issue
fix fixed format sense data builder issue
2016-10-18 16:59:25 +08:00

262 lines
6.5 KiB
Go

/*
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 (
"fmt"
"math/rand"
"time"
)
var (
SESSION_NORMAL int = 0
SESSION_DISCOVERY int = 1
)
var DIGEST_CRC32C uint = 1 << 1
var DIGEST_NONE uint = 1 << 0
var DIGEST_ALL uint = DIGEST_NONE | DIGEST_CRC32C
var BHS_SIZE = 48
const (
MAX_QUEUE_CMD_MIN = 1
MAX_QUEUE_CMD_DEF = 128
MAX_QUEUE_CMD_MAX = 512
)
const (
ISCSI_PARAM_MAX_RECV_DLENGTH = iota
ISCSI_PARAM_HDRDGST_EN
ISCSI_PARAM_DATADGST_EN
ISCSI_PARAM_INITIAL_R2T_EN
ISCSI_PARAM_MAX_R2T
ISCSI_PARAM_IMM_DATA_EN
ISCSI_PARAM_FIRST_BURST
ISCSI_PARAM_MAX_BURST
ISCSI_PARAM_PDU_INORDER_EN
ISCSI_PARAM_DATASEQ_INORDER_EN
ISCSI_PARAM_ERL
ISCSI_PARAM_IFMARKER_EN
ISCSI_PARAM_OFMARKER_EN
ISCSI_PARAM_DEFAULTTIME2WAIT
ISCSI_PARAM_DEFAULTTIME2RETAIN
ISCSI_PARAM_OFMARKINT
ISCSI_PARAM_IFMARKINT
ISCSI_PARAM_MAXCONNECTIONS
/* iSCSI Extensions for RDMA (RFC5046) */
ISCSI_PARAM_RDMA_EXTENSIONS
ISCSI_PARAM_TARGET_RDSL
ISCSI_PARAM_INITIATOR_RDSL
ISCSI_PARAM_MAX_OUTST_PDU
/* "local" parmas, never sent to the initiator */
ISCSI_PARAM_FIRST_LOCAL
ISCSI_PARAM_MAX_XMIT_DLENGTH = ISCSI_PARAM_FIRST_LOCAL
ISCSI_PARAM_MAX_QUEUE_CMD
/* must always be last */
ISCSI_PARAM_MAX
)
type ISCSISessionParam struct {
State int
Value uint
}
/*
* The defaults here are according to the spec and must not be changed,
* otherwise the initiator may make the wrong assumption. If you want
* to change a value, edit the value in iscsi_target_create.
*
* The param MaxXmitDataSegmentLength doesn't really exist. It's a way
* to remember the RDSL of the initiator, which defaults to 8k if he has
* not told us otherwise.
*/
type iscsiSessionKeys struct {
name string
def uint
min uint
max uint
}
var sessionKeys []iscsiSessionKeys = []iscsiSessionKeys{
// ISCSI_PARAM_MAX_RECV_DLENGTH
{"MaxRecvDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_HDRDGST_EN
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
// ISCSI_PARAM_DATADGST_EN
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_ALL},
// ISCSI_PARAM_INITIAL_R2T_EN
{"InitialR2T", 1, 0, 1},
// ISCSI_PARAM_MAX_R2T
{"MaxOutstandingR2T", 1, 1, 65535},
// ISCSI_PARAM_IMM_DATA_EN
{"ImmediateData", 1, 0, 1},
// ISCSI_PARAM_FIRST_BURST
{"FirstBurstLength", 65536, 512, 16777215},
// ISCSI_PARAM_MAX_BURST
{"MaxBurstLength", 262144, 512, 16777215},
// ISCSI_PARAM_PDU_INORDER_EN
{"DataPDUInOrder", 1, 0, 1},
// ISCSI_PARAM_DATASEQ_INORDER_EN
{"DataSequenceInOrder", 1, 0, 1},
// ISCSI_PARAM_ERL
{"ErrorRecoveryLevel", 0, 0, 2},
// ISCSI_PARAM_IFMARKER_EN
{"IFMarker", 0, 0, 1},
// ISCSI_PARAM_OFMARKER_EN
{"OFMarker", 0, 0, 1},
// ISCSI_PARAM_DEFAULTTIME2WAIT
{"DefaultTime2Wait", 2, 0, 3600},
// ISCSI_PARAM_DEFAULTTIME2RETAIN
{"DefaultTime2Retain", 20, 0, 3600},
// ISCSI_PARAM_OFMARKINT
{"OFMarkInt", 2048, 1, 65535},
// ISCSI_PARAM_IFMARKINT
{"IFMarkInt", 2048, 1, 65535},
// ISCSI_PARAM_MAXCONNECTIONS
{"MaxConnections", 1, 1, 65535},
// ISCSI_PARAM_RDMA_EXTENSIONS
{"RDMAExtensions", 0, 0, 1},
// ISCSI_PARAM_TARGET_RDSL
{"TargetRecvDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_INITIATOR_RDSL
{"InitiatorRecvDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_MAX_OUTST_PDU
{"MaxOutstandingUnexpectedPDUs", 0, 2, 4294967295},
// "local" parmas, never sent to the initiator
// ISCSI_PARAM_MAX_XMIT_DLENGTH
{"MaxXmitDataSegmentLength", 8192, 512, 16777215},
// ISCSI_PARAM_MAX_QUEUE_CMD
{"MaxQueueCmd", MAX_QUEUE_CMD_DEF, MAX_QUEUE_CMD_MIN, MAX_QUEUE_CMD_MAX},
}
// Session is an iSCSI session.
type ISCSISession struct {
Refcount int
Initiator string
InitiatorAlias string
Target *ISCSITarget
Isid uint64
Tsih uint64
ExpCmdSN uint32
// only one connection per session
Connections []*iscsiConnection
Commands []*ISCSICommand
PendingTasks taskQueue
MaxQueueCommand uint32
SessionParam []ISCSISessionParam
Info string
Rdma int
}
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
}
func (tq taskQueue) Swap(i, j int) {
tq[i], tq[j] = tq[j], tq[i]
}
func (tq *taskQueue) Push(x interface{}) {
item := x.(*iscsiTask)
*tq = append(*tq, item)
}
func (tq *taskQueue) Pop() interface{} {
old := *tq
n := len(old)
item := old[n-1]
*tq = old[0 : n-1]
return item
}
// The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
// appear in all iSCSI PDUs. In addition, when used, the Initiator Task
// Tag and Logical Unit Number always appear in the same location in the
// header.
type iscsiHeader struct {
opcode uint8
flags uint8 // Final bit
rsvd2 [2]uint8
hlength uint8 // AHSs total length
dlength [3]uint8 // Data length
lun [8]uint8
itt uint8 // Initiator Task Tag
ttt uint8 // Target Task Tag
statsn uint8
expStatSN uint8
maxStatSN uint8
other [12]uint8
}
type iscsiPdu struct {
bhs iscsiHeader
ahsSize uint
dataSize uint
}
// New creates a new session.
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
var (
target *ISCSITarget
tsih uint64
)
for _, t := range s.iSCSITargets {
if t.TID == conn.tid {
target = t
break
}
}
if target == nil {
return nil, fmt.Errorf("No target found with tid(%d)", conn.tid)
}
for {
rand.Seed(int64(time.Now().UTC().Nanosecond()))
tsih = uint64(rand.Uint32())
for _, s := range target.Sessions {
if s.Tsih == tsih {
tsih = 0
break
}
}
if tsih != 0 {
break
}
}
sess := &ISCSISession{
Tsih: tsih,
Initiator: conn.initiator,
InitiatorAlias: conn.initiatorAlias,
Target: target,
Connections: []*iscsiConnection{conn},
SessionParam: conn.sessionParam,
MaxQueueCommand: uint32(conn.sessionParam[ISCSI_PARAM_MAX_QUEUE_CMD].Value),
Rdma: 0,
ExpCmdSN: conn.expCmdSN,
}
conn.session = sess
return sess, nil
}