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 12BEE1FF0EF for ; Sun, 02 Aug 2026 05:33:22 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 3115A2155B; Sun, 02 Aug 2026 05:32:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=neatech-ar.20251104.gappssmtp.com; s=20251104; t=1785641566; x=1786246366; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=M+JRaeWY55uhNtc29Xcu66suGYRmGwRNSNDOIx+UBPo=; b=lXK5HCLbHXA85Dw40t+0TYHUKIKvUyVvE0sobXR22x1SvjlIsYepgg6Ar7XV3Y0xhr O9p53zpJz21g0o+LwIrXbWvYlP0yrlGWK6irKZOdyBBdQydZHdP3AxomlyqgxWMufFXz hMwYmJCCSrcwI0+jp9WBbhNTrGYk6qubvlkaznrdo1sQtZpacYBEqyWzoTcccp9yxCf2 qMBCuDIely4bL3kE8jZvViwpFr8GWwBlqwjbJiBO06UM8wmwCa2VemHoa4LbNXvGtrQQ ohV5tQTdXg1PkAWLe/vsmA3AAzgZIlKk+0nEG5ohYt1DnL+9y7i9le1dnfhGfNepj558 cTVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785641566; x=1786246366; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=M+JRaeWY55uhNtc29Xcu66suGYRmGwRNSNDOIx+UBPo=; b=mv8sUHSyh+cNcyS8aLtaGc/Nj6fJuyXtXJf3p+01ZOzWKntXfyY++DP9Ojv+D+uXEp J2AWcrCj/ueBQAHBHPqIFfjzsBW+5lQVe/hBfIB9LGxqgaw6cll458f0PNCift738q1B zksdf5b2hxap+wT3QmSJx2+XJLgZjWYIB3XD+vqY8nuhmZvntnDo3yR/Ve3cMaijCoDV DoWr3Q9bjpD3hBXI5DxrRVO2JJv/elCposwrTpZdKyZj6dCTWHeJlW7jD4LgAUErvjB4 HOd95EYA4U1KT9nQntFf+vhz5/X+C/rkiZyTTb5GCsf8mH5AodB/YRLKZpq/Odl5G80I 9W5Q== X-Gm-Message-State: AOJu0YytGo7yWjprtlpyr8BlfmwdLTzrBXWidg30POChOh95YkZP5sJn ceccLFfIc/JO1iNihbTaSjvDJNwadkcAMzkt+McJ0Mnb6Daf6SZxXbUnt44itCTgTfHyjeYCbnp kn6oi1Q8= X-Gm-Gg: AR+sD10UUlIhqjvHUTnd0Z6sV5LQVDghb9o5qlO4ZFT09zDNkQLZXXf8sgHcC918djv 4Yj9OHDph7WxhEtXBQSsYTiknJiWqavsay1Bytb7bTRODTMW7SRfFJCluizb2TT8opHrGJAoTRL fvDZOFavek5kskbP+YqJy06k28RhOwhxoQWFul2JbMWUcMS6EBjW1eYgI2wKD15mTS387zUzE90 b4ou8oC1QGoA/q+qVVmxpKtIQXm0epIBiR8Pxd3n//H84K/Y5Ff1N5T/+gP2zXaB5NClw+HKWiA WeqHWQX2PUnbLgsv30NtFMu6v+ODae3iDfXUshpt0Q/0rZF6v07947O+zYsQkQ9MLCBo9bPKOFK ps9gGZZXgpHXzgkHIGZ8rCjf8k1Wp1oDLwyY8z9L2RI027JPCwLNo2vSBeRzzptCA9nXYKHgeI0 /lJXKxlZ1++nyjdPoELJSj0fjzrkf2vKheJDKG4VDUu2CeP1kmaSQrhx/taamXGpVtFoMTTi9DR 0DNEt/KLYdrmQQzIbgZa7aRr35x3kFVr/fNUazDDHYx X-Received: by 2002:a17:902:da81:b0:2cc:d6de:d58e with SMTP id d9443c01a7336-2d05242299dmr83464755ad.4.1785641565685; Sat, 01 Aug 2026 20:32:45 -0700 (PDT) From: Joaquin Varela To: pve-devel@lists.proxmox.com Subject: [PATCH storage v2 4/7] zfsnvme: make all-path loss policy explicit Date: Sun, 2 Aug 2026 00:31:38 -0300 Message-ID: <5dc4f773ff3bcabb4debdeb26815c2acea17b371.1785636979.git.joaquinvarela@neatech.ar> X-Mailer: git-send-email 2.54.0.windows.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DMARC_PASS -0.1 DMARC pass policy RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no 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: 5QL3HVS3TLT7IEY5IWAPUVIOEATZETUO X-Message-ID-Hash: 5QL3HVS3TLT7IEY5IWAPUVIOEATZETUO X-MailFrom: joaquinvarela@neatech.ar 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 CC: Joaquin Varela X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Expose nvme-fast-io-fail-tmo as an optional storage property. Leaving it unset preserves queue-until-controller-loss behavior. Setting it asks the kernel to return I/O errors earlier when all paths are reconnecting. Reject values greater than a finite controller-loss timeout. Pass the policy through libnvme runtime JSON and explicit nvme connect arguments. Add tests for propagation and validation. Signed-off-by: Joaquin Varela --- src/PVE/Storage/ZFSNVMePlugin.pm | 29 ++++++++++++++++++++++ src/test/zfsnvme_test.pm | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/PVE/Storage/ZFSNVMePlugin.pm b/src/PVE/Storage/ZFSNVMePlugin.pm index d02dff4..6513df0 100644 --- a/src/PVE/Storage/ZFSNVMePlugin.pm +++ b/src/PVE/Storage/ZFSNVMePlugin.pm @@ -237,6 +237,14 @@ sub properties($class) { maximum => 86400, default => 600, }, + 'nvme-fast-io-fail-tmo' => { + description => + "Optional time before failing I/O on a reconnecting NVMe controller. Unset queues I/O until controller loss timeout.", + type => 'integer', + minimum => 0, + maximum => 86400, + optional => 1, + }, 'nvme-nr-io-queues' => { description => "Number of NVMe/TCP I/O queues per controller.", type => 'integer', @@ -261,6 +269,7 @@ sub options($class) { 'nvme-keep-alive-tmo' => { optional => 1 }, 'nvme-reconnect-delay' => { optional => 1 }, 'nvme-ctrl-loss-tmo' => { optional => 1 }, + 'nvme-fast-io-fail-tmo' => { optional => 1 }, 'nvme-nr-io-queues' => { optional => 1 }, nodes => { optional => 1 }, disable => { optional => 1 }, @@ -269,6 +278,18 @@ sub options($class) { }; } +sub _validate_fail_fast_timeout($config, $default_ctrl_loss_tmo = undef) { + my $fast = $config->{'nvme-fast-io-fail-tmo'}; + return if !defined($fast); + + my $ctrl = $config->{'nvme-ctrl-loss-tmo'}; + $ctrl = $default_ctrl_loss_tmo if !defined($ctrl); + return if !defined($ctrl) || $ctrl < 0; + + die "nvme-fast-io-fail-tmo must not exceed nvme-ctrl-loss-tmo\n" + if $fast > $ctrl; +} + sub check_config($class, $section_id, $config, $create, $skip_schema_check) { if ($create) { $config->{sparse} = 1 if !defined($config->{sparse}); @@ -285,6 +306,7 @@ sub check_config($class, $section_id, $config, $create, $skip_schema_check) { } elsif (defined($config->{'nvme-host-ifaces'})) { parse_nvme_host_ifaces($config->{'nvme-host-ifaces'}); } + _validate_fail_fast_timeout($config, $create ? 600 : undef); return $class->SUPER::check_config($section_id, $config, $create, $skip_schema_check); } @@ -380,6 +402,7 @@ sub on_update_hook_full($class, $storeid, $scfg, $update, $delete, $sensitive) { delete @prospective{$delete->@*} if $delete; verify_nvme_nqn($prospective{subsysnqn}); _configured_portals(\%prospective); + _validate_fail_fast_timeout(\%prospective, 600); _assert_unique_target($storeid, \%prospective); my $old_key = file_read_firstline(secret_path($storeid)); @@ -433,6 +456,9 @@ my sub write_runtime_config($storeid, $scfg, $hostnqn, $hostid, $key, $portals) keep_alive_tmo => $scfg->{'nvme-keep-alive-tmo'} // 5, reconnect_delay => $scfg->{'nvme-reconnect-delay'} // 2, ctrl_loss_tmo => $scfg->{'nvme-ctrl-loss-tmo'} // 600, + (defined($scfg->{'nvme-fast-io-fail-tmo'}) + ? (fast_io_fail_tmo => $scfg->{'nvme-fast-io-fail-tmo'}) + : ()), ($scfg->{'nvme-nr-io-queues'} ? (nr_io_queues => $scfg->{'nvme-nr-io-queues'}) : ()), @@ -585,6 +611,9 @@ sub _connect_portal($config_path, $scfg, $portal) { '--ctrl-loss-tmo', $scfg->{'nvme-ctrl-loss-tmo'} // 600, ]; + if (defined(my $fast = $scfg->{'nvme-fast-io-fail-tmo'})) { + push $cmd->@*, '--fast_io_fail_tmo', $fast; + } if (my $queues = $scfg->{'nvme-nr-io-queues'}) { push $cmd->@*, '--nr-io-queues', $queues; } diff --git a/src/test/zfsnvme_test.pm b/src/test/zfsnvme_test.pm index edb8b38..e06ac76 100644 --- a/src/test/zfsnvme_test.pm +++ b/src/test/zfsnvme_test.pm @@ -244,6 +244,22 @@ is_deeply( 'nvme connect is explicitly bound to the configured data interface', ); +PVE::Storage::ZFSNVMePlugin::_connect_portal( + '/run/pve-storage/test.json', + { + subsysnqn => 'nqn.2026-07.example:test', + 'nvme-ctrl-loss-tmo' => 60, + 'nvme-fast-io-fail-tmo' => 15, + }, + { address => '10.90.2.11', port => 4420, host_iface => 'ens21' }, +); +my $connect_args = join(' ', @connect_cmd); +like( + $connect_args, + qr/--fast_io_fail_tmo 15/, + 'configured fast I/O fail timeout is passed to nvme connect', +); + $nvme_mock->redefine( zfs_request => sub { my ($class, $config, $timeout, $method, @params) = @_; @@ -329,6 +345,32 @@ eval { }; is($@, '', 'check_config accepts a valid partial update'); +eval { + PVE::Storage::ZFSNVMePlugin::_validate_fail_fast_timeout( + { + 'nvme-ctrl-loss-tmo' => 30, + 'nvme-fast-io-fail-tmo' => 31, + }, + 600, + ); +}; +like( + $@, + qr/must not exceed nvme-ctrl-loss-tmo/, + 'fast I/O fail timeout cannot outlive the controller loss timeout', +); + +eval { + PVE::Storage::ZFSNVMePlugin::_validate_fail_fast_timeout( + { + 'nvme-ctrl-loss-tmo' => -1, + 'nvme-fast-io-fail-tmo' => 30, + }, + 600, + ); +}; +is($@, '', 'fast I/O fail remains valid with infinite controller reconnect'); + { no warnings 'redefine'; local *PVE::Storage::config = sub { return { ids => {} } }; -- 2.54.0.windows.1