Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## Unreleased

-
### Bugfixes

- Fix panic if multiple instances are configured and too many instances become unavailable ([#313](https://github.com/Nitrokey/nethsm-pkcs11/issues/313))

## [2.1.0][] (2026-02-27)

Expand Down
27 changes: 10 additions & 17 deletions pkcs11/src/backend/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,33 +227,26 @@ impl LoginCtx {
accept_health_check: HealthCheck,
) -> (&InstanceData, ShouldHealthCheck) {
let threads_allowed = THREADS_ALLOWED.load(Relaxed);
let index = self.slot.instance_balancer.fetch_add(1, Relaxed);
let index = index % self.slot.instances.len();
let instance = &self.slot.instances[index];
match (instance.should_try(), threads_allowed, accept_health_check) {
(InstanceAttempt::Failed, _, _)
| (InstanceAttempt::Retry, true, _)
| (InstanceAttempt::Retry, false, HealthCheck::Avoid) => {}
(InstanceAttempt::Working, _, _) => return (instance, ShouldHealthCheck::RunDirectly),
(InstanceAttempt::Retry, false, HealthCheck::Possible) => {
return (instance, ShouldHealthCheck::HealthCheckFirst)
}
}
for i in 0..self.slot.instances.len() - 1 {
let instance = &self.slot.instances[index + i];

let index = self.slot.instance_balancer.load(Relaxed);
let (head, tail) = self
.slot
.instances
.split_at((index + 1) % self.slot.instances.len());
for (i, instance) in tail.iter().chain(head).enumerate() {
// we split at index + 1, so we need to add i + 1 to the old index
let i = i + 1;
match (instance.should_try(), threads_allowed, accept_health_check) {
(InstanceAttempt::Failed, _, _)
| (InstanceAttempt::Retry, true, _)
| (InstanceAttempt::Retry, false, HealthCheck::Avoid) => continue,
(InstanceAttempt::Working, _, _) => {
// This not true round-robin in case of multithreaded acces
// This not true round-robin in case of multithreaded access
// This is degraded mode so best-effort is attempted at best
self.slot.instance_balancer.fetch_add(i, Relaxed);
return (instance, ShouldHealthCheck::RunDirectly);
}
(InstanceAttempt::Retry, false, HealthCheck::Possible) => {
// This not true round-robin in case of multithreaded acces
// This not true round-robin in case of multithreaded access
// This is degraded mode so best-effort is attempted at best
self.slot.instance_balancer.fetch_add(i, Relaxed);
return (instance, ShouldHealthCheck::HealthCheckFirst);
Expand Down
25 changes: 25 additions & 0 deletions tools/tests/p11nethsm.unreachable.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# log_file: /tmp/p11nethsm.log
# log_level: Trace
slots:
- label: LocalHSM
description: Local HSM (docker)
operator:
username: "operator"
password: "opPassphrase"
administrator:
username: "admin"
password: "Administrator"
instances:
- url: "https://localhost:9999/api/v1"
max_idle_connections: 16
danger_insecure_cert: true
- url: "https://localhost:9998/api/v1"
max_idle_connections: 16
danger_insecure_cert: true
- url: "https://localhost:9998/api/v1"
max_idle_connections: 16
danger_insecure_cert: true
retries:
count: 1
delay_seconds: 1
timeout_seconds: 10
11 changes: 11 additions & 0 deletions tools/tests/unreachable_instances.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash -x

set -e

export P11NETHSM_CONFIG_FILE=./tools/tests/p11nethsm.unreachable.conf

output=$(pkcs11-tool --module ./target/debug/libnethsm_pkcs11.so -O 2>&1 || true)
echo "$output"

! grep --quiet panic <<< "$output"
grep --quiet "No slot with a token was found" <<< "$output"
Loading