From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id AABF91FF0AF for ; Fri, 06 Nov 2026 01:53:35 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id B8CE321965; Fri, 06 Nov 2026 01:51:06 +0100 (CET) From: Thomas Lamprecht To: pve-devel@lists.proxmox.com Subject: [PATCH v3 storage 36/76] multipath: collect identities of non-iSCSI SCSI paths Date: Fri, 6 Nov 2026 00:31:11 +0100 Message-ID: <20261105233336.2178064-37-t.lamprecht@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261105233336.2178064-1-t.lamprecht@proxmox.com> References: <20261105233336.2178064-1-t.lamprecht@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793926179765 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.323 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: HBKJ4WNPLPAKELCCJ5JRWW7H7GPROEI7 X-Message-ID-Hash: HBKJ4WNPLPAKELCCJ5JRWW7H7GPROEI7 X-MailFrom: t.lamprecht@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Fibre Channel paths need the same trusted WWID evidence as iSCSI paths, but have no session target to group them by. Allow collection across SCSI transports, treating each non-iSCSI path independently until its identity is established. Keep unresolved identities distinct from paths with no identity evidence, so consumers do not mistake failed discovery for an absent LUN. Signed-off-by: Thomas Lamprecht --- src/PVE/Multipath/Identity.pm | 27 ++++++++++------ src/test/run_lun_identity_tests.pl | 49 ++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/PVE/Multipath/Identity.pm b/src/PVE/Multipath/Identity.pm index 86141e98..3281c6d4 100644 --- a/src/PVE/Multipath/Identity.pm +++ b/src/PVE/Multipath/Identity.pm @@ -64,8 +64,13 @@ sub resolve($disks, $daemon, $naa_ok) { my $luns = {}; for my $name (sort keys %$disks) { my $disk = $disks->{$name}; - my $key = "$disk->{target}\0$disk->{lun}"; - $luns->{$key} //= { target => $disk->{target}, lun => $disk->{lun}, paths => [] }; + my $target = $disk->{target}; + my $key = defined($target) ? "$target\0$disk->{lun}" : "\0$name"; + $luns->{$key} //= { + defined($target) ? (target => $target) : (), + lun => $disk->{lun}, + paths => [], + }; push $luns->{$key}->{paths}->@*, $name; } @@ -151,22 +156,26 @@ my sub lun_targets($sessions) { return $res; } -my sub session_disks($sessions, $targets) { +my sub scsi_disks($sessions, $targets, $all) { my $lun_target = lun_targets($sessions); my $res = {}; dir_glob_foreach( "$SYSFS/bus/scsi/devices", '\d+:\d+:\d+:(\d+)', sub($hctl, $lun) { - my $target = $lun_target->{$hctl} // return; - return if $targets && !$targets->{$target}; + my $target = $lun_target->{$hctl}; + if (defined($target)) { + return if $targets && !$targets->{$target} && !$all; + } else { + return if !$all; + } my $dir = "bus/scsi/devices/$hctl"; return if (sysfs_line("$dir/type") // '') ne '0'; # disks only my (undef, $name) = dir_glob_regex("$SYSFS/$dir/block", '([a-z]\S*)'); return if !defined($name); my $devt = sysfs_line("$dir/block/$name/dev") // return; - my $disk = - { target => $target, lun => int($lun), devt => $devt, hctl => $hctl, dir => $dir }; + my $disk = { lun => int($lun), devt => $devt, hctl => $hctl, dir => $dir }; + $disk->{target} = $target if defined($target); # Read the holder through the SCSI device, so neither name can refer to another device # meanwhile. my (undef, $holder) = dir_glob_regex("$SYSFS/$dir/block/$name/holders", '(dm-\d+)'); @@ -182,7 +191,7 @@ my sub session_disks($sessions, $targets) { sub collect(%param) { my $sessions = iscsi_sessions(); - my $disks = session_disks($sessions, $param{targets}); + my $disks = scsi_disks($sessions, $param{targets}, $param{all}); my ($daemon, $error); if (grep { !defined($_->{map}) } values %$disks) { @@ -235,7 +244,7 @@ sub portals_covered($ids, $target, $portals) { } sub target_luns($ids, $target) { - return [grep { $_->{target} eq $target } values $ids->{luns}->%*]; + return [grep { ($_->{target} // '') eq $target } values $ids->{luns}->%*]; } # The WWID of the LUN of the path $name in $ids, if it was resolved and the name still denotes the diff --git a/src/test/run_lun_identity_tests.pl b/src/test/run_lun_identity_tests.pl index 4e9507a0..9f89c923 100755 --- a/src/test/run_lun_identity_tests.pl +++ b/src/test/run_lun_identity_tests.pl @@ -881,4 +881,53 @@ $rows = PVE::Multipath::Status::aggregate_cluster_status( ); ok($rows->[0]->{'not-observed'}, 'end to end: the last LUN of a target retired, not observed'); +# --- paths of other transports --- + +{ + # a Fibre Channel LUN on host 20, on a node without any iSCSI session: each path counts on + # its own + $identity->unmock_all(); + my $iscsi_sys = $sys; + $sys = tempdir(CLEANUP => 1); + $PVE::Multipath::Identity::SYSFS = $sys; + make_path("$sys/class/iscsi_session"); + disk_device('20:0:0:0', 'sdg', '8:96'); + disk_device('20:0:1:0', 'sdh', '8:112'); + $queries = 0; + $reply = { + sdg => { + devt => '8:96', + hctl => '20:0:0:0', + init => 'ok', + target => '0x200', + wwid => 'SFC_LUN', + }, + sdh => { + devt => '8:112', + hctl => '20:0:1:0', + init => 'ok', + target => '0x201', + wwid => 'SFC_LUN', + }, + }; + my $ids = PVE::Multipath::Identity::collect(targets => {}); + is( + PVE::Multipath::Identity::path_wwid($ids, 'sdg'), + undef, + 'other transports: not without asking for them', + ); + $ids = PVE::Multipath::Identity::collect(all => 1); + is_deeply( + [map { PVE::Multipath::Identity::path_wwid($ids, $_) } qw(sdg sdh)], + ['SFC_LUN', 'SFC_LUN'], + 'other transports: every path identified on its own', + ); + is( + scalar(grep { !defined($_->{target}) } values $ids->{luns}->%*), + 2, + 'other transports: as LUNs of their own, as nothing groups the paths', + ); + ($sys, $PVE::Multipath::Identity::SYSFS) = ($iscsi_sys, $iscsi_sys); +} + done_testing(); -- 2.47.3