Blame SOURCES/0163-efinet-open-Simple-Network-Protocol-exclusively.patch

28f7f8
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
ecb9bb
From: Andrei Borzenkov <arvidjaar@gmail.com>
ecb9bb
Date: Thu, 7 May 2015 20:37:17 +0300
28f7f8
Subject: [PATCH] efinet: open Simple Network Protocol exclusively
ecb9bb
ecb9bb
EDK2 network stack is based on Managed Network Protocol which is layered
ecb9bb
on top of Simple Management Protocol and does background polling. This
ecb9bb
polling races with grub for received (and probably trasmitted) packets
ecb9bb
which causes either serious slowdown or complete failure to load files.
ecb9bb
ecb9bb
Open SNP device exclusively.  This destroys all child MNP instances and
ecb9bb
stops background polling.
ecb9bb
ecb9bb
Exclusive open cannot be done when enumerating cards, as it would destroy
ecb9bb
PXE information we need to autoconfigure interface; and it cannot be done
ecb9bb
during autoconfiguration as we need to do it for non-PXE boot as well. So
ecb9bb
move SNP open to card ->open method and add matching ->close to clean up.
ecb9bb
ecb9bb
Based on patch from Mark Salter <msalter@redhat.com>
ecb9bb
ecb9bb
Also-By: Mark Salter <msalter@redhat.com>
ecb9bb
Closes: 41731
ecb9bb
---
ecb9bb
 grub-core/net/drivers/efi/efinet.c | 46 ++++++++++++++++++++++++++++++++++++++
ecb9bb
 1 file changed, 46 insertions(+)
ecb9bb
ecb9bb
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
28f7f8
index caa7b50228b..6a1dd1f9dff 100644
ecb9bb
--- a/grub-core/net/drivers/efi/efinet.c
ecb9bb
+++ b/grub-core/net/drivers/efi/efinet.c
ecb9bb
@@ -142,9 +142,55 @@ get_card_packet (struct grub_net_card *dev)
ecb9bb
   return nb;
ecb9bb
 }
ecb9bb
 
ecb9bb
+static grub_err_t
ecb9bb
+open_card (struct grub_net_card *dev)
ecb9bb
+{
ecb9bb
+  grub_efi_simple_network_t *net;
ecb9bb
+
ecb9bb
+  /* Try to reopen SNP exlusively to close any active MNP protocol instance
ecb9bb
+     that may compete for packet polling
ecb9bb
+   */
ecb9bb
+  net = grub_efi_open_protocol (dev->efi_handle, &net_io_guid,
ecb9bb
+				GRUB_EFI_OPEN_PROTOCOL_BY_EXCLUSIVE);
ecb9bb
+  if (net)
ecb9bb
+    {
ecb9bb
+      if (net->mode->state == GRUB_EFI_NETWORK_STOPPED
ecb9bb
+	  && efi_call_1 (net->start, net) != GRUB_EFI_SUCCESS)
ecb9bb
+	return grub_error (GRUB_ERR_NET_NO_CARD, "%s: net start failed",
ecb9bb
+			   dev->name);
ecb9bb
+
ecb9bb
+      if (net->mode->state == GRUB_EFI_NETWORK_STOPPED)
ecb9bb
+	return grub_error (GRUB_ERR_NET_NO_CARD, "%s: card stopped",
ecb9bb
+			   dev->name);
ecb9bb
+
ecb9bb
+      if (net->mode->state == GRUB_EFI_NETWORK_STARTED
ecb9bb
+	  && efi_call_3 (net->initialize, net, 0, 0) != GRUB_EFI_SUCCESS)
ecb9bb
+	return grub_error (GRUB_ERR_NET_NO_CARD, "%s: net initialize failed",
ecb9bb
+			   dev->name);
ecb9bb
+
ecb9bb
+      efi_call_4 (grub_efi_system_table->boot_services->close_protocol,
ecb9bb
+		  dev->efi_net, &net_io_guid,
ecb9bb
+		  grub_efi_image_handle, dev->efi_handle);
ecb9bb
+      dev->efi_net = net;
ecb9bb
+    }
ecb9bb
+
ecb9bb
+  /* If it failed we just try to run as best as we can */
ecb9bb
+  return GRUB_ERR_NONE;
ecb9bb
+}
ecb9bb
+
ecb9bb
+static void
ecb9bb
+close_card (struct grub_net_card *dev)
ecb9bb
+{
ecb9bb
+  efi_call_4 (grub_efi_system_table->boot_services->close_protocol,
ecb9bb
+	      dev->efi_net, &net_io_guid,
ecb9bb
+	      grub_efi_image_handle, dev->efi_handle);
ecb9bb
+}
ecb9bb
+
ecb9bb
 static struct grub_net_card_driver efidriver =
ecb9bb
   {
ecb9bb
     .name = "efinet",
ecb9bb
+    .open = open_card,
ecb9bb
+    .close = close_card,
ecb9bb
     .send = send_card_buffer,
ecb9bb
     .recv = get_card_packet
ecb9bb
   };