262 lines
6.5 KiB
Go
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
|
|
}
|