2 comments below.

Reviewed-by: Jaben Carsey <jaben.car...@intel.com>

> -----Original Message-----
> From: Qiu, Shumin
> Sent: Sunday, April 10, 2016 5:55 AM
> To: edk2-devel@lists.01.org
> Cc: Qiu, Shumin <shumin....@intel.com>; Carsey, Jaben
> <jaben.car...@intel.com>; Ni, Ruiyu <ruiyu...@intel.com>
> Subject: [PATCH] ShellPkg : Cache the environment variable into memory to
> enhance the performance.
> Importance: High
> 
> Currently UEFI Shell reads variable storage to get the environment
> variables every time running a new command. And reading(writing)
> UEFI variables is a high cost operation on most platforms. In order
> to enhance the performance this patch read the variable storage once
> and cache the environment variables in memory. Every further 'set'
> command will save the variable not only to Shell cache, but also the
> flash variable storage.
> 
> Cc: Jaben Carsey <jaben.car...@intel.com>
> Cc: Ruiyu Ni <ruiyu...@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.0
> Signed-off-by: Qiu Shumin <shumin....@intel.com>
> ---
>  ShellPkg/Application/Shell/Shell.c         |   4 +
>  ShellPkg/Application/Shell/ShellEnvVar.c   | 175
> ++++++++++++++++++++++++++++-
>  ShellPkg/Application/Shell/ShellEnvVar.h   |  82 +++++++++++++-
>  ShellPkg/Application/Shell/ShellProtocol.c | 101 ++++++++++-------
>  4 files changed, 317 insertions(+), 45 deletions(-)
> 
> diff --git a/ShellPkg/Application/Shell/Shell.c
> b/ShellPkg/Application/Shell/Shell.c
> index bd695a4..12daff9 100644
> --- a/ShellPkg/Application/Shell/Shell.c
> +++ b/ShellPkg/Application/Shell/Shell.c
> @@ -445,6 +445,8 @@ UefiMain (
>      Status = CommandInit();
>      ASSERT_EFI_ERROR(Status);
> 
> +    Status = ShellInitEnvVarList ();
> +
>      //
>      // Check the command line
>      //
> @@ -702,6 +704,8 @@ FreeResources:
>      DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;);
>    }
> 
> +  ShellFreeEnvVarList ();
> +
>    if (ShellCommandGetExit()) {
>      return ((EFI_STATUS)ShellCommandGetExitCode());
>    }
> diff --git a/ShellPkg/Application/Shell/ShellEnvVar.c
> b/ShellPkg/Application/Shell/ShellEnvVar.c
> index a8f177e..d2c721d 100644
> --- a/ShellPkg/Application/Shell/ShellEnvVar.c
> +++ b/ShellPkg/Application/Shell/ShellEnvVar.c
> @@ -1,7 +1,7 @@
>  /** @file
>    function declarations for shell environment functions.
> 
> -  Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
> +  Copyright (c) 2009 - 2016, 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
> @@ -17,6 +17,11 @@
>  #define INIT_NAME_BUFFER_SIZE  128
>  #define INIT_DATA_BUFFER_SIZE  1024
> 
> +//
> +// The list is used to cache the environment variables.
> +//
> +ENV_VAR_LIST                   gShellEnvVarList;
> +
>  /**
>    Reports whether an environment variable is Volatile or Non-Volatile.
> 
> @@ -379,3 +384,171 @@ SetEnvironmentVariables(
>    //
>    return (SetEnvironmentVariableList(&VarList->Link));
>  }
> +
> +/**
> +  Find an environment variable in the gShellEnvVarList.
> +
> +  @param Key        The name of the environment variable.
> +  @param Value      The value of the environment variable, the buffer
> +                    shoule be freed by the caller.
> +  @param ValueSize  The size in bytes of the environment variable
> +                    including the tailing CHAR_NELL.
> +  @param Atts       The attributes of the variable.
> +
> +  @retval EFI_SUCCESS       The command executed successfully.
> +  @retval EFI_NOT_FOUND     The environment variable is not found in
> +                            gShellEnvVarList.
> +
> +**/
> +EFI_STATUS
> +ShellFindEnvVarInList (
> +  IN  CONST CHAR16    *Key,
> +  OUT CHAR16          **Value,
> +  OUT UINTN           *ValueSize,
> +  OUT UINT32          *Atts OPTIONAL
> +  )
> +{
> +  ENV_VAR_LIST      *Node;
> +
> +  if (Key == NULL || Value == NULL || ValueSize == NULL) {
> +    return SHELL_INVALID_PARAMETER;
> +  }
> +
> +  for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
> +      ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
> +      ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node-
> >Link)
> +     ){
> +    if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
> +      *Value      = AllocateCopyPool(StrSize(Node->Val), Node->Val);
> +      *ValueSize  = StrSize(Node->Val);
> +      if (Atts != NULL) {
> +        *Atts = Node->Atts;
> +      }
> +      return EFI_SUCCESS;
> +    }
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/**
> +  Add an environment variable into gShellEnvVarList.
> +
> +  @param Key        The name of the environment variable.
> +  @param Value      The value of environment variable.
> +  @param ValueSize  The size in bytes of the environment variable
> +                    including the tailing CHAR_NULL
> +  @param Atts       The attributes of the variable.
> +
> +**/
> +VOID
> +ShellAddEnvVarToList (
> +  IN CONST CHAR16     *Key,
> +  IN CONST CHAR16     *Value,
> +  IN UINTN            ValueSize,
> +  IN UINT32           Atts
> +  )
> +{
> +  ENV_VAR_LIST      *Node;
> +
> +  if (Key == NULL || Value == NULL || ValueSize == 0) {
> +    return;
> +  }
> +

Please add a  comment for what this loop here does. (something like "update the 
variable if it exists")

> +  for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
> +      ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
> +      ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node-
> >Link)
> +     ){
> +    if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
> +      Node->Atts = Atts;
> +      SHELL_FREE_NON_NULL(Node->Val);
> +      Node->Val  = AllocateZeroPool (ValueSize);
> +      ASSERT (Node->Val != NULL);
> +      CopyMem(Node->Val, Value, ValueSize);
> +      return;
> +    }
> +  }
> +
> +  //
> +  // If the environment varialbe key doesn't exist in list.
> +  //
> +  Node = (ENV_VAR_LIST*)AllocateZeroPool (sizeof(ENV_VAR_LIST));
> +  ASSERT (Node != NULL);
> +  Node->Key = AllocateCopyPool(StrSize(Key), Key);
> +  ASSERT (Node->Key != NULL);
> +  Node->Val = AllocateCopyPool(ValueSize, Value);
> +  ASSERT (Node->Val != NULL);
> +  InsertTailList(&gShellEnvVarList.Link, &Node->Link);

Why is Node->Atts not assigned here?  I think it should be.

> +
> +  return;
> +}
> +
> +/**
> +  Remove a specified environment variable in gShellEnvVarList.
> +
> +  @param Key        The name of the environment variable.
> +
> +  @retval EFI_SUCCESS       The command executed successfully.
> +  @retval EFI_NOT_FOUND     The environment variable is not found in
> +                            gShellEnvVarList.
> +**/
> +EFI_STATUS
> +ShellRemvoeEnvVarFromList (
> +  IN CONST CHAR16           *Key
> +  )
> +{
> +  ENV_VAR_LIST      *Node;
> +
> +  if (Key == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
> +      ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
> +      ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node-
> >Link)
> +     ){
> +    if (Node->Key != NULL && StrCmp(Key, Node->Key) == 0) {
> +      SHELL_FREE_NON_NULL(Node->Key);
> +      SHELL_FREE_NON_NULL(Node->Val);
> +      RemoveEntryList(&Node->Link);
> +      SHELL_FREE_NON_NULL(Node);
> +      return EFI_SUCCESS;
> +    }
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/**
> +  Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
> +  variables.
> +
> +**/
> +EFI_STATUS
> +ShellInitEnvVarList (
> +  VOID
> +  )
> +{
> +  EFI_STATUS    Status;
> +
> +  InitializeListHead(&gShellEnvVarList.Link);
> +  Status = GetEnvironmentVariableList (&gShellEnvVarList.Link);
> +
> +  return Status;
> +}
> +
> +/**
> +  Destructe the gShellEnvVarList.
> +
> +**/
> +VOID
> +ShellFreeEnvVarList (
> +  VOID
> +  )
> +{
> +  FreeEnvironmentVariableList (&gShellEnvVarList.Link);
> +  InitializeListHead(&gShellEnvVarList.Link);
> +
> +  return;
> +}
> +
> diff --git a/ShellPkg/Application/Shell/ShellEnvVar.h
> b/ShellPkg/Application/Shell/ShellEnvVar.h
> index ab3d916..dd88b29 100644
> --- a/ShellPkg/Application/Shell/ShellEnvVar.h
> +++ b/ShellPkg/Application/Shell/ShellEnvVar.h
> @@ -6,7 +6,7 @@
>  //#include <Library/UefiRuntimeServicesTableLib.h>
> 
> 
> -  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
> +  Copyright (c) 2009 - 2016, 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
> @@ -27,6 +27,12 @@ typedef struct {
>    UINT32      Atts;
>  } ENV_VAR_LIST;
> 
> +//
> +// The list is used to cache the environment variables.
> +//
> +extern ENV_VAR_LIST    gShellEnvVarList;
> +
> +
>  /**
>    Reports whether an environment variable is Volatile or Non-Volatile
> 
> @@ -206,5 +212,79 @@ FreeEnvironmentVariableList(
>    IN LIST_ENTRY *List
>    );
> 
> +/**
> +  Find an environment variable in the gShellEnvVarList.
> +
> +  @param Key        The name of the environment variable.
> +  @param Value      The value of the environment variable, the buffer
> +                    shoule be freed by the caller.
> +  @param ValueSize  The size in bytes of the environment variable
> +                    including the tailing CHAR_NULL.
> +  @param Atts       The attributes of the variable.
> +
> +  @retval EFI_SUCCESS       The command executed successfully.
> +  @retval EFI_NOT_FOUND     The environment variable is not found in
> +                            gShellEnvVarList.
> +
> +**/
> +EFI_STATUS
> +ShellFindEnvVarInList (
> +  IN  CONST CHAR16    *Key,
> +  OUT CHAR16          **Value,
> +  OUT UINTN           *ValueSize,
> +  OUT UINT32          *Atts OPTIONAL
> +  );
> +
> +/**
> +  Add an environment variable into gShellEnvVarList.
> +
> +  @param Key        The name of the environment variable.
> +  @param Value      The value of environment variable.
> +  @param ValueSize  The size in bytes of the environment variable
> +                    including the tailing CHAR_NELL
> +  @param Atts       The attributes of the variable.
> +
> +**/
> +VOID
> +ShellAddEnvVarToList (
> +  IN CONST CHAR16     *Key,
> +  IN CONST CHAR16     *Value,
> +  IN UINTN            ValueSize,
> +  IN UINT32           Atts
> +  );
> +
> +/**
> +  Remove a specified environment variable in gShellEnvVarList.
> +
> +  @param Key        The name of the environment variable.
> +
> +  @retval EFI_SUCCESS       The command executed successfully.
> +  @retval EFI_NOT_FOUND     The environment variable is not found in
> +                            gShellEnvVarList.
> +**/
> +EFI_STATUS
> +ShellRemvoeEnvVarFromList (
> +  IN CONST CHAR16           *Key
> +  );
> +
> +/**
> +  Initialize the gShellEnvVarList and cache all Shell-Guid-based environment
> +  variables.
> +
> +**/
> +EFI_STATUS
> +ShellInitEnvVarList (
> +  VOID
> +  );
> +
> +/**
> +  Destructe the gShellEnvVarList.
> +
> +**/
> +VOID
> +ShellFreeEnvVarList (
> +  VOID
> +  );
> +
>  #endif //_SHELL_ENVIRONMENT_VARIABLE_HEADER_
> 
> diff --git a/ShellPkg/Application/Shell/ShellProtocol.c
> b/ShellPkg/Application/Shell/ShellProtocol.c
> index e55b5e9..17c3002 100644
> --- a/ShellPkg/Application/Shell/ShellProtocol.c
> +++ b/ShellPkg/Application/Shell/ShellProtocol.c
> @@ -2691,7 +2691,6 @@ EfiShellGetEnvEx(
>    EFI_STATUS  Status;
>    VOID        *Buffer;
>    UINTN       Size;
> -  LIST_ENTRY  List;
>    ENV_VAR_LIST *Node;
>    CHAR16      *CurrentWriteLocation;
> 
> @@ -2699,21 +2698,13 @@ EfiShellGetEnvEx(
>    Buffer = NULL;
> 
>    if (Name == NULL) {
> -    //
> -    // Get all our environment variables
> -    //
> -    InitializeListHead(&List);
> -    Status = GetEnvironmentVariableList(&List);
> -    if (EFI_ERROR(Status)){
> -      return (NULL);
> -    }
> 
>      //
>      // Build the semi-colon delimited list. (2 passes)
>      //
> -    for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
> -      ; !IsNull(&List, &Node->Link)
> -      ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
> +    for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
> +      ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
> +      ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node-
> >Link)
>       ){
>        ASSERT(Node->Key != NULL);
>        Size += StrSize(Node->Key);
> @@ -2723,16 +2714,13 @@ EfiShellGetEnvEx(
> 
>      Buffer = AllocateZeroPool(Size);
>      if (Buffer == NULL) {
> -      if (!IsListEmpty (&List)) {
> -        FreeEnvironmentVariableList(&List);
> -      }
>        return (NULL);
>      }
>      CurrentWriteLocation = (CHAR16*)Buffer;
> 
> -    for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
> -      ; !IsNull(&List, &Node->Link)
> -      ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
> +    for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
> +      ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
> +      ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node-
> >Link)
>       ){
>        ASSERT(Node->Key != NULL);
>        StrCpyS( CurrentWriteLocation,
> @@ -2742,37 +2730,43 @@ EfiShellGetEnvEx(
>        CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
>      }
> 
> -    //
> -    // Free the list...
> -    //
> -    if (!IsListEmpty (&List)) {
> -      FreeEnvironmentVariableList(&List);
> -    }
>    } else {
>      //
>      // We are doing a specific environment variable
>      //
> +    Status = ShellFindEnvVarInList(Name, (CHAR16**)&Buffer, &Size,
> Attributes);
> 
> -    //
> -    // get the size we need for this EnvVariable
> -    //
> -    Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name,
> Attributes, &Size, Buffer);
> -    if (Status == EFI_BUFFER_TOO_SMALL) {
> +    if (EFI_ERROR(Status)){
>        //
> -      // Allocate the space and recall the get function
> +      // get the size we need for this EnvVariable
>        //
> -      Buffer = AllocateZeroPool(Size);
>        Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name,
> Attributes, &Size, Buffer);
> -    }
> -    //
> -    // we didnt get it (might not exist)
> -    // free the memory if we allocated any and return NULL
> -    //
> -    if (EFI_ERROR(Status)) {
> -      if (Buffer != NULL) {
> -        FreePool(Buffer);
> +      if (Status == EFI_BUFFER_TOO_SMALL) {
> +        //
> +        // Allocate the space and recall the get function
> +        //
> +        Buffer = AllocateZeroPool(Size);
> +        Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name,
> Attributes, &Size, Buffer);
> +      }
> +      //
> +      // we didnt get it (might not exist)
> +      // free the memory if we allocated any and return NULL
> +      //
> +      if (EFI_ERROR(Status)) {
> +        if (Buffer != NULL) {
> +          FreePool(Buffer);
> +        }
> +        return (NULL);
> +      } else {
> +        //
> +        // If we did not find the environment variable in the 
> gShellEnvVarList
> +        // but get it from UEFI variable storage successfully then we need
> update
> +        // the gShellEnvVarList.
> +        //
> +        ShellFreeEnvVarList ();
> +        Status = ShellInitEnvVarList ();
> +        ASSERT (Status == EFI_SUCCESS);
>        }
> -      return (NULL);
>      }
>    }
> 
> @@ -2832,14 +2826,35 @@ InternalEfiShellSetEnv(
>    IN BOOLEAN Volatile
>    )
>  {
> +  EFI_STATUS      Status;
> +  UINT32          Atts;
> +
> +  Atts = 0x0;
> +
>    if (Value == NULL || StrLen(Value) == 0) {
> -    return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name));
> +    Status = SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
> +    if (!EFI_ERROR(Status)) {
> +      ShellRemvoeEnvVarFromList(Name);
> +    }
> +    return Status;
>    } else {
>      SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
>      if (Volatile) {
> -      return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value),
> Value));
> +      Status = SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value),
> Value);
> +      if (!EFI_ERROR(Status)) {
> +        Atts   &= ~EFI_VARIABLE_NON_VOLATILE;
> +        Atts   |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
> +        ShellAddEnvVarToList(Name, Value, StrSize(Value), Atts);
> +      }
> +      return Status;
>      } else {
> -      return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value),
> Value));
> +      Status = SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value),
> Value);
> +      if (!EFI_ERROR(Status)) {
> +        Atts   |= EFI_VARIABLE_NON_VOLATILE;
> +        Atts   |= EFI_VARIABLE_BOOTSERVICE_ACCESS;
> +        ShellAddEnvVarToList(Name, Value, StrSize(Value), Atts);
> +      }
> +      return Status;
>      }
>    }
>  }
> --
> 2.7.1.windows.2

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to