186 lines
6.3 KiB
Diff
186 lines
6.3 KiB
Diff
From 9f0274170a9aa123f12b995fbe613051439c8a03 Mon Sep 17 00:00:00 2001
|
|
From: Fedora Ninjas <grub2-owner@fedoraproject.org>
|
|
Date: Tue, 25 Mar 2025 17:31:59 +0000
|
|
Subject: [PATCH 1/2] efinet: Close and reopen card on failure
|
|
|
|
There are some known bugs with network adapter firmware implementations,
|
|
that may lead to intermittent problem of network adapter link being down, despite network
|
|
being set up.
|
|
Ultimate fix of this issue should be done on firmware side, but as for now we try to close
|
|
and reopen network adapter and retransmit packet in case we see failures.
|
|
|
|
Without this fix certain amount of PXE boots fails with inability to transmit packet, with this fix,
|
|
such failures are not seen.
|
|
|
|
Orabug: 35126950
|
|
Orabug: 37747175
|
|
Signed-off-by: Alex Burmashev <alexander.burmashev@oracle.com>
|
|
---
|
|
grub-core/net/drivers/efi/efinet.c | 143 +++++++++++++++++------------
|
|
1 file changed, 82 insertions(+), 61 deletions(-)
|
|
|
|
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
|
|
index 1a24f38..a8a1bfa 100644
|
|
--- a/grub-core/net/drivers/efi/efinet.c
|
|
+++ b/grub-core/net/drivers/efi/efinet.c
|
|
@@ -37,67 +37,6 @@ static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
|
|
static grub_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
|
|
static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
|
|
|
|
-static grub_err_t
|
|
-send_card_buffer (struct grub_net_card *dev,
|
|
- struct grub_net_buff *pack)
|
|
-{
|
|
- grub_efi_status_t st;
|
|
- grub_efi_simple_network_t *net = dev->efi_net;
|
|
- grub_uint64_t limit_time = grub_get_time_ms () + 4000;
|
|
- void *txbuf;
|
|
-
|
|
- if (dev->txbusy)
|
|
- while (1)
|
|
- {
|
|
- txbuf = NULL;
|
|
- st = efi_call_3 (net->get_status, net, 0, &txbuf);
|
|
- if (st != GRUB_EFI_SUCCESS)
|
|
- return grub_error (GRUB_ERR_IO,
|
|
- N_("couldn't send network packet"));
|
|
- /*
|
|
- Some buggy firmware could return an arbitrary address instead of the
|
|
- txbuf address we trasmitted, so just check that txbuf is non NULL
|
|
- for success. This is ok because we open the SNP protocol in
|
|
- exclusive mode so we know we're the only ones transmitting on this
|
|
- box and since we only transmit one packet at a time we know our
|
|
- transmit was successfull.
|
|
- */
|
|
- if (txbuf)
|
|
- {
|
|
- dev->txbusy = 0;
|
|
- break;
|
|
- }
|
|
- if (limit_time < grub_get_time_ms ())
|
|
- return grub_error (GRUB_ERR_TIMEOUT,
|
|
- N_("couldn't send network packet"));
|
|
- }
|
|
-
|
|
- dev->last_pkt_size = (pack->tail - pack->data);
|
|
- if (dev->last_pkt_size > dev->mtu)
|
|
- dev->last_pkt_size = dev->mtu;
|
|
-
|
|
- grub_memcpy (dev->txbuf, pack->data, dev->last_pkt_size);
|
|
-
|
|
- st = efi_call_7 (net->transmit, net, 0, dev->last_pkt_size,
|
|
- dev->txbuf, NULL, NULL, NULL);
|
|
- if (st != GRUB_EFI_SUCCESS)
|
|
- return grub_error (GRUB_ERR_IO, N_("couldn't send network packet"));
|
|
-
|
|
- /*
|
|
- The card may have sent out the packet immediately - set txbusy
|
|
- to 0 in this case.
|
|
- Cases were observed where checking txbuf at the next call
|
|
- of send_card_buffer() is too late: 0 is returned in txbuf and
|
|
- we run in the GRUB_ERR_TIMEOUT case above.
|
|
- Perhaps a timeout in the FW has discarded the recycle buffer.
|
|
- */
|
|
- txbuf = NULL;
|
|
- st = efi_call_3 (net->get_status, net, 0, &txbuf);
|
|
- dev->txbusy = !(st == GRUB_EFI_SUCCESS && txbuf);
|
|
-
|
|
- return GRUB_ERR_NONE;
|
|
-}
|
|
-
|
|
static struct grub_net_buff *
|
|
get_card_packet (struct grub_net_card *dev)
|
|
{
|
|
@@ -219,6 +158,88 @@ close_card (struct grub_net_card *dev)
|
|
grub_efi_image_handle, dev->efi_handle);
|
|
}
|
|
|
|
+static grub_err_t
|
|
+send_card_buffer (struct grub_net_card *dev,
|
|
+ struct grub_net_buff *pack)
|
|
+{
|
|
+ grub_efi_status_t st;
|
|
+ grub_efi_simple_network_t *net = dev->efi_net;
|
|
+ grub_uint64_t limit_time = grub_get_time_ms () + 4000;
|
|
+ void *txbuf;
|
|
+ grub_err_t ret;
|
|
+ int retry = 0;
|
|
+
|
|
+ if (dev->txbusy)
|
|
+ while (1)
|
|
+ {
|
|
+ txbuf = NULL;
|
|
+ st = efi_call_3 (net->get_status, net, 0, &txbuf);
|
|
+ if (st != GRUB_EFI_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_IO,
|
|
+ N_("couldn't send network packet"));
|
|
+ /*
|
|
+ Some buggy firmware could return an arbitrary address instead of the
|
|
+ txbuf address we trasmitted, so just check that txbuf is non NULL
|
|
+ for success. This is ok because we open the SNP protocol in
|
|
+ exclusive mode so we know we're the only ones transmitting on this
|
|
+ box and since we only transmit one packet at a time we know our
|
|
+ transmit was successfull.
|
|
+ */
|
|
+ if (txbuf)
|
|
+ {
|
|
+ dev->txbusy = 0;
|
|
+ break;
|
|
+ }
|
|
+ if (limit_time < grub_get_time_ms ())
|
|
+ {
|
|
+ if (!retry)
|
|
+ {
|
|
+ close_card (dev);
|
|
+ grub_millisleep (100);
|
|
+ ret = open_card (dev);
|
|
+ if (ret != GRUB_ERR_NONE)
|
|
+ return grub_error (GRUB_ERR_IO,
|
|
+ N_("couldn't open card"));
|
|
+ st = efi_call_7 (net->transmit, net, 0, dev->last_pkt_size,
|
|
+ dev->txbuf, NULL, NULL, NULL);
|
|
+ if (st != GRUB_EFI_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_IO,
|
|
+ N_("couldn't send network packet"));
|
|
+ retry = 1;
|
|
+ grub_uint64_t limit_time = grub_get_time_ms () + 10000;
|
|
+ break;
|
|
+ }
|
|
+ return grub_error (GRUB_ERR_TIMEOUT,
|
|
+ N_("couldn't send network packet"));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev->last_pkt_size = (pack->tail - pack->data);
|
|
+ if (dev->last_pkt_size > dev->mtu)
|
|
+ dev->last_pkt_size = dev->mtu;
|
|
+
|
|
+ grub_memcpy (dev->txbuf, pack->data, dev->last_pkt_size);
|
|
+
|
|
+ st = efi_call_7 (net->transmit, net, 0, dev->last_pkt_size,
|
|
+ dev->txbuf, NULL, NULL, NULL);
|
|
+ if (st != GRUB_EFI_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_IO, N_("couldn't send network packet"));
|
|
+
|
|
+ /*
|
|
+ The card may have sent out the packet immediately - set txbusy
|
|
+ to 0 in this case.
|
|
+ Cases were observed where checking txbuf at the next call
|
|
+ of send_card_buffer() is too late: 0 is returned in txbuf and
|
|
+ we run in the GRUB_ERR_TIMEOUT case above.
|
|
+ Perhaps a timeout in the FW has discarded the recycle buffer.
|
|
+ */
|
|
+ txbuf = NULL;
|
|
+ st = efi_call_3 (net->get_status, net, 0, &txbuf);
|
|
+ dev->txbusy = !(st == GRUB_EFI_SUCCESS && txbuf);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
static struct grub_net_card_driver efidriver =
|
|
{
|
|
.name = "efinet",
|
|
--
|
|
2.43.5
|
|
|