@@ -350,23 +350,72 @@ private boolean hasOtherActiveLuns(String host, int port, String iqn, String lun
350350 }
351351 for (java .io .File entry : entries ) {
352352 String name = entry .getName ();
353- if (name .startsWith (prefix ) && !name .equals (prefix + lun )) {
353+ // Skip partition entries (e.g. lun-0-part1, lun-0-part2) — these are not
354+ // independent LUNs, they are partition symlinks for the same LUN disk.
355+ // Only count actual LUN entries (no "-part" suffix after the lun number).
356+ if (name .startsWith (prefix ) && !name .equals (prefix + lun ) && !name .contains ("-part" )) {
354357 logger .debug ("Found other active LUN on same target: " + name );
355358 return true ;
356359 }
357360 }
358361 return false ;
359362 }
360363
364+ /**
365+ * Removes a single stale SCSI device from the kernel using the sysfs interface.
366+ *
367+ * When ONTAP unmaps a LUN from the host's igroup, the by-path symlink and the
368+ * underlying SCSI device (/dev/sdX) remain present in the kernel until explicitly
369+ * removed — the kernel does not auto-remove devices from live iSCSI sessions.
370+ *
371+ * This method resolves the by-path symlink to the real block device name (e.g. sdd),
372+ * then writes "1" to /sys/block/<dev>/device/delete — the standard Linux kernel SCSI
373+ * API for removing a single device without tearing down the entire iSCSI session.
374+ * Once the kernel processes the delete, it also removes the by-path symlink.
375+ *
376+ * This is used instead of iscsiadm --logout when other LUNs on the same IQN are still
377+ * active (ONTAP single-IQN-per-SVM model), since logout would tear down ALL LUNs.
378+ */
379+ private void removeStaleScsiDevice (String host , int port , String iqn , String lun ) {
380+ String byPath = getByPath (host , port , "/" + iqn + "/" + lun );
381+ java .nio .file .Path byPathLink = java .nio .file .Paths .get (byPath );
382+ if (!java .nio .file .Files .exists (byPathLink )) {
383+ logger .debug ("by-path entry for LUN " + lun + " already gone, nothing to remove" );
384+ return ;
385+ }
386+ try {
387+ java .nio .file .Path realDevice = byPathLink .toRealPath ();
388+ String devName = realDevice .getFileName ().toString ();
389+ java .io .File deleteFile = new java .io .File ("/sys/block/" + devName + "/device/delete" );
390+ if (!deleteFile .exists ()) {
391+ logger .warn ("sysfs delete entry not found for device " + devName + " — cannot remove stale SCSI device" );
392+ return ;
393+ }
394+ try (java .io .FileWriter fw = new java .io .FileWriter (deleteFile )) {
395+ fw .write ("1" );
396+ }
397+ logger .info ("Removed stale SCSI device " + devName + " for LUN /" + iqn + "/" + lun + " via sysfs" );
398+ } catch (Exception e ) {
399+ logger .warn ("Failed to remove stale SCSI device for LUN /" + iqn + "/" + lun + ": " + e .getMessage ());
400+ }
401+ }
402+
361403 private boolean disconnectPhysicalDisk (String host , int port , String iqn , String lun ) {
362404 // Check if other LUNs on the same IQN target are still in use.
363405 // ONTAP (and similar) uses a single IQN per SVM with multiple LUNs.
364406 // Doing iscsiadm --logout tears down the ENTIRE target session,
365407 // which would destroy access to ALL LUNs — not just the one being disconnected.
366408 if (hasOtherActiveLuns (host , port , iqn , lun )) {
367409 logger .info ("Skipping iSCSI logout for /" + iqn + "/" + lun +
368- " — other LUNs on the same target are still active" );
369- return true ;
410+ " — other LUNs on the same target are still active. Removing stale SCSI device for this LUN only." );
411+ removeStaleScsiDevice (host , port , iqn , lun );
412+ // After removing this LUN's device, re-check: if no other LUNs remain active,
413+ // If it is the last one then must logout to clean up the iSCSI session entirely.
414+ if (hasOtherActiveLuns (host , port , iqn , lun )) {
415+ logger .info ("Other LUNs still active after removing /" + iqn + "/" + lun + " — session kept alive." );
416+ return true ;
417+ }
418+ logger .info ("No more active LUNs on target after removing /" + iqn + "/" + lun + " — proceeding with iSCSI logout." );
370419 }
371420
372421 // No other LUNs active on this target — safe to logout and delete the node record.
0 commit comments