fix: remove dead sessions from target session list

When an iSCSI connection closes or a logout occurs, the associated
session was never removed from the target's Sessions map, and the
TSIH was never released back to the bitmap allocator. This caused
session and TSIH leaks over repeated connect/disconnect cycles.

Changes:
- Add removeConnectionFromSession() to properly clean up session
  when its last connection is closed
- Call session cleanup from handler() on CONN_STATE_CLOSE
- Convert iscsiExecLogout to a method and add session cleanup on logout
- Release TSIH in UnBindISCSISession to prevent TSIH bitmap exhaustion

Fixes #42

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lei Xue
2026-03-16 17:22:01 +08:00
parent a75e34e9d3
commit 70e1955487
2 changed files with 27 additions and 3 deletions

View File

@@ -357,6 +357,7 @@ func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) {
s.rxHandler(conn) s.rxHandler(conn)
if conn.state == CONN_STATE_CLOSE { if conn.state == CONN_STATE_CLOSE {
log.Warningf("iscsi connection[%d] closed", conn.cid) log.Warningf("iscsi connection[%d] closed", conn.cid)
s.removeConnectionFromSession(conn)
conn.close() conn.close()
IPMutex.Lock() IPMutex.Lock()
remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0] remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0]
@@ -373,6 +374,7 @@ func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) {
} }
if conn.state == CONN_STATE_CLOSE { if conn.state == CONN_STATE_CLOSE {
log.Warningf("iscsi connection[%d] closed", conn.cid) log.Warningf("iscsi connection[%d] closed", conn.cid)
s.removeConnectionFromSession(conn)
conn.close() conn.close()
IPMutex.Lock() IPMutex.Lock()
remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0] remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0]
@@ -491,7 +493,7 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) {
case OpLogoutReq: case OpLogoutReq:
log.Debug("OpLogoutReq") log.Debug("OpLogoutReq")
s.setClientStatus(false) s.setClientStatus(false)
if err := iscsiExecLogout(conn); err != nil { if err := s.iscsiExecLogout(conn); err != nil {
log.Warningf("set connection to close") log.Warningf("set connection to close")
conn.state = CONN_STATE_CLOSE conn.state = CONN_STATE_CLOSE
} }
@@ -559,7 +561,7 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
return conn.buildRespPackage(OpLoginResp, nil) return conn.buildRespPackage(OpLoginResp, nil)
} }
func iscsiExecLogout(conn *iscsiConnection) error { func (s *ISCSITargetDriver) iscsiExecLogout(conn *iscsiConnection) error {
log.Infof("Logout request received from initiator: %v", conn.conn.RemoteAddr().String()) log.Infof("Logout request received from initiator: %v", conn.conn.RemoteAddr().String())
cmd := conn.req cmd := conn.req
conn.resp = &ISCSICommand{ conn.resp = &ISCSICommand{
@@ -573,6 +575,7 @@ func iscsiExecLogout(conn *iscsiConnection) error {
} else { } else {
conn.resp.ExpCmdSN = conn.session.ExpCmdSN conn.resp.ExpCmdSN = conn.session.ExpCmdSN
conn.resp.MaxCmdSN = conn.session.ExpCmdSN + conn.session.MaxQueueCommand conn.resp.MaxCmdSN = conn.session.ExpCmdSN + conn.session.MaxQueueCommand
s.removeConnectionFromSession(conn)
} }
IPMutex.Lock() IPMutex.Lock()
remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0] remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0]
@@ -1018,7 +1021,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error
s.setClientStatus(false) s.setClientStatus(false)
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag} conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}
conn.txIOState = IOSTATE_TX_BHS conn.txIOState = IOSTATE_TX_BHS
iscsiExecLogout(conn) s.iscsiExecLogout(conn)
case OpTextReq: case OpTextReq:
err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode]) err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode])
log.Error(err) log.Error(err)

View File

@@ -334,6 +334,27 @@ func (s *ISCSITargetDriver) UnBindISCSISession(sess *ISCSISession) {
defer target.SessionsRWMutex.Unlock() defer target.SessionsRWMutex.Unlock()
delete(target.Sessions, sess.TSIH) delete(target.Sessions, sess.TSIH)
scsi.RemoveITNexus(sess.Target.SCSITarget, sess.ITNexus) scsi.RemoveITNexus(sess.Target.SCSITarget, sess.ITNexus)
s.ReleaseTSIH(sess.TSIH)
log.Infof("session %x unbound from target %s", sess.TSIH, target.SCSITarget.Name)
}
// removeConnectionFromSession removes a connection from its session.
// If the session has no remaining connections, the session is unbound.
func (s *ISCSITargetDriver) removeConnectionFromSession(conn *iscsiConnection) {
sess := conn.session
if sess == nil {
return
}
sess.ConnectionsRWMutex.Lock()
delete(sess.Connections, conn.cid)
remaining := len(sess.Connections)
sess.ConnectionsRWMutex.Unlock()
if remaining == 0 {
s.UnBindISCSISession(sess)
}
conn.session = nil
} }
func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error { func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error {