Thread (24 messages) 24 messages, 7 authors, 2023-10-13

Re: [PATCHv14 5/9] efi: Add unaccepted memory support

From: Kirill A. Shutemov <hidden>
Date: 2023-10-13 19:53:15
Also in: linux-efi, linux-mm, lkml

On Fri, Oct 13, 2023 at 12:45:20PM -0500, Tom Lendacky wrote:
On 10/13/23 11:22, Kirill A. Shutemov wrote:
quoted
On Fri, Oct 13, 2023 at 03:33:58PM +0300, Kirill A. Shutemov wrote:
quoted
quoted
While testing SNP guests running today's tip/master (ef19bc9dddc3) I ran
into what seems to be fairly significant lock contention due to the
unaccepted_memory_lock spinlock above, which results in a constant stream
of soft-lockups until the workload gets all its memory accepted/faulted
in if the guest has around 16+ vCPUs.

I've included the guest dmesg traces I was seeing below.

In this case I was running a 32 vCPU guest with 200GB of memory running on
a 256 thread EPYC (Milan) system, and can trigger the above situation fairly
reliably by running the following workload in a freshly-booted guests:

   stress --vm 32 --vm-bytes 5G --vm-keep

Scaling up the number of stress threads and vCPUs should make it easier
to reproduce.

Other than unresponsiveness/lockup messages until the memory is accepted,
the guest seems to continue running fine, but for large guests where
unaccepted memory is more likely to be useful, it seems like it could be
an issue, especially when consider 100+ vCPU guests.
Okay, sorry for delay. It took time to reproduce it with TDX.

I will look what can be done.
Could you check if the patch below helps?
diff --git a/drivers/firmware/efi/unaccepted_memory.c b/drivers/firmware/efi/unaccepted_memory.c
index 853f7dc3c21d..591da3f368fa 100644
--- a/drivers/firmware/efi/unaccepted_memory.c
+++ b/drivers/firmware/efi/unaccepted_memory.c
@@ -8,6 +8,14 @@
  /* Protects unaccepted memory bitmap */
  static DEFINE_SPINLOCK(unaccepted_memory_lock);
+struct accept_range {
+	struct list_head list;
+	unsigned long start;
+	unsigned long end;
+};
+
+static LIST_HEAD(accepting_list);
+
  /*
   * accept_memory() -- Consult bitmap and accept the memory if needed.
   *
@@ -24,6 +32,7 @@ void accept_memory(phys_addr_t start, phys_addr_t end)
  {
  	struct efi_unaccepted_memory *unaccepted;
  	unsigned long range_start, range_end;
+	struct accept_range range, *entry;
  	unsigned long flags;
  	u64 unit_size;
@@ -80,7 +89,25 @@ void accept_memory(phys_addr_t start, phys_addr_t end)
  	range_start = start / unit_size;
+	range.start = start;
+	range.end = end;
+retry:
  	spin_lock_irqsave(&unaccepted_memory_lock, flags);
+
+	list_for_each_entry(entry, &accepting_list, list) {
+		if (entry->end < start)
+			continue;
+		if (entry->start > end)
Should this be a >= check since start and end are page aligned values?
Right. Good catch.
quoted
+			continue;
+		spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
+
+		/* Somebody else accepting the range */
+		cpu_relax();
+		goto retry;
Could you set some kind of flag here so that ...
quoted
+	}
+
... once you get here, that means that area was accepted and removed from
the list, so I think you could just drop the lock and exit now, right?
Because at that point the bitmap will have been updated and you wouldn't be
accepting any memory anyway?
No. Consider the case if someone else accept part of the range you are
accepting.

I guess we can check if the range on the list covers what we are accepting
fully, but it complication. Checking bitmap at this point is cheap enough:
we already hold the lock.

-- 
  Kiryl Shutsemau / Kirill A. Shutemov
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help