Laszlo, After I sent the mail to propose changing the CpuIo driver to improve IO performance, I now saw your patch sent earlier than my last mail. A very interesting feeling.
Reviewed-by: Ruiyu Ni <ruiyu...@intel.com> Regards, Ray >-----Original Message----- >From: edk2-devel [mailto:edk2-devel-boun...@lists.01.org] On Behalf Of Laszlo >Ersek >Sent: Friday, April 8, 2016 5:52 AM >To: edk2-devel-01 <edk2-de...@ml01.01.org> >Cc: Ni, Ruiyu <ruiyu...@intel.com>; Justen, Jordan L ><jordan.l.jus...@intel.com>; Fan, Jeff <jeff....@intel.com>; Mark ><kram...@gmail.com> >Subject: [edk2] [PATCH] UefiCpuPkg: CpuIo2Dxe: optimize FIFO reads and writes >of IO ports > >* Short description: > > The CpuIoServiceRead() and CpuIoServiceWrite() functions transfer data > between memory and IO ports with individual Io(Read|Write)(8|16|32) > function calls, each in an appropriately set up loop. > > On the Ia32 and X64 platforms however, FIFO reads and writes can be > optimized, by coding them in assembly, and delegating the loop to the > CPU, with the REP prefix. > > On KVM virtualization hosts, this difference has a huge performance > impact: if the loop is open-coded, then the virtual machine traps to the > hypervisor on every single UINT8 / UINT16 / UINT32 transfer, whereas > with the REP prefix, KVM can transfer up to a page of data per VM trap. > This is especially noticeable with IDE PIO transfers, where all the data > are squeezed through IO ports. > >* Long description: > > The RootBridgeIoIoRW() function in > > PcAtChipsetPkg/PciHostBridgeDxe/PciRootBridgeIo.c > > used to have the exact same IO port acces optimization, dating back > verbatim to commit 1fd376d9792: > > PcAtChipsetPkg/PciHostBridgeDxe: Improve KVM FIFO I/O read/write > performance > > OvmfPkg cloned the "PcAtChipsetPkg/PciHostBridgeDxe" driver (for > unrelated reasons), and inherited the optimization from PcAtChipsetPkg. > > The "PcAtChipsetPkg/PciHostBridgeDxe" driver was ultimately removed in > commit 111d79db47: > > PcAtChipsetPkg/PciHostBridge: Remove PciHostBridge driver > > and OvmfPkg too was rebased to the new core Pci Host Bridge Driver, in > commit 4014885ffd: > > OvmfPkg: switch to MdeModulePkg/Bus/Pci/PciHostBridgeDxe > > This caused the optimization to go lost. Namely, the > RootBridgeIoIoRead() and RootBridgeIoIoWrite() functions in the new core > Pci Host Bridge Driver delegate IO port accesses to > EFI_CPU_IO2_PROTOCOL. And, in OvmfPkg (and likely most other Ia32 / X64 > edk2 platforms), this protocol is provided by "UefiCpuPkg/CpuIo2Dxe", > which lacks the optimization. > > Therefore, this patch ports the C source code logic from commit > 1fd376d9792 (see above) to "UefiCpuPkg/CpuIo2Dxe", plus it ports the > NASM-converted assembly helper functions from OvmfPkg commits > 6026bf460037 and ace1d0517b65: > > OvmfPkg PciHostBridgeDxe: Convert Ia32/IoFifo.asm to NASM > > OvmfPkg PciHostBridgeDxe: Convert X64/IoFifo.asm to NASM > >* Notes about the port: > > - The write and read branches from commit 1fd376d9792 are split to the > separate functions CpuIoServiceWrite() and CpuIoServiceRead(). > > - The EfiPciWidthUintXX constants are replaced with EfiCpuIoWidthUintXX. > > - The cast expression "(UINTN) Address" is replaced with > "(UINTN)Address" (i.e., no space), because that's how the receiving > functions spell it as well. > > - The labels in the switch statements are unindented by one level, to > match the edk2 coding style (and the rest of UefiCpuPkg) better. > >* The first signoff belongs to Jordan, because he authored all of > 1fd376d9792, 6026bf460037 and ace1d0517b65. > >Contributed-under: TianoCore Contribution Agreement 1.0 >Signed-off-by: Jordan Justen <jordan.l.jus...@intel.com> >Contributed-under: TianoCore Contribution Agreement 1.0 >Signed-off-by: Laszlo Ersek <ler...@redhat.com> >Ref: https://www.redhat.com/archives/vfio-users/2016-April/msg00029.html >Reported-by: Mark <kram...@gmail.com> >Ref: http://thread.gmane.org/gmane.comp.bios.edk2.devel/10424/focus=10432 >Reported-by: Jordan Justen <jordan.l.jus...@intel.com> >Cc: Jordan Justen <jordan.l.jus...@intel.com> >Cc: Ruiyu Ni <ruiyu...@intel.com> >Cc: Jeff Fan <jeff....@intel.com> >Cc: Mark <kram...@gmail.com> >--- > UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf | 7 + > UefiCpuPkg/CpuIo2Dxe/IoFifo.h | 176 ++++++++++++++++++++ > UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c | 49 ++++++ > UefiCpuPkg/CpuIo2Dxe/Ia32/IoFifo.nasm | 136 +++++++++++++++ > UefiCpuPkg/CpuIo2Dxe/X64/IoFifo.nasm | 125 ++++++++++++++ > 5 files changed, 493 insertions(+) > >diff --git a/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf >b/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf >index 8ef8b3d31cff..be79b1b3b992 100644 >--- a/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf >+++ b/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf >@@ -30,7 +30,14 @@ [Defines] > [Sources] > CpuIo2Dxe.c > CpuIo2Dxe.h >+ IoFifo.h > >+[Sources.IA32] >+ Ia32/IoFifo.nasm >+ >+[Sources.X64] >+ X64/IoFifo.nasm >+ > [Packages] > MdePkg/MdePkg.dec > >diff --git a/UefiCpuPkg/CpuIo2Dxe/IoFifo.h b/UefiCpuPkg/CpuIo2Dxe/IoFifo.h >new file mode 100644 >index 000000000000..9978f8bfc39a >--- /dev/null >+++ b/UefiCpuPkg/CpuIo2Dxe/IoFifo.h >@@ -0,0 +1,176 @@ >+/** @file >+ I/O FIFO routines >+ >+ Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR> >+ >+ This program and the accompanying materials are licensed and made available >+ under the terms and conditions of the BSD License which accompanies this >+ distribution. The full text of the license may be found at >+ http://opensource.org/licenses/bsd-license.php >+ >+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, >+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR >IMPLIED. >+ >+**/ >+ >+#ifndef _IO_FIFO_H_INCLUDED_ >+#define _IO_FIFO_H_INCLUDED_ >+ >+/** >+ Reads an 8-bit I/O port fifo into a block of memory. >+ >+ Reads the 8-bit I/O fifo port specified by Port. >+ >+ The port is read Count times, and the read data is >+ stored in the provided Buffer. >+ >+ This function must guarantee that all I/O read and write operations are >+ serialized. >+ >+ If 8-bit I/O port operations are not supported, then ASSERT(). >+ >+ @param Port The I/O port to read. >+ @param Count The number of times to read I/O port. >+ @param Buffer The buffer to store the read data into. >+ >+**/ >+VOID >+EFIAPI >+IoReadFifo8 ( >+ IN UINTN Port, >+ IN UINTN Count, >+ OUT VOID *Buffer >+ ); >+ >+/** >+ Reads a 16-bit I/O port fifo into a block of memory. >+ >+ Reads the 16-bit I/O fifo port specified by Port. >+ >+ The port is read Count times, and the read data is >+ stored in the provided Buffer. >+ >+ This function must guarantee that all I/O read and write operations are >+ serialized. >+ >+ If 16-bit I/O port operations are not supported, then ASSERT(). >+ >+ @param Port The I/O port to read. >+ @param Count The number of times to read I/O port. >+ @param Buffer The buffer to store the read data into. >+ >+**/ >+VOID >+EFIAPI >+IoReadFifo16 ( >+ IN UINTN Port, >+ IN UINTN Count, >+ OUT VOID *Buffer >+ ); >+ >+/** >+ Reads a 32-bit I/O port fifo into a block of memory. >+ >+ Reads the 32-bit I/O fifo port specified by Port. >+ >+ The port is read Count times, and the read data is >+ stored in the provided Buffer. >+ >+ This function must guarantee that all I/O read and write operations are >+ serialized. >+ >+ If 32-bit I/O port operations are not supported, then ASSERT(). >+ >+ @param Port The I/O port to read. >+ @param Count The number of times to read I/O port. >+ @param Buffer The buffer to store the read data into. >+ >+**/ >+VOID >+EFIAPI >+IoReadFifo32 ( >+ IN UINTN Port, >+ IN UINTN Count, >+ OUT VOID *Buffer >+ ); >+ >+/** >+ Writes a block of memory into an 8-bit I/O port fifo. >+ >+ Writes the 8-bit I/O fifo port specified by Port. >+ >+ The port is written Count times, and the write data is >+ retrieved from the provided Buffer. >+ >+ This function must guarantee that all I/O write and write operations are >+ serialized. >+ >+ If 8-bit I/O port operations are not supported, then ASSERT(). >+ >+ @param Port The I/O port to write. >+ @param Count The number of times to write I/O port. >+ @param Buffer The buffer to store the write data into. >+ >+**/ >+VOID >+EFIAPI >+IoWriteFifo8 ( >+ IN UINTN Port, >+ IN UINTN Count, >+ OUT VOID *Buffer >+ ); >+ >+/** >+ Writes a block of memory into a 16-bit I/O port fifo. >+ >+ Writes the 16-bit I/O fifo port specified by Port. >+ >+ The port is written Count times, and the write data is >+ retrieved from the provided Buffer. >+ >+ This function must guarantee that all I/O write and write operations are >+ serialized. >+ >+ If 16-bit I/O port operations are not supported, then ASSERT(). >+ >+ @param Port The I/O port to write. >+ @param Count The number of times to write I/O port. >+ @param Buffer The buffer to store the write data into. >+ >+**/ >+VOID >+EFIAPI >+IoWriteFifo16 ( >+ IN UINTN Port, >+ IN UINTN Count, >+ OUT VOID *Buffer >+ ); >+ >+/** >+ Writes a block of memory into a 32-bit I/O port fifo. >+ >+ Writes the 32-bit I/O fifo port specified by Port. >+ >+ The port is written Count times, and the write data is >+ retrieved from the provided Buffer. >+ >+ This function must guarantee that all I/O write and write operations are >+ serialized. >+ >+ If 32-bit I/O port operations are not supported, then ASSERT(). >+ >+ @param Port The I/O port to write. >+ @param Count The number of times to write I/O port. >+ @param Buffer The buffer to store the write data into. >+ >+**/ >+VOID >+EFIAPI >+IoWriteFifo32 ( >+ IN UINTN Port, >+ IN UINTN Count, >+ OUT VOID *Buffer >+ ); >+ >+#endif >+ >diff --git a/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c >b/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c >index 3d8a79923025..6ccfc40e10f8 100644 >--- a/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c >+++ b/UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.c >@@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER >EXPRESS OR IMPLIED. > **/ > > #include "CpuIo2Dxe.h" >+#include "IoFifo.h" > > // > // Handle for the CPU I/O 2 Protocol >@@ -410,6 +411,30 @@ CpuIoServiceRead ( > InStride = mInStride[Width]; > OutStride = mOutStride[Width]; > OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); >+ >+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) >+ if (InStride == 0) { >+ switch (OperationWidth) { >+ case EfiCpuIoWidthUint8: >+ IoReadFifo8 ((UINTN)Address, Count, Buffer); >+ return EFI_SUCCESS; >+ case EfiCpuIoWidthUint16: >+ IoReadFifo16 ((UINTN)Address, Count, Buffer); >+ return EFI_SUCCESS; >+ case EfiCpuIoWidthUint32: >+ IoReadFifo32 ((UINTN)Address, Count, Buffer); >+ return EFI_SUCCESS; >+ default: >+ // >+ // The CpuIoCheckParameter call above will ensure that this >+ // path is not taken. >+ // >+ ASSERT (FALSE); >+ break; >+ } >+ } >+#endif >+ > for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += > OutStride, Count--) { > if (OperationWidth == EfiCpuIoWidthUint8) { > *Uint8Buffer = IoRead8 ((UINTN)Address); >@@ -492,6 +517,30 @@ CpuIoServiceWrite ( > InStride = mInStride[Width]; > OutStride = mOutStride[Width]; > OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH) (Width & 0x03); >+ >+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) >+ if (InStride == 0) { >+ switch (OperationWidth) { >+ case EfiCpuIoWidthUint8: >+ IoWriteFifo8 ((UINTN)Address, Count, Buffer); >+ return EFI_SUCCESS; >+ case EfiCpuIoWidthUint16: >+ IoWriteFifo16 ((UINTN)Address, Count, Buffer); >+ return EFI_SUCCESS; >+ case EfiCpuIoWidthUint32: >+ IoWriteFifo32 ((UINTN)Address, Count, Buffer); >+ return EFI_SUCCESS; >+ default: >+ // >+ // The CpuIoCheckParameter call above will ensure that this >+ // path is not taken. >+ // >+ ASSERT (FALSE); >+ break; >+ } >+ } >+#endif >+ > for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, > Uint8Buffer += OutStride, Count--) { > if (OperationWidth == EfiCpuIoWidthUint8) { > IoWrite8 ((UINTN)Address, *Uint8Buffer); >diff --git a/UefiCpuPkg/CpuIo2Dxe/Ia32/IoFifo.nasm >b/UefiCpuPkg/CpuIo2Dxe/Ia32/IoFifo.nasm >new file mode 100644 >index 000000000000..daa90a99af37 >--- /dev/null >+++ b/UefiCpuPkg/CpuIo2Dxe/Ia32/IoFifo.nasm >@@ -0,0 +1,136 @@ >+;------------------------------------------------------------------------------ >+; >+; Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR> >+; >+; This program and the accompanying materials are licensed and made available >+; under the terms and conditions of the BSD License which accompanies this >+; distribution. The full text of the license may be found at >+; http://opensource.org/licenses/bsd-license.php. >+; >+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, >+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR >IMPLIED. >+; >+;------------------------------------------------------------------------------ >+ >+ SECTION .text >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoReadFifo8 ( >+; IN UINTN Port, >+; IN UINTN Size, >+; IN VOID *Buffer >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoReadFifo8) >+ASM_PFX(IoReadFifo8): >+ push edi >+ cld >+ mov dx, [esp + 8] >+ mov ecx, [esp + 12] >+ mov edi, [esp + 16] >+rep insb >+ pop edi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoReadFifo16 ( >+; IN UINTN Port, >+; IN UINTN Size, >+; IN VOID *Buffer >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoReadFifo16) >+ASM_PFX(IoReadFifo16): >+ push edi >+ cld >+ mov dx, [esp + 8] >+ mov ecx, [esp + 12] >+ mov edi, [esp + 16] >+rep insw >+ pop edi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoReadFifo32 ( >+; IN UINTN Port, >+; IN UINTN Size, >+; IN VOID *Buffer >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoReadFifo32) >+ASM_PFX(IoReadFifo32): >+ push edi >+ cld >+ mov dx, [esp + 8] >+ mov ecx, [esp + 12] >+ mov edi, [esp + 16] >+rep insd >+ pop edi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoWriteFifo8 ( >+; IN UINTN Port, >+; IN UINTN Size, >+; IN VOID *Buffer >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoWriteFifo8) >+ASM_PFX(IoWriteFifo8): >+ push esi >+ cld >+ mov dx, [esp + 8] >+ mov ecx, [esp + 12] >+ mov esi, [esp + 16] >+rep outsb >+ pop esi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoWriteFifo16 ( >+; IN UINTN Port, >+; IN UINTN Size, >+; IN VOID *Buffer >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoWriteFifo16) >+ASM_PFX(IoWriteFifo16): >+ push esi >+ cld >+ mov dx, [esp + 8] >+ mov ecx, [esp + 12] >+ mov esi, [esp + 16] >+rep outsw >+ pop esi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoWriteFifo32 ( >+; IN UINTN Port, >+; IN UINTN Size, >+; IN VOID *Buffer >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoWriteFifo32) >+ASM_PFX(IoWriteFifo32): >+ push esi >+ cld >+ mov dx, [esp + 8] >+ mov ecx, [esp + 12] >+ mov esi, [esp + 16] >+rep outsd >+ pop esi >+ ret >+ >diff --git a/UefiCpuPkg/CpuIo2Dxe/X64/IoFifo.nasm >b/UefiCpuPkg/CpuIo2Dxe/X64/IoFifo.nasm >new file mode 100644 >index 000000000000..bb3d1da67af5 >--- /dev/null >+++ b/UefiCpuPkg/CpuIo2Dxe/X64/IoFifo.nasm >@@ -0,0 +1,125 @@ >+;------------------------------------------------------------------------------ >+; >+; Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR> >+; >+; This program and the accompanying materials are licensed and made available >+; under the terms and conditions of the BSD License which accompanies this >+; distribution. The full text of the license may be found at >+; http://opensource.org/licenses/bsd-license.php. >+; >+; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, >+; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR >IMPLIED. >+; >+;------------------------------------------------------------------------------ >+ >+ DEFAULT REL >+ SECTION .text >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoReadFifo8 ( >+; IN UINTN Port, // rcx >+; IN UINTN Size, // rdx >+; IN VOID *Buffer // r8 >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoReadFifo8) >+ASM_PFX(IoReadFifo8): >+ cld >+ xchg rcx, rdx >+ xchg rdi, r8 ; rdi: buffer address; r8: save rdi >+rep insb >+ mov rdi, r8 ; restore rdi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoReadFifo16 ( >+; IN UINTN Port, // rcx >+; IN UINTN Size, // rdx >+; IN VOID *Buffer // r8 >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoReadFifo16) >+ASM_PFX(IoReadFifo16): >+ cld >+ xchg rcx, rdx >+ xchg rdi, r8 ; rdi: buffer address; r8: save rdi >+rep insw >+ mov rdi, r8 ; restore rdi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoReadFifo32 ( >+; IN UINTN Port, // rcx >+; IN UINTN Size, // rdx >+; IN VOID *Buffer // r8 >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoReadFifo32) >+ASM_PFX(IoReadFifo32): >+ cld >+ xchg rcx, rdx >+ xchg rdi, r8 ; rdi: buffer address; r8: save rdi >+rep insd >+ mov rdi, r8 ; restore rdi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoWriteFifo8 ( >+; IN UINTN Port, // rcx >+; IN UINTN Size, // rdx >+; IN VOID *Buffer // r8 >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoWriteFifo8) >+ASM_PFX(IoWriteFifo8): >+ cld >+ xchg rcx, rdx >+ xchg rsi, r8 ; rsi: buffer address; r8: save rsi >+rep outsb >+ mov rsi, r8 ; restore rsi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoWriteFifo16 ( >+; IN UINTN Port, // rcx >+; IN UINTN Size, // rdx >+; IN VOID *Buffer // r8 >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoWriteFifo16) >+ASM_PFX(IoWriteFifo16): >+ cld >+ xchg rcx, rdx >+ xchg rsi, r8 ; rsi: buffer address; r8: save rsi >+rep outsw >+ mov rsi, r8 ; restore rsi >+ ret >+ >+;------------------------------------------------------------------------------ >+; VOID >+; EFIAPI >+; IoWriteFifo32 ( >+; IN UINTN Port, // rcx >+; IN UINTN Size, // rdx >+; IN VOID *Buffer // r8 >+; ); >+;------------------------------------------------------------------------------ >+global ASM_PFX(IoWriteFifo32) >+ASM_PFX(IoWriteFifo32): >+ cld >+ xchg rcx, rdx >+ xchg rsi, r8 ; rsi: buffer address; r8: save rsi >+rep outsd >+ mov rsi, r8 ; restore rsi >+ ret >+ >-- >1.8.3.1 > >_______________________________________________ >edk2-devel mailing list >edk2-devel@lists.01.org >https://lists.01.org/mailman/listinfo/edk2-devel _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel