public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Sunil V L" <sunilvl@ventanamicro.com>
To: devel@edk2.groups.io
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>,
	Leif Lindholm <quic_llindhol@quicinc.com>,
	Jiewen Yao <jiewen.yao@intel.com>,
	Jordan Justen <jordan.l.justen@intel.com>,
	Gerd Hoffmann <kraxel@redhat.com>
Subject: [edk2-staging/RiscV64QemuVirt PATCH V4 31/34] OvmfPkg/NorFlashDxe: Avoid switching between modes in a tight loop
Date: Fri, 14 Oct 2022 22:18:33 +0530	[thread overview]
Message-ID: <20221014164836.1513036-32-sunilvl@ventanamicro.com> (raw)
In-Reply-To: <20221014164836.1513036-1-sunilvl@ventanamicro.com>

Currently, when dealing with small updates that can be written out
directly (i.e., if they only involve clearing bits and not setting bits,
as the latter requires a block level erase), we iterate over the data
one word at a time, read the old value, compare it, write the new value,
and repeat, unless we encountered a value that we cannot write (0->1
transition), in which case we fall back to a block level operation.

This is inefficient for two reasons:
- reading and writing a word at a time involves switching between array
  and programming mode for every word of data, which is
  disproportionately costly when running under KVM;
- we end up writing some data twice, as we may not notice that a block
  erase is needed until after some data has been written to flash.

So replace this sequence with a single read of up to twice the buffered
write maximum size, followed by one or two buffered writes if the data
can be written directly. Otherwise, fall back to the existing block
level sequence, but without writing out part of the data twice.

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 OvmfPkg/Drivers/NorFlashDxe/NorFlash.c | 197 +++++++-------------
 1 file changed, 68 insertions(+), 129 deletions(-)

diff --git a/OvmfPkg/Drivers/NorFlashDxe/NorFlash.c b/OvmfPkg/Drivers/NorFlashDxe/NorFlash.c
index cdc6b5da8bfb..649e0789db3c 100644
--- a/OvmfPkg/Drivers/NorFlashDxe/NorFlash.c
+++ b/OvmfPkg/Drivers/NorFlashDxe/NorFlash.c
@@ -582,20 +582,11 @@ NorFlashWriteSingleBlock (
   IN        UINT8               *Buffer
   )
 {
-  EFI_STATUS  TempStatus;
-  UINT32      Tmp;
-  UINT32      TmpBuf;
-  UINT32      WordToWrite;
-  UINT32      Mask;
-  BOOLEAN     DoErase;
-  UINTN       BytesToWrite;
+  EFI_STATUS  Status;
   UINTN       CurOffset;
-  UINTN       WordAddr;
   UINTN       BlockSize;
   UINTN       BlockAddress;
-  UINTN       PrevBlockAddress;
-
-  PrevBlockAddress = 0;
+  UINT8       *OrigData;
 
   DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
 
@@ -606,6 +597,12 @@ NorFlashWriteSingleBlock (
     return EFI_ACCESS_DENIED;
   }
 
+  // Check we did get some memory. Buffer is BlockSize.
+  if (Instance->ShadowBuffer == NULL) {
+    DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
+    return EFI_DEVICE_ERROR;
+  }
+
   // Cache the block size to avoid de-referencing pointers all the time
   BlockSize = Instance->Media.BlockSize;
 
@@ -625,149 +622,91 @@ NorFlashWriteSingleBlock (
     return EFI_BAD_BUFFER_SIZE;
   }
 
-  // Pick 128bytes as a good start for word operations as opposed to erasing the
-  // block and writing the data regardless if an erase is really needed.
-  // It looks like most individual NV variable writes are smaller than 128bytes.
-  if (*NumBytes <= 128) {
+  // Pick P30_MAX_BUFFER_SIZE_IN_BYTES (== 128 bytes) as a good start for word
+  // operations as opposed to erasing the block and writing the data regardless
+  // if an erase is really needed.  It looks like most individual NV variable
+  // writes are smaller than 128 bytes.
+  // To avoid pathological cases were a 2 byte write is disregarded because it
+  // occurs right at a 128 byte buffered write alignment boundary, permit up to
+  // twice the max buffer size, and perform two writes if needed.
+  if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) {
     // Check to see if we need to erase before programming the data into NOR.
     // If the destination bits are only changing from 1s to 0s we can just write.
     // After a block is erased all bits in the block is set to 1.
     // If any byte requires us to erase we just give up and rewrite all of it.
-    DoErase      = FALSE;
-    BytesToWrite = *NumBytes;
-    CurOffset    = Offset;
 
-    while (BytesToWrite > 0) {
-      // Read full word from NOR, splice as required. A word is the smallest
-      // unit we can write.
-      TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof (Tmp), &Tmp);
-      if (EFI_ERROR (TempStatus)) {
-        return EFI_DEVICE_ERROR;
-      }
-
-      // Physical address of word in NOR to write.
-      WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (
-                                          Instance->RegionBaseAddress,
+    // Read the old version of the data into the shadow buffer
+    Status = NorFlashRead (
+               Instance,
                Lba,
-                                          BlockSize
+               Offset & ~BOUNDARY_OF_32_WORDS,
+               (*NumBytes | BOUNDARY_OF_32_WORDS) + 1,
+               Instance->ShadowBuffer
                );
-      // The word of data that is to be written.
-      TmpBuf = *((UINT32 *)(Buffer + (*NumBytes - BytesToWrite)));
-
-      // First do word aligned chunks.
-      if ((CurOffset & 0x3) == 0) {
-        if (BytesToWrite >= 4) {
-          // Is the destination still in 'erased' state?
-          if (~Tmp != 0) {
-            // Check to see if we are only changing bits to zero.
-            if ((Tmp ^ TmpBuf) & TmpBuf) {
-              DoErase = TRUE;
-              break;
-            }
+    if (EFI_ERROR (Status)) {
+      return EFI_DEVICE_ERROR;
     }
 
-          // Write this word to NOR
-          WordToWrite   = TmpBuf;
-          CurOffset    += sizeof (TmpBuf);
-          BytesToWrite -= sizeof (TmpBuf);
-        } else {
-          // BytesToWrite < 4. Do small writes and left-overs
-          Mask = ~((~0) << (BytesToWrite * 8));
-          // Mask out the bytes we want.
-          TmpBuf &= Mask;
-          // Is the destination still in 'erased' state?
-          if ((Tmp & Mask) != Mask) {
-            // Check to see if we are only changing bits to zero.
-            if ((Tmp ^ TmpBuf) & TmpBuf) {
-              DoErase = TRUE;
-              break;
-            }
-          }
+    // Make OrigData point to the start of the old version of the data inside
+    // the word aligned buffer
+    OrigData = Instance->ShadowBuffer + (Offset & BOUNDARY_OF_32_WORDS);
 
-          // Merge old and new data. Write merged word to NOR
-          WordToWrite  = (Tmp & ~Mask) | TmpBuf;
-          CurOffset   += BytesToWrite;
-          BytesToWrite = 0;
-        }
-      } else {
-        // Do multiple words, but starting unaligned.
-        if (BytesToWrite > (4 - (CurOffset & 0x3))) {
-          Mask = ((~0) << ((CurOffset & 0x3) * 8));
-          // Mask out the bytes we want.
-          TmpBuf &= Mask;
-          // Is the destination still in 'erased' state?
-          if ((Tmp & Mask) != Mask) {
-            // Check to see if we are only changing bits to zero.
-            if ((Tmp ^ TmpBuf) & TmpBuf) {
-              DoErase = TRUE;
-              break;
-            }
+    // Update the buffer containing the old version of the data with the new
+    // contents, while checking whether the old version had any bits cleared
+    // that we want to set. In that case, we will need to erase the block first.
+    for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) {
+      if (~OrigData[CurOffset] & Buffer[CurOffset]) {
+        goto DoErase;
       }
 
-          // Merge old and new data. Write merged word to NOR
-          WordToWrite   = (Tmp & ~Mask) | TmpBuf;
-          BytesToWrite -= (4 - (CurOffset & 0x3));
-          CurOffset    += (4 - (CurOffset & 0x3));
-        } else {
-          // Unaligned and fits in one word.
-          Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);
-          // Mask out the bytes we want.
-          TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;
-          // Is the destination still in 'erased' state?
-          if ((Tmp & Mask) != Mask) {
-            // Check to see if we are only changing bits to zero.
-            if ((Tmp ^ TmpBuf) & TmpBuf) {
-              DoErase = TRUE;
-              break;
-            }
-          }
-
-          // Merge old and new data. Write merged word to NOR
-          WordToWrite  = (Tmp & ~Mask) | TmpBuf;
-          CurOffset   += BytesToWrite;
-          BytesToWrite = 0;
-        }
+      OrigData[CurOffset] = Buffer[CurOffset];
     }
 
     //
-      // Write the word to NOR.
+    // Write the updated buffer to NOR.
     //
-
     BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
-      if (BlockAddress != PrevBlockAddress) {
-        TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
-        if (EFI_ERROR (TempStatus)) {
-          return EFI_DEVICE_ERROR;
+
+    // Unlock the block if we have to
+    Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
+    if (EFI_ERROR (Status)) {
+      goto Exit;
+    }
+
+    Status = NorFlashWriteBuffer (
+               Instance,
+               BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS),
+               P30_MAX_BUFFER_SIZE_IN_BYTES,
+               Instance->ShadowBuffer
+               );
+    if (EFI_ERROR (Status)) {
+      goto Exit;
     }
 
-        PrevBlockAddress = BlockAddress;
+    if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) > P30_MAX_BUFFER_SIZE_IN_BYTES) {
+      BlockAddress += P30_MAX_BUFFER_SIZE_IN_BYTES;
+      Status        = NorFlashWriteBuffer (
+                        Instance,
+                        BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS),
+                        P30_MAX_BUFFER_SIZE_IN_BYTES,
+                        Instance->ShadowBuffer + P30_MAX_BUFFER_SIZE_IN_BYTES
+                        );
+      if (EFI_ERROR (Status)) {
+        goto Exit;
+      }
     }
 
-      TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);
+Exit:
     // Put device back into Read Array mode
     SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
 
-      if (EFI_ERROR (TempStatus)) {
-        return EFI_DEVICE_ERROR;
-      }
-    }
-
-    // Exit if we got here and could write all the data. Otherwise do the
-    // Erase-Write cycle.
-    if (!DoErase) {
-      return EFI_SUCCESS;
-    }
-  }
-
-  // Check we did get some memory. Buffer is BlockSize.
-  if (Instance->ShadowBuffer == NULL) {
-    DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
-    return EFI_DEVICE_ERROR;
+    return Status;
   }
 
+DoErase:
   // Read NOR Flash data into shadow buffer
-  TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
-  if (EFI_ERROR (TempStatus)) {
+  Status = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
+  if (EFI_ERROR (Status)) {
     // Return one of the pre-approved error statuses
     return EFI_DEVICE_ERROR;
   }
@@ -776,8 +715,8 @@ NorFlashWriteSingleBlock (
   CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
 
   // Write the modified buffer back to the NorFlash
-  TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
-  if (EFI_ERROR (TempStatus)) {
+  Status = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
+  if (EFI_ERROR (Status)) {
     // Return one of the pre-approved error statuses
     return EFI_DEVICE_ERROR;
   }
-- 
2.38.0


  parent reply	other threads:[~2022-10-14 16:50 UTC|newest]

Thread overview: 62+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-14 16:48 [edk2-staging/RiscV64QemuVirt PATCH V4 00/34] Add support for RISC-V virt machine Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 01/34] MdePkg/Register: Add register definition header files for RISC-V Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 02/34] MdePkg: Add RISCV_EFI_BOOT_PROTOCOL related definitions Sunil V L
2022-10-15 13:28   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 03/34] MdePkg/BaseLib: RISC-V: Add few more helper functions Sunil V L
2022-10-15 13:30   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 04/34] MdePkg: Add BaseRiscVSbiLib Library for RISC-V Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 05/34] OvmfPkg/PlatformInitLib: Refactor to allow other architectures Sunil V L
2022-10-15 13:39   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 06/34] OvmfPkg/PlatformInitLib: Add support for RISC-V Sunil V L
2022-10-15 13:35   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 07/34] OvmfPkg/ResetSystemLib: Refactor to allow other architectures Sunil V L
2022-10-15 13:37   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 08/34] OvmfPkg/ResetSystemLib: Add support for RISC-V Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 09/34] OvmfPkg/Sec: Refactor to allow other architectures Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 10/34] OvmfPkg/Sec: Add RISC-V support Sunil V L
2022-10-15 15:50   ` [edk2-devel] " Chang, Abner
2022-10-17  4:53     ` Sunil V L
2022-10-17 11:26       ` Chang, Abner
2022-10-17 11:32   ` Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 11/34] OvmfPkg/PlatformPei: Refactor to allow other architectures Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 12/34] OvmfPkg/PlatformPei: Add support for RISC-V Sunil V L
2022-10-15 16:13   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 13/34] UefiCpuPkg/CpuTimerLib: Refactor to allow other architectures Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 14/34] UefiCpuPkg/CpuTimerLib: Add support for RISC-V Sunil V L
2022-10-16 13:56   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 15/34] UefiCpuPkg/CpuExceptionHandlerLib: Refactor to allow other architectures Sunil V L
2022-10-16 14:00   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 16/34] UefiCpuPkg/CpuExceptionHandlerLib: Add support for RISC-V Sunil V L
2022-10-16 14:06   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 17/34] UefiCpuPkg/CpuDxe: Refactor to allow other architectures Sunil V L
2022-10-16 14:41   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 18/34] UefiCpuPkg/CpuDxe: Add support for RISC-V Sunil V L
2022-10-16 14:26   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 19/34] UefiCpuPkg/CpuDxe: Add RISCV_EFI_BOOT_PROTOCOL support Sunil V L
2022-10-16 14:48   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 20/34] UefiCpuPkg: Add CpuTimerDxe module Sunil V L
2022-10-16 14:53   ` [edk2-devel] " Chang, Abner
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 21/34] ArmVirtPkg/PlatformHasAcpiDtDxe: Move to OvmfPkg Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 22/34] ArmVirtPkg: Fix up the location of PlatformHasAcpiDtDxe Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 23/34] ArmVirtPkg/PlatformBootManagerLib: Move to OvmfPkg Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 24/34] ArmVirtPkg: Fix up the paths to PlatformBootManagerLib Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 25/34] ArmPlatformPkg/NorFlashPlatformLib.h:Move to MdePkg Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 26/34] EmbeddedPkg/NvVarStoreFormattedLib: Migrate to MdeModulePkg Sunil V L
2022-10-19  8:54   ` Ard Biesheuvel
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 27/34] ArmVirtPkg: Update the references to NvVarStoreFormattedLib Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 28/34] OvmfPkg: Add NorFlashQemuLib library Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 29/34] OvmfPkg: Add Qemu NOR flash DXE driver Sunil V L
2022-10-19  9:00   ` Ard Biesheuvel
2022-10-19 12:41     ` Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 30/34] OvmfPkg/NorFlashDxe: Avoid switching to array mode during writes Sunil V L
2022-10-14 16:48 ` Sunil V L [this message]
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 32/34] OvmfPkg: RiscVVirt: Add Qemu Virt platform support Sunil V L
2022-10-22  7:16   ` [edk2-devel] " Chang, Abner
2022-10-24  7:47     ` Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 33/34] Maintainers.txt: Add entry for OvmfPkg/RiscVVirt Sunil V L
2022-10-14 16:48 ` [edk2-staging/RiscV64QemuVirt PATCH V4 34/34] UefiCpuPkg/UefiCpuPkg.ci.yaml: Ignore RISC-V file Sunil V L
2022-10-22  7:01   ` [edk2-devel] " Chang, Abner
2022-10-14 16:50 ` [edk2-devel] [edk2-staging/RiscV64QemuVirt PATCH V4 00/34] Add support for RISC-V virt machine Ard Biesheuvel
2022-10-14 17:14   ` Sunil V L
2022-10-15  2:33     ` Chang, Abner
     [not found]     ` <171E1D672CB73789.10474@groups.io>
2022-10-22  7:21       ` Chang, Abner

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-list 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=20221014164836.1513036-32-sunilvl@ventanamicro.com \
    --to=devel@edk2.groups.io \
    /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