From: Alexander Graf <graf@amazon.com>
To: <linux-kernel@vger.kernel.org>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>, <nh-open-source@amazon.com>,
"The AWS Nitro Enclaves Team"
<aws-nitro-enclaves-devel@amazon.com>
Subject: [PATCH 04/19] nitro_enclaves: Retry a command the device asks us to retry
Date: Wed, 11 Nov 2026 13:49:53 +0000 [thread overview]
Message-ID: <59febb32ee6366b39e484b3cd0c70b1133fadd87.1794393487.git.graf@amazon.com> (raw)
In-Reply-To: <cover.1794300000.git.graf@amazon.com>
The nitro_enclaves device may return -EAGAIN under rare circumstances,
which asks the system to perform the command again. Handle it as gracefully
as possible inside the kernel driver.
Most commands finish almost instantly; give them a 40s timeout. But commands
like free may indicate through -EAGAIN that they are still actively working
on freeling a slot which can take significantly longer. Use 10 minutes for
those.
To ensure that user space still reacts to events during such long timeout
periods, rework some of the locking ordering and use killable locks.
With this patch applied, a launch or a release that runs into such a
window waits for it to pass. When the device still asks for a retry once
the time is up, the caller gets -EAGAIN.
Signed-off-by: Alexander Graf <graf@amazon.com>
---
drivers/virt/nitro_enclaves/ne_misc_dev.c | 28 +++++---
drivers/virt/nitro_enclaves/ne_pci_dev.c | 83 ++++++++++++++++++++++-
2 files changed, 101 insertions(+), 10 deletions(-)
diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c
index bb5fdc377b051..0e9d6157dcc64 100644
--- a/drivers/virt/nitro_enclaves/ne_misc_dev.c
+++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c
@@ -1155,7 +1155,9 @@ static long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long
if (copy_from_user(&vcpu_id, (void __user *)arg, sizeof(vcpu_id)))
return -EFAULT;
- mutex_lock(&ne_enclave->enclave_info_mutex);
+ rc = mutex_lock_killable(&ne_enclave->enclave_info_mutex);
+ if (rc)
+ return rc;
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
@@ -1219,11 +1221,14 @@ static long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long
case NE_GET_IMAGE_LOAD_INFO: {
struct ne_image_load_info image_load_info = {};
+ int rc;
if (copy_from_user(&image_load_info, (void __user *)arg, sizeof(image_load_info)))
return -EFAULT;
- mutex_lock(&ne_enclave->enclave_info_mutex);
+ rc = mutex_lock_killable(&ne_enclave->enclave_info_mutex);
+ if (rc)
+ return rc;
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
@@ -1267,7 +1272,9 @@ static long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long
return -NE_ERR_INVALID_FLAG_VALUE;
}
- mutex_lock(&ne_enclave->enclave_info_mutex);
+ rc = mutex_lock_killable(&ne_enclave->enclave_info_mutex);
+ if (rc)
+ return rc;
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
@@ -1347,7 +1354,9 @@ static long ne_enclave_ioctl(struct file *file, unsigned int cmd, unsigned long
return -NE_ERR_INVALID_ENCLAVE_CID;
}
- mutex_lock(&ne_enclave->enclave_info_mutex);
+ rc = mutex_lock_killable(&ne_enclave->enclave_info_mutex);
+ if (rc)
+ return rc;
if (ne_enclave->state != NE_STATE_INIT) {
dev_err_ratelimited(ne_misc_dev.this_device,
@@ -1777,15 +1786,18 @@ static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case NE_CREATE_VM: {
- int enclave_fd = -1;
struct ne_pci_dev *ne_pci_dev = ne_devs.ne_pci_dev;
u64 __user *slot_uid = (void __user *)arg;
+ int rc;
- mutex_lock(&ne_pci_dev->enclaves_list_mutex);
- enclave_fd = ne_create_vm_ioctl(ne_pci_dev, slot_uid);
+ rc = mutex_lock_killable(&ne_pci_dev->enclaves_list_mutex);
+ if (rc)
+ return rc;
+
+ rc = ne_create_vm_ioctl(ne_pci_dev, slot_uid);
mutex_unlock(&ne_pci_dev->enclaves_list_mutex);
- return enclave_fd;
+ return rc;
}
default:
diff --git a/drivers/virt/nitro_enclaves/ne_pci_dev.c b/drivers/virt/nitro_enclaves/ne_pci_dev.c
index c37777bb95b49..199c3c7f8c42e 100644
--- a/drivers/virt/nitro_enclaves/ne_pci_dev.c
+++ b/drivers/virt/nitro_enclaves/ne_pci_dev.c
@@ -26,6 +26,32 @@
*/
#define NE_DEFAULT_TIMEOUT_MSECS (120000) /* 120 sec */
+/**
+ * NE_CMD_RETRY_INITIAL_MSECS - Delay before the first retry of a command the
+ * NE PCI device asked to be sent again.
+ */
+#define NE_CMD_RETRY_INITIAL_MSECS (10)
+
+/**
+ * NE_CMD_RETRY_MAX_MSECS - Upper bound on the delay between two retries of a
+ * command the NE PCI device asked to be sent again.
+ */
+#define NE_CMD_RETRY_MAX_MSECS (160)
+
+/**
+ * NE_CMD_RETRY_TOTAL_MSECS - How long after its first refusal a command the
+ * NE PCI device asks to be sent again may still go
+ * out, before -EAGAIN is reported to the caller.
+ */
+#define NE_CMD_RETRY_TOTAL_MSECS (40000) /* 40 sec */
+
+/**
+ * NE_FREE_TIMEOUT_MSECS - How long after a first -EAGAIN ENCLAVE_STOP and
+ * SLOT_FREE may still go out. Scrubbing a large enclave
+ * can take minutes.
+ */
+#define NE_FREE_TIMEOUT_MSECS (600000) /* 10 min */
+
static const struct pci_device_id ne_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMAZON, PCI_DEVICE_ID_NE) },
{ 0, }
@@ -96,11 +122,24 @@ static int ne_wait_for_reply(struct pci_dev *pdev)
return 0;
}
+/**
+ * ne_cmd_frees_slot() - Whether a command is one of the two that free a slot.
+ * @cmd_type: Command type of the request.
+ *
+ * Return: true for ENCLAVE_STOP and SLOT_FREE.
+ */
+static bool ne_cmd_frees_slot(enum ne_pci_dev_cmd_type cmd_type)
+{
+ return cmd_type == ENCLAVE_STOP || cmd_type == SLOT_FREE;
+}
+
int ne_do_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
void *cmd_request, size_t cmd_request_size,
struct ne_pci_dev_cmd_reply *cmd_reply, size_t cmd_reply_size)
{
+ unsigned int delay_msecs = NE_CMD_RETRY_INITIAL_MSECS;
struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
+ unsigned long deadline = 0;
int rc = -EINVAL;
if (cmd_type <= INVALID_CMD || cmd_type >= MAX_CMD) {
@@ -137,12 +176,23 @@ int ne_do_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
return -EINVAL;
}
+retry:
/*
* Use this mutex so that the PCI device handles one command request at
* a time.
*/
mutex_lock(&ne_pci_dev->pci_dev_mutex);
+ if (deadline && time_after_eq(jiffies, deadline)) {
+ dev_err_ratelimited(&pdev->dev,
+ "Device asked to retry cmd type=%u, giving up\n",
+ cmd_type);
+
+ rc = -EAGAIN;
+
+ goto unlock_mutex;
+ }
+
atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
ne_submit_request(pdev, cmd_type, cmd_request, cmd_request_size);
@@ -159,6 +209,27 @@ int ne_do_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
+ if (cmd_reply->rc == -EAGAIN) {
+ /*
+ * The device answered, so nothing is in flight and another
+ * command may go out while this one waits to be sent again.
+ */
+ mutex_unlock(&ne_pci_dev->pci_dev_mutex);
+
+ if (!deadline) {
+ unsigned int budget_msecs = ne_cmd_frees_slot(cmd_type) ?
+ NE_FREE_TIMEOUT_MSECS :
+ NE_CMD_RETRY_TOTAL_MSECS;
+
+ deadline = jiffies + msecs_to_jiffies(budget_msecs);
+ }
+
+ msleep(delay_msecs);
+ delay_msecs = min(delay_msecs * 2, NE_CMD_RETRY_MAX_MSECS);
+
+ goto retry;
+ }
+
if (cmd_reply->rc < 0) {
rc = cmd_reply->rc;
@@ -221,7 +292,14 @@ static void ne_event_work_handler(struct work_struct *work)
int rc = -EINVAL;
struct slot_info_req slot_info_req = {};
- mutex_lock(&ne_pci_dev->enclaves_list_mutex);
+ /*
+ * A release holds these locks for as long as the device takes to free
+ * the slot, which can be minutes, so wait killable: that is not
+ * reported as a hung task. The worker gets no signal, so the locks do
+ * not fail.
+ */
+ if (mutex_lock_killable(&ne_pci_dev->enclaves_list_mutex))
+ return;
/*
* Iterate over all enclaves registered for the Nitro Enclaves
@@ -229,7 +307,8 @@ static void ne_event_work_handler(struct work_struct *work)
* is corresponding to.
*/
list_for_each_entry(ne_enclave, &ne_pci_dev->enclaves_list, enclave_list_entry) {
- mutex_lock(&ne_enclave->enclave_info_mutex);
+ if (mutex_lock_killable(&ne_enclave->enclave_info_mutex))
+ break;
/*
* Enclaves that were never started cannot receive out-of-band
--
2.47.1
next prev parent reply other threads:[~2026-11-11 13:50 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 13:49 [PATCH 00/19] nitro_enclaves: Fix enclave release, command retries and the CPU pool Alexander Graf
2026-11-11 13:49 ` [PATCH 01/19] nitro_enclaves: Release the reference on a rejected region page Alexander Graf
2026-11-11 13:49 ` [PATCH 02/19] nitro_enclaves: Split freeing the slot from freeing the enclave Alexander Graf
2026-11-11 13:49 ` [PATCH 03/19] nitro_enclaves: Ignore the reply of a failed slot info command Alexander Graf
2026-11-11 13:49 ` Alexander Graf [this message]
2026-11-11 13:49 ` [PATCH 05/19] nitro_enclaves: Stop sending commands to a broken device Alexander Graf
2026-11-11 13:49 ` [PATCH 06/19] nitro_enclaves: Set up the device state before requesting its interrupts Alexander Graf
2026-11-11 13:49 ` [PATCH 07/19] nitro_enclaves: Wait for file operations before unbinding the PCI device Alexander Graf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=59febb32ee6366b39e484b3cd0c70b1133fadd87.1794393487.git.graf@amazon.com \
--to=graf@amazon.com \
--cc=arnd@arndb.de \
--cc=aws-nitro-enclaves-devel@amazon.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nh-open-source@amazon.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®