From 70e1955487f1ae0d4648fe9bc388568163745c44 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Mon, 16 Mar 2026 17:22:01 +0800 Subject: [PATCH] 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) --- pkg/port/iscsit/iscsid.go | 9 ++++++--- pkg/port/iscsit/session.go | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/pkg/port/iscsit/iscsid.go b/pkg/port/iscsit/iscsid.go index fb35a89..b0375c6 100644 --- a/pkg/port/iscsit/iscsid.go +++ b/pkg/port/iscsit/iscsid.go @@ -357,6 +357,7 @@ func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) { s.rxHandler(conn) if conn.state == CONN_STATE_CLOSE { log.Warningf("iscsi connection[%d] closed", conn.cid) + s.removeConnectionFromSession(conn) conn.close() IPMutex.Lock() 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 { log.Warningf("iscsi connection[%d] closed", conn.cid) + s.removeConnectionFromSession(conn) conn.close() IPMutex.Lock() remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0] @@ -491,7 +493,7 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) { case OpLogoutReq: log.Debug("OpLogoutReq") s.setClientStatus(false) - if err := iscsiExecLogout(conn); err != nil { + if err := s.iscsiExecLogout(conn); err != nil { log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE } @@ -559,7 +561,7 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error { 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()) cmd := conn.req conn.resp = &ISCSICommand{ @@ -573,6 +575,7 @@ func iscsiExecLogout(conn *iscsiConnection) error { } else { conn.resp.ExpCmdSN = conn.session.ExpCmdSN conn.resp.MaxCmdSN = conn.session.ExpCmdSN + conn.session.MaxQueueCommand + s.removeConnectionFromSession(conn) } IPMutex.Lock() remoteIP := strings.Split(conn.conn.RemoteAddr().String(), ":")[0] @@ -1018,7 +1021,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error s.setClientStatus(false) conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag} conn.txIOState = IOSTATE_TX_BHS - iscsiExecLogout(conn) + s.iscsiExecLogout(conn) case OpTextReq: err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode]) log.Error(err) diff --git a/pkg/port/iscsit/session.go b/pkg/port/iscsit/session.go index 386593d..faf49ef 100644 --- a/pkg/port/iscsit/session.go +++ b/pkg/port/iscsit/session.go @@ -334,6 +334,27 @@ func (s *ISCSITargetDriver) UnBindISCSISession(sess *ISCSISession) { defer target.SessionsRWMutex.Unlock() delete(target.Sessions, sess.TSIH) 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 {