* [edk2-devel] [edk2-redfish-client][PATCH 01/10] RedfishClientPkg: introduce Redfish HTTP cache library
@ 2024-01-03 11:59 Nickle Wang via groups.io
2024-01-03 13:01 ` Chang, Abner via groups.io
0 siblings, 1 reply; 3+ messages in thread
From: Nickle Wang via groups.io @ 2024-01-03 11:59 UTC (permalink / raw)
To: devel; +Cc: Abner Chang, Igor Kulchytskyy, Nick Ramirez
Introduce RedfishHttpCacheLib to improve HTTP GET performance
in Redfish feature drivers. Feature drivers often query same
Redfish resource multiple times for different purpose. Add
HTTP cache mechanism to improve the performance.
Signed-off-by: Nickle Wang <nicklew@nvidia.com>
Cc: Abner Chang <abner.chang@amd.com>
Cc: Igor Kulchytskyy <igork@ami.com>
Cc: Nick Ramirez <nramirez@nvidia.com>
---
RedfishClientPkg/RedfishClientPkg.dec | 3 +-
RedfishClientPkg/RedfishClientLibs.dsc.inc | 3 +-
RedfishClientPkg/RedfishClientPkg.dsc | 3 +-
.../RedfishHttpCacheLib.inf | 48 ++
.../Include/Library/RedfishHttpCacheLib.h | 59 ++
.../RedfishHttpCacheLibInternal.h | 63 ++
.../RedfishHttpCacheLib/RedfishHttpCacheLib.c | 774 ++++++++++++++++++
7 files changed, 950 insertions(+), 3 deletions(-)
create mode 100644 RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
create mode 100644 RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
create mode 100644 RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInternal.h
create mode 100644 RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
diff --git a/RedfishClientPkg/RedfishClientPkg.dec b/RedfishClientPkg/RedfishClientPkg.dec
index 5f8a0350..b350faca 100644
--- a/RedfishClientPkg/RedfishClientPkg.dec
+++ b/RedfishClientPkg/RedfishClientPkg.dec
@@ -2,7 +2,7 @@
# Redfish Client Package
#
# (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
-# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
@@ -26,6 +26,7 @@
EdkIIRedfishResourceConfigLib|Include/Library/EdkIIRedfishResourceConfigLib.h
RedfishEventLib|Include/Library/RedfishEventLib.h
RedfishVersionLib|Include/Library/RedfishVersionLib.h
+ RedfishHttpCacheLib|Include/Library/RedfishHttpCacheLib.h
[LibraryClasses.Common.Private]
## @libraryclass Redfish Helper Library
diff --git a/RedfishClientPkg/RedfishClientLibs.dsc.inc b/RedfishClientPkg/RedfishClientLibs.dsc.inc
index 5eae6711..572f426e 100644
--- a/RedfishClientPkg/RedfishClientLibs.dsc.inc
+++ b/RedfishClientPkg/RedfishClientLibs.dsc.inc
@@ -6,7 +6,7 @@
# of EDKII network library classes.
#
# (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -40,3 +40,4 @@
RedfishVersionLib|RedfishClientPkg/Library/RedfishVersionLib/RedfishVersionLib.inf
RedfishAddendumLib|RedfishClientPkg/Library/RedfishAddendumLib/RedfishAddendumLib.inf
RedfishDebugLib|RedfishPkg/Library/RedfishDebugLib/RedfishDebugLib.inf
+ RedfishHttpCacheLib|RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
diff --git a/RedfishClientPkg/RedfishClientPkg.dsc b/RedfishClientPkg/RedfishClientPkg.dsc
index e16c91b8..0e3ef1ac 100644
--- a/RedfishClientPkg/RedfishClientPkg.dsc
+++ b/RedfishClientPkg/RedfishClientPkg.dsc
@@ -2,7 +2,7 @@
# Redfish Client Package
#
# (C) Copyright 2021 Hewlett-Packard Enterprise Development LP.
-# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
@@ -61,5 +61,6 @@
RedfishClientPkg/Library/RedfishFeatureUtilityLib/RedfishFeatureUtilityLib.inf
RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf
RedfishClientPkg/Library/RedfishAddendumLib/RedfishAddendumLib.inf
+ RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
!include RedfishClientPkg/RedfishClient.dsc.inc
diff --git a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
new file mode 100644
index 00000000..32da4f83
--- /dev/null
+++ b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
@@ -0,0 +1,48 @@
+## @file
+# Redfish HTTP cache library helps Redfish application to get Redfish resource
+# from BMC with cache mechanism enabled.
+#
+# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010006
+ BASE_NAME = RedfishHttpCacheLib
+ FILE_GUID = 21F8FEEC-023C-451D-824D-823058FD9481
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = RedfishHttpCacheLib| DXE_DRIVER UEFI_DRIVER
+ CONSTRUCTOR = RedfishHttpCacheConstructor
+ DESTRUCTOR = RedfishHttpCacheDestructor
+
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ RedfishHttpCacheLibInternal.h
+ RedfishHttpCacheLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ RedfishPkg/RedfishPkg.dec
+ RedfishClientPkg/RedfishClientPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ RedfishLib
+ UefiLib
+ RedfishDebugLib
+ ReportStatusCodeLib
+ PrintLib
+
+[depex]
+ TRUE
+
diff --git a/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h b/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
new file mode 100644
index 00000000..1277b981
--- /dev/null
+++ b/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
@@ -0,0 +1,59 @@
+/** @file
+ This file defines the Redfish HTTP cache library interface.
+
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef REDFISH_HTTP_CACHE_LIB_H_
+#define REDFISH_HTTP_CACHE_LIB_H_
+
+#include <Uefi.h>
+#include <Library/RedfishLib.h>
+
+/**
+ Get redfish resource from given resource URI with cache mechanism
+ supported. It's caller's responsibility to Response by calling
+ RedfishFreeResponse ().
+
+ @param[in] Service Redfish service instance to make query.
+ @param[in] Uri Target resource URI.
+ @param[out] Response HTTP response from redfish service.
+ @param[in] UseCache If it is TRUE, this function will search for
+ cache first. If it is FALSE, this function
+ will query Redfish URI directly.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+RedfishHttpGetResource (
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ OUT REDFISH_RESPONSE *Response,
+ IN BOOLEAN UseCache
+ );
+
+/**
+ Reset the cached data specified by given URI. When response data
+ returned by RedfishHttpResetResource() is modified, the response
+ data can not be used by other caller. Application calls this
+ function to make this data to be stale data and
+ RedfishHttpResetResource() will get latest data from remote server
+ again.
+
+ @param[in] Uri Target resource URI.
+
+ @retval EFI_SUCCESS Resrouce is reset successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+RedfishHttpResetResource (
+ IN EFI_STRING Uri
+ );
+
+#endif
diff --git a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInternal.h b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInternal.h
new file mode 100644
index 00000000..2549335d
--- /dev/null
+++ b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInternal.h
@@ -0,0 +1,63 @@
+/** @file
+ This file defines the Redfish HTTP cache library internal headers.
+
+ Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef REDFISH_HTTP_CACHE_INTERNAL_LIB_H_
+#define REDFISH_HTTP_CACHE_INTERNAL_LIB_H_
+
+#include <Uefi.h>
+#include <RedfishBase.h>
+
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/RedfishLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/RedfishHttpCacheLib.h>
+#include <Library/RedfishDebugLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/PrintLib.h>
+
+#define REDFISH_HTTP_CACHE_LIST_SIZE 0x0F
+#define REDFISH_HTTP_GET_RETRY_MAX 0x0F
+#define REDFISH_HTTP_RETRY_WAIT (2 * 1000000U) ///< 1 second
+#define REDFISH_ERROR_MSG_MAX 128
+#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP failure(0x%x): %a"
+#define REDFISH_HTTP_CACHE_DEBUG DEBUG_VERBOSE
+#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_VERBOSE
+
+///
+/// Definition of REDFISH_HTTP_CACHE_DATA
+///
+typedef struct {
+ LIST_ENTRY List;
+ EFI_STRING Uri;
+ UINTN HitCount;
+ REDFISH_RESPONSE *Response;
+} REDFISH_HTTP_CACHE_DATA;
+
+#define REDFISH_HTTP_CACHE_FROM_LIST(a) BASE_CR (a, REDFISH_HTTP_CACHE_DATA, List)
+
+///
+/// Definition of REDFISH_HTTP_CACHE_LIST
+///
+typedef struct {
+ LIST_ENTRY Head;
+ UINTN Count;
+ UINTN Capacity;
+} REDFISH_HTTP_CACHE_LIST;
+
+///
+/// Definition of REDFISH_HTTP_CACHE_PRIVATE
+///
+typedef struct {
+ REDFISH_HTTP_CACHE_LIST CacheList;
+} REDFISH_HTTP_CACHE_PRIVATE;
+
+#endif
diff --git a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
new file mode 100644
index 00000000..c751e608
--- /dev/null
+++ b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
@@ -0,0 +1,774 @@
+/** @file
+ Redfish HTTP cache library helps Redfish application to get Redfish resource
+ from BMC with cache mechanism enabled.
+
+ Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "RedfishHttpCacheLibInternal.h"
+
+REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL;
+
+/**
+ This function copy the data in SrcResponse to DstResponse.
+
+ @param[in] SrcResponse Source Response to copy.
+ @param[out] DstResponse Destination Response.
+
+ @retval EFI_SUCCESS Response is copied successfully.
+ @retval Others Error occurs.
+
+**/
+EFI_STATUS
+CopyRedfishResponse (
+ IN REDFISH_RESPONSE *SrcResponse,
+ OUT REDFISH_RESPONSE *DstResponse
+ )
+{
+ EDKII_JSON_VALUE JsonValue;
+ REDFISH_SERVICE Service;
+ UINTN Index;
+
+ if ((SrcResponse == NULL) || (DstResponse == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (SrcResponse == DstResponse) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Status code
+ //
+ if (SrcResponse->StatusCode != NULL) {
+ DstResponse->StatusCode = AllocateCopyPool (sizeof (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
+ if (DstResponse->StatusCode == NULL) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Header
+ //
+ if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
+ DstResponse->HeaderCount = 0;
+ DstResponse->Headers = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) * SrcResponse->HeaderCount);
+ if (DstResponse->Headers == NULL) {
+ goto ON_ERROR;
+ }
+
+ for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
+ DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize (SrcResponse->Headers[Index].FieldName), SrcResponse->Headers[Index].FieldName);
+ if (DstResponse->Headers[Index].FieldName == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize (SrcResponse->Headers[Index].FieldValue), SrcResponse->Headers[Index].FieldValue);
+ if (DstResponse->Headers[Index].FieldValue == NULL) {
+ goto ON_ERROR;
+ }
+
+ DstResponse->HeaderCount += 1;
+ }
+ }
+
+ //
+ // Payload
+ //
+ if (SrcResponse->Payload != NULL) {
+ Service = RedfishServiceInPayload (SrcResponse->Payload);
+ JsonValue = RedfishJsonInPayload (SrcResponse->Payload);
+ DstResponse->Payload = RedfishCreatePayload (JsonValue, Service);
+ if (DstResponse->Payload == NULL) {
+ goto ON_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ RedfishFreeResponse (
+ DstResponse->StatusCode,
+ DstResponse->HeaderCount,
+ DstResponse->Headers,
+ DstResponse->Payload
+ );
+
+ return EFI_OUT_OF_RESOURCES;
+}
+
+/**
+ This function clone input response and return to caller
+
+ @param[in] Response Response to clone.
+
+ @retval REDFISH_RESPONSE * Response is cloned.
+ @retval NULL Errors occur.
+
+**/
+REDFISH_RESPONSE *
+CloneRedfishResponse (
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ EFI_STATUS Status;
+ REDFISH_RESPONSE *NewResponse;
+
+ if (Response == NULL) {
+ return NULL;
+ }
+
+ NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
+ if (NewResponse == NULL) {
+ return NULL;
+ }
+
+ Status = CopyRedfishResponse (Response, NewResponse);
+ if (EFI_ERROR (Status)) {
+ FreePool (NewResponse);
+ return NULL;
+ }
+
+ return NewResponse;
+}
+
+/**
+
+ Convert Unicode string to ASCII string. It's call responsibility to release returned buffer.
+
+ @param[in] UnicodeStr Unicode string to convert.
+
+ @retval CHAR8 * ASCII string returned.
+ @retval NULL Errors occur.
+
+**/
+CHAR8 *
+StringUnicodeToAscii (
+ IN EFI_STRING UnicodeStr
+ )
+{
+ CHAR8 *AsciiStr;
+ UINTN AsciiStrSize;
+ EFI_STATUS Status;
+
+ if (IS_EMPTY_STRING (UnicodeStr)) {
+ return NULL;
+ }
+
+ AsciiStrSize = StrLen (UnicodeStr) + 1;
+ AsciiStr = AllocatePool (AsciiStrSize);
+ if (AsciiStr == NULL) {
+ return NULL;
+ }
+
+ Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
+ FreePool (AsciiStr);
+ return NULL;
+ }
+
+ return AsciiStr;
+}
+
+/**
+ Release REDFISH_HTTP_CACHE_DATA resource
+
+ @param[in] Data Pointer to REDFISH_HTTP_CACHE_DATA instance
+
+ @retval EFI_SUCCESS REDFISH_HTTP_CACHE_DATA is released successfully.
+ @retval EFI_INVALID_PARAMETER Data is NULL
+
+**/
+EFI_STATUS
+ReleaseHttpCacheData (
+ IN REDFISH_HTTP_CACHE_DATA *Data
+ )
+{
+ if (Data == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Data->Uri != NULL) {
+ FreePool (Data->Uri);
+ }
+
+ if (Data->Response != NULL) {
+ if (Data->Response->Payload != NULL) {
+ RedfishFreeResponse (
+ Data->Response->StatusCode,
+ Data->Response->HeaderCount,
+ Data->Response->Headers,
+ Data->Response->Payload
+ );
+ FreePool (Data->Response);
+ }
+ }
+
+ FreePool (Data);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Create new cache data.
+
+ @param[in] Uri The URI string matching to this cache data.
+ @param[in] Response HTTP response.
+
+ @retval REDFISH_HTTP_CACHE_DATA * Pointer to newly created cache data.
+ @retval NULL No memory available.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+NewHttpCacheData (
+ IN EFI_STRING Uri,
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ REDFISH_HTTP_CACHE_DATA *NewData;
+ UINTN Size;
+
+ if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+ return NULL;
+ }
+
+ NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
+ if (NewData == NULL) {
+ return NULL;
+ }
+
+ Size = StrSize (Uri);
+ NewData->Uri = AllocateCopyPool (Size, Uri);
+ if (NewData->Uri == NULL) {
+ goto ON_ERROR;
+ }
+
+ NewData->Response = Response;
+ NewData->HitCount = 1;
+
+ return NewData;
+
+ON_ERROR:
+
+ if (NewData != NULL) {
+ ReleaseHttpCacheData (NewData);
+ }
+
+ return NULL;
+}
+
+/**
+ Search on given ListHeader for given URI string.
+
+ @param[in] ListHeader Target list to search.
+ @param[in] Uri Target URI to search.
+
+ @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
+ @retval NULL No cache data with given URI is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindHttpCacheData (
+ IN LIST_ENTRY *ListHeader,
+ IN EFI_STRING Uri
+ )
+{
+ LIST_ENTRY *List;
+ REDFISH_HTTP_CACHE_DATA *Data;
+
+ if (IS_EMPTY_STRING (Uri)) {
+ return NULL;
+ }
+
+ if (IsListEmpty (ListHeader)) {
+ return NULL;
+ }
+
+ Data = NULL;
+ List = GetFirstNode (ListHeader);
+ while (!IsNull (ListHeader, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+ if (StrCmp (Data->Uri, Uri) == 0) {
+ return Data;
+ }
+
+ List = GetNextNode (ListHeader, List);
+ }
+
+ return NULL;
+}
+
+/**
+ Search on given ListHeader and return cache data with minimum hit count.
+
+ @param[in] ListHeader Target list to search.
+
+ @retval REDFISH_HTTP_CACHE_DATA Target cache data is returned.
+ @retval NULL No cache data is found.
+
+**/
+REDFISH_HTTP_CACHE_DATA *
+FindUnusedHttpCacheData (
+ IN LIST_ENTRY *ListHeader
+ )
+{
+ LIST_ENTRY *List;
+ REDFISH_HTTP_CACHE_DATA *Data;
+ REDFISH_HTTP_CACHE_DATA *UnusedData;
+ UINTN HitCount;
+
+ if (IsListEmpty (ListHeader)) {
+ return NULL;
+ }
+
+ Data = NULL;
+ UnusedData = NULL;
+ HitCount = 0;
+
+ List = GetFirstNode (ListHeader);
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+ UnusedData = Data;
+ HitCount = Data->HitCount;
+ List = GetNextNode (ListHeader, List);
+
+ while (!IsNull (ListHeader, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+ if (Data->HitCount < HitCount) {
+ HitCount = Data->HitCount;
+ UnusedData = Data;
+ }
+
+ List = GetNextNode (ListHeader, List);
+ }
+
+ return UnusedData;
+}
+
+/**
+ Delete a cache data by given cache instance.
+
+ @param[in] List Target cache list to be removed.
+ @param[in] Data Pointer to the instance to be deleted.
+
+ @retval EFI_SUCCESS Cache data is removed.
+ @retval Others Fail to remove cache data.
+
+**/
+EFI_STATUS
+DeleteHttpCacheData (
+ IN REDFISH_HTTP_CACHE_LIST *List,
+ IN REDFISH_HTTP_CACHE_DATA *Data
+ )
+{
+ if ((List == NULL) || (Data == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__, Data->Uri));
+
+ RemoveEntryList (&Data->List);
+ --List->Count;
+
+ return ReleaseHttpCacheData (Data);
+}
+
+/**
+ Add new cache by given URI and HTTP response to specify List.
+
+ @param[in] List Target cache list to add.
+ @param[in] Uri The URI string matching to this cache data.
+ @param[in] Response HTTP response.
+
+ @retval EFI_SUCCESS Cache data is added.
+ @retval Others Fail to add cache data.
+
+**/
+EFI_STATUS
+AddHttpCacheData (
+ IN REDFISH_HTTP_CACHE_LIST *List,
+ IN EFI_STRING Uri,
+ IN REDFISH_RESPONSE *Response
+ )
+{
+ REDFISH_HTTP_CACHE_DATA *NewData;
+ REDFISH_HTTP_CACHE_DATA *OldData;
+ REDFISH_HTTP_CACHE_DATA *UnusedData;
+ REDFISH_RESPONSE *NewResponse;
+
+ if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If same cache data exist, replace it with latest one.
+ //
+ OldData = FindHttpCacheData (&List->Head, Uri);
+ if (OldData != NULL) {
+ DeleteHttpCacheData (List, OldData);
+ }
+
+ //
+ // Check capacity
+ //
+ if (List->Count >= List->Capacity) {
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused cache\n", __func__));
+ UnusedData = FindUnusedHttpCacheData (&List->Head);
+ if (UnusedData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DeleteHttpCacheData (List, UnusedData);
+ }
+
+ //
+ // Clone a local copy
+ //
+ NewResponse = CloneRedfishResponse (Response);
+ if (NewResponse == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NewData = NewHttpCacheData (Uri, NewResponse);
+ if (NewData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (&List->Head, &NewData->List);
+ ++List->Count;
+
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n", __func__, List->Count, List->Capacity, NewData->Uri));
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Release all cache from list.
+
+ @param[in] CacheList The list to be released.
+
+ @retval EFI_SUCCESS All cache data are released.
+ @retval EFI_INVALID_PARAMETER CacheList is NULL.
+
+**/
+EFI_STATUS
+ReleaseCacheList (
+ IN REDFISH_HTTP_CACHE_LIST *CacheList
+ )
+{
+ LIST_ENTRY *List;
+ LIST_ENTRY *Next;
+ REDFISH_HTTP_CACHE_DATA *Data;
+
+ if (CacheList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IsListEmpty (&CacheList->Head)) {
+ return EFI_SUCCESS;
+ }
+
+ Data = NULL;
+ Next = NULL;
+ List = GetFirstNode (&CacheList->Head);
+ while (!IsNull (&CacheList->Head, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+ Next = GetNextNode (&CacheList->Head, List);
+
+ DeleteHttpCacheData (CacheList, Data);
+
+ List = Next;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Debug output the cache list.
+
+ @param[in] Msg Debug message string.
+ @param[in] ErrorLevel Output error level.
+ @param[in] CacheList Target list to dump.
+
+ @retval EFI_SUCCESS Debug dump finished.
+ @retval EFI_INVALID_PARAMETER HttpCacheList is NULL.
+
+**/
+EFI_STATUS
+DumpHttpCacheList (
+ IN CONST CHAR8 *Msg,
+ IN UINTN ErrorLevel,
+ IN REDFISH_HTTP_CACHE_LIST *CacheList
+ )
+{
+ LIST_ENTRY *List;
+ REDFISH_HTTP_CACHE_DATA *Data;
+ UINTN Index;
+
+ if (CacheList == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IS_EMPTY_STRING (Msg)) {
+ DEBUG ((ErrorLevel, "%a\n", Msg));
+ }
+
+ if (IsListEmpty (&CacheList->Head)) {
+ DEBUG ((ErrorLevel, "list is empty\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count, CacheList->Capacity));
+ Data = NULL;
+ Index = 0;
+ List = GetFirstNode (&CacheList->Head);
+ while (!IsNull (&CacheList->Head, List)) {
+ Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
+
+ DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data->HitCount));
+
+ List = GetNextNode (&CacheList->Head, List);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get redfish resource from given resource URI with cache mechanism
+ supported. It's caller's responsibility to Response by calling
+ RedfishFreeResponse ().
+
+ @param[in] Service Redfish service instance to make query.
+ @param[in] Uri Target resource URI.
+ @param[out] Response HTTP response from redfish service.
+ @param[in] UseCache If it is TRUE, this function will search for
+ cache first. If it is FALSE, this function
+ will query Redfish URI directly.
+
+ @retval EFI_SUCCESS Resrouce is returned successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+RedfishHttpGetResource (
+ IN REDFISH_SERVICE Service,
+ IN EFI_STRING Uri,
+ OUT REDFISH_RESPONSE *Response,
+ IN BOOLEAN UseCache
+ )
+{
+ EFI_STATUS Status;
+ CHAR8 *AsciiUri;
+ REDFISH_HTTP_CACHE_DATA *CacheData;
+ UINTN RetryCount;
+
+ if ((Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (mRedfishHttpCachePrivate == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ AsciiUri = NULL;
+ CacheData = NULL;
+ RetryCount = 0;
+
+ //
+ // Search for cache list.
+ //
+ if (UseCache) {
+ CacheData = FindHttpCacheData (&mRedfishHttpCachePrivate->CacheList.Head, Uri);
+ if (CacheData != NULL) {
+ DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache hit! %s\n", __func__, Uri));
+
+ //
+ // Copy cached response to caller's buffer.
+ //
+ Status = CopyRedfishResponse (CacheData->Response, Response);
+ CacheData->HitCount += 1;
+ return Status;
+ }
+ }
+
+ AsciiUri = StringUnicodeToAscii (Uri);
+ if (AsciiUri == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Get resource from redfish service.
+ //
+ do {
+ RetryCount += 1;
+ Status = RedfishGetByUri (
+ Service,
+ AsciiUri,
+ Response
+ );
+ if (!EFI_ERROR (Status) || (RetryCount >= REDFISH_HTTP_GET_RETRY_MAX)) {
+ break;
+ }
+
+ //
+ // Retry when BMC is not ready.
+ //
+ if ((Response->StatusCode != NULL)) {
+ DEBUG_CODE (
+ DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
+ );
+
+ if (*Response->StatusCode != HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
+ break;
+ }
+
+ FreePool (Response->StatusCode);
+ Response->StatusCode = NULL;
+ }
+
+ DEBUG ((DEBUG_WARN, "%a: RedfishGetByUri failed, retry (%d/%d)\n", __func__, RetryCount, REDFISH_HTTP_GET_RETRY_MAX));
+ gBS->Stall (REDFISH_HTTP_RETRY_WAIT);
+ } while (TRUE);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: get %a failed (%d/%d): %r\n", __func__, AsciiUri, RetryCount, REDFISH_HTTP_GET_RETRY_MAX, Status));
+ if (Response->Payload != NULL) {
+ RedfishFreeResponse (
+ NULL,
+ 0,
+ NULL,
+ Response->Payload
+ );
+ Response->Payload = NULL;
+ }
+
+ goto ON_RELEASE;
+ }
+
+ //
+ // Keep response in cache list
+ //
+ Status = AddHttpCacheData (&mRedfishHttpCachePrivate->CacheList, Uri, Response);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri, Status));
+ goto ON_RELEASE;
+ }
+
+ DEBUG_CODE (
+ DumpHttpCacheList (__func__, REDFISH_HTTP_CACHE_DEBUG_DUMP, &mRedfishHttpCachePrivate->CacheList);
+ );
+
+ON_RELEASE:
+
+ if (AsciiUri != NULL) {
+ FreePool (AsciiUri);
+ }
+
+ return Status;
+}
+
+/**
+ Reset the cached data specified by given URI. When response data
+ returned by RedfishHttpResetResource() is modified, the response
+ data can not be used by other caller. Application calls this
+ function to make this data to be stale data and
+ RedfishHttpResetResource() will get latest data from remote server
+ again.
+
+ @param[in] Uri Target resource URI.
+
+ @retval EFI_SUCCESS Resrouce is reset successfully.
+ @retval Others Errors occur.
+
+**/
+EFI_STATUS
+RedfishHttpResetResource (
+ IN EFI_STRING Uri
+ )
+{
+ REDFISH_HTTP_CACHE_DATA *CacheData;
+
+ if (IS_EMPTY_STRING (Uri)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (mRedfishHttpCachePrivate == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ CacheData = FindHttpCacheData (&mRedfishHttpCachePrivate->CacheList.Head, Uri);
+ if (CacheData == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ DeleteHttpCacheData (&mRedfishHttpCachePrivate->CacheList, CacheData);
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ Initial HTTP cache library instance.
+
+ @param[in] ImageHandle The image handle.
+ @param[in] SystemTable The system table.
+
+ @retval EFI_SUCCESS Initial library successfully.
+ @retval Other Return error status.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishHttpCacheConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ mRedfishHttpCachePrivate = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_PRIVATE));
+ if (mRedfishHttpCachePrivate == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initial cache list
+ //
+ mRedfishHttpCachePrivate->CacheList.Capacity = REDFISH_HTTP_CACHE_LIST_SIZE;
+ mRedfishHttpCachePrivate->CacheList.Count = 0x00;
+ InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Release allocated resource.
+
+ @param[in] ImageHandle Handle that identifies the image to be unloaded.
+ @param[in] SystemTable The system table.
+
+ @retval EFI_SUCCESS The image has been unloaded.
+
+**/
+EFI_STATUS
+EFIAPI
+RedfishHttpCacheDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ if (mRedfishHttpCachePrivate != NULL) {
+ if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
+ ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
+ }
+
+ FreePool (mRedfishHttpCachePrivate);
+ mRedfishHttpCachePrivate = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
--
2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113071): https://edk2.groups.io/g/devel/message/113071
Mute This Topic: https://groups.io/mt/103500371/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [edk2-devel] [edk2-redfish-client][PATCH 01/10] RedfishClientPkg: introduce Redfish HTTP cache library
2024-01-03 11:59 [edk2-devel] [edk2-redfish-client][PATCH 01/10] RedfishClientPkg: introduce Redfish HTTP cache library Nickle Wang via groups.io
@ 2024-01-03 13:01 ` Chang, Abner via groups.io
2024-01-04 2:33 ` Nickle Wang via groups.io
0 siblings, 1 reply; 3+ messages in thread
From: Chang, Abner via groups.io @ 2024-01-03 13:01 UTC (permalink / raw)
To: Nickle Wang, devel@edk2.groups.io; +Cc: Igor Kulchytskyy, Nick Ramirez
[AMD Official Use Only - General]
Hi Nickle,
I have no problem with this feature as we have been using this for a while. Per discussion off line, HttpCacheLibrary would be replaced with protocol version later. I would just give my RB to this patch as this patch is sent for cleaning up the backlogs we are going to upstream.
But before we merging this patch, could you please replace all "BMC" term in these files with either Redfish service or service? Thanks.
Abner
> -----Original Message-----
> From: Nickle Wang <nicklew@nvidia.com>
> Sent: Wednesday, January 3, 2024 8:00 PM
> To: devel@edk2.groups.io
> Cc: Chang, Abner <Abner.Chang@amd.com>; Igor Kulchytskyy
> <igork@ami.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: [edk2-redfish-client][PATCH 01/10] RedfishClientPkg: introduce
> Redfish HTTP cache library
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> Introduce RedfishHttpCacheLib to improve HTTP GET performance
> in Redfish feature drivers. Feature drivers often query same
> Redfish resource multiple times for different purpose. Add
> HTTP cache mechanism to improve the performance.
>
> Signed-off-by: Nickle Wang <nicklew@nvidia.com>
> Cc: Abner Chang <abner.chang@amd.com>
> Cc: Igor Kulchytskyy <igork@ami.com>
> Cc: Nick Ramirez <nramirez@nvidia.com>
> ---
> RedfishClientPkg/RedfishClientPkg.dec | 3 +-
> RedfishClientPkg/RedfishClientLibs.dsc.inc | 3 +-
> RedfishClientPkg/RedfishClientPkg.dsc | 3 +-
> .../RedfishHttpCacheLib.inf | 48 ++
> .../Include/Library/RedfishHttpCacheLib.h | 59 ++
> .../RedfishHttpCacheLibInternal.h | 63 ++
> .../RedfishHttpCacheLib/RedfishHttpCacheLib.c | 774 ++++++++++++++++++
> 7 files changed, 950 insertions(+), 3 deletions(-)
> create mode 100644
> RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> create mode 100644
> RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> create mode 100644
> RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInternal.h
> create mode 100644
> RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
>
> diff --git a/RedfishClientPkg/RedfishClientPkg.dec
> b/RedfishClientPkg/RedfishClientPkg.dec
> index 5f8a0350..b350faca 100644
> --- a/RedfishClientPkg/RedfishClientPkg.dec
> +++ b/RedfishClientPkg/RedfishClientPkg.dec
> @@ -2,7 +2,7 @@
> # Redfish Client Package
> #
> # (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
> -# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> #
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> ##
> @@ -26,6 +26,7 @@
>
> EdkIIRedfishResourceConfigLib|Include/Library/EdkIIRedfishResourceConfigLi
> b.h
> RedfishEventLib|Include/Library/RedfishEventLib.h
> RedfishVersionLib|Include/Library/RedfishVersionLib.h
> + RedfishHttpCacheLib|Include/Library/RedfishHttpCacheLib.h
>
> [LibraryClasses.Common.Private]
> ## @libraryclass Redfish Helper Library
> diff --git a/RedfishClientPkg/RedfishClientLibs.dsc.inc
> b/RedfishClientPkg/RedfishClientLibs.dsc.inc
> index 5eae6711..572f426e 100644
> --- a/RedfishClientPkg/RedfishClientLibs.dsc.inc
> +++ b/RedfishClientPkg/RedfishClientLibs.dsc.inc
> @@ -6,7 +6,7 @@
> # of EDKII network library classes.
> #
> # (C) Copyright 2021-2022 Hewlett Packard Enterprise Development LP<BR>
> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> #
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> #
> @@ -40,3 +40,4 @@
>
> RedfishVersionLib|RedfishClientPkg/Library/RedfishVersionLib/RedfishVersio
> nLib.inf
>
> RedfishAddendumLib|RedfishClientPkg/Library/RedfishAddendumLib/Redfis
> hAddendumLib.inf
> RedfishDebugLib|RedfishPkg/Library/RedfishDebugLib/RedfishDebugLib.inf
> +
> RedfishHttpCacheLib|RedfishClientPkg/Library/RedfishHttpCacheLib/Redfish
> HttpCacheLib.inf
> diff --git a/RedfishClientPkg/RedfishClientPkg.dsc
> b/RedfishClientPkg/RedfishClientPkg.dsc
> index e16c91b8..0e3ef1ac 100644
> --- a/RedfishClientPkg/RedfishClientPkg.dsc
> +++ b/RedfishClientPkg/RedfishClientPkg.dsc
> @@ -2,7 +2,7 @@
> # Redfish Client Package
> #
> # (C) Copyright 2021 Hewlett-Packard Enterprise Development LP.
> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> #
> # SPDX-License-Identifier: BSD-2-Clause-Patent
> #
> @@ -61,5 +61,6 @@
>
> RedfishClientPkg/Library/RedfishFeatureUtilityLib/RedfishFeatureUtilityLib.inf
> RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf
> RedfishClientPkg/Library/RedfishAddendumLib/RedfishAddendumLib.inf
> + RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
>
> !include RedfishClientPkg/RedfishClient.dsc.inc
> diff --git
> a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> new file mode 100644
> index 00000000..32da4f83
> --- /dev/null
> +++
> b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> @@ -0,0 +1,48 @@
> +## @file
> +# Redfish HTTP cache library helps Redfish application to get Redfish
> resource
> +# from BMC with cache mechanism enabled.
> +#
> +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> + INF_VERSION = 0x00010006
> + BASE_NAME = RedfishHttpCacheLib
> + FILE_GUID = 21F8FEEC-023C-451D-824D-823058FD9481
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + LIBRARY_CLASS = RedfishHttpCacheLib| DXE_DRIVER UEFI_DRIVER
> + CONSTRUCTOR = RedfishHttpCacheConstructor
> + DESTRUCTOR = RedfishHttpCacheDestructor
> +
> +#
> +# VALID_ARCHITECTURES = IA32 X64 EBC
> +#
> +
> +[Sources]
> + RedfishHttpCacheLibInternal.h
> + RedfishHttpCacheLib.c
> +
> +[Packages]
> + MdePkg/MdePkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + RedfishPkg/RedfishPkg.dec
> + RedfishClientPkg/RedfishClientPkg.dec
> +
> +[LibraryClasses]
> + BaseLib
> + DebugLib
> + UefiBootServicesTableLib
> + MemoryAllocationLib
> + RedfishLib
> + UefiLib
> + RedfishDebugLib
> + ReportStatusCodeLib
> + PrintLib
> +
> +[depex]
> + TRUE
> +
> diff --git a/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> b/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> new file mode 100644
> index 00000000..1277b981
> --- /dev/null
> +++ b/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> @@ -0,0 +1,59 @@
> +/** @file
> + This file defines the Redfish HTTP cache library interface.
> +
> + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef REDFISH_HTTP_CACHE_LIB_H_
> +#define REDFISH_HTTP_CACHE_LIB_H_
> +
> +#include <Uefi.h>
> +#include <Library/RedfishLib.h>
> +
> +/**
> + Get redfish resource from given resource URI with cache mechanism
> + supported. It's caller's responsibility to Response by calling
> + RedfishFreeResponse ().
> +
> + @param[in] Service Redfish service instance to make query.
> + @param[in] Uri Target resource URI.
> + @param[out] Response HTTP response from redfish service.
> + @param[in] UseCache If it is TRUE, this function will search for
> + cache first. If it is FALSE, this function
> + will query Redfish URI directly.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +RedfishHttpGetResource (
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + OUT REDFISH_RESPONSE *Response,
> + IN BOOLEAN UseCache
> + );
> +
> +/**
> + Reset the cached data specified by given URI. When response data
> + returned by RedfishHttpResetResource() is modified, the response
> + data can not be used by other caller. Application calls this
> + function to make this data to be stale data and
> + RedfishHttpResetResource() will get latest data from remote server
> + again.
> +
> + @param[in] Uri Target resource URI.
> +
> + @retval EFI_SUCCESS Resrouce is reset successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +RedfishHttpResetResource (
> + IN EFI_STRING Uri
> + );
> +
> +#endif
> diff --git
> a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInterna
> l.h
> b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInterna
> l.h
> new file mode 100644
> index 00000000..2549335d
> --- /dev/null
> +++
> b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInterna
> l.h
> @@ -0,0 +1,63 @@
> +/** @file
> + This file defines the Redfish HTTP cache library internal headers.
> +
> + Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef REDFISH_HTTP_CACHE_INTERNAL_LIB_H_
> +#define REDFISH_HTTP_CACHE_INTERNAL_LIB_H_
> +
> +#include <Uefi.h>
> +#include <RedfishBase.h>
> +
> +#include <Library/UefiLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/RedfishLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/RedfishHttpCacheLib.h>
> +#include <Library/RedfishDebugLib.h>
> +#include <Library/ReportStatusCodeLib.h>
> +#include <Library/PrintLib.h>
> +
> +#define REDFISH_HTTP_CACHE_LIST_SIZE 0x0F
> +#define REDFISH_HTTP_GET_RETRY_MAX 0x0F
> +#define REDFISH_HTTP_RETRY_WAIT (2 * 1000000U) ///< 1 second
> +#define REDFISH_ERROR_MSG_MAX 128
> +#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP failure(0x%x): %a"
> +#define REDFISH_HTTP_CACHE_DEBUG DEBUG_VERBOSE
> +#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_VERBOSE
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_DATA
> +///
> +typedef struct {
> + LIST_ENTRY List;
> + EFI_STRING Uri;
> + UINTN HitCount;
> + REDFISH_RESPONSE *Response;
> +} REDFISH_HTTP_CACHE_DATA;
> +
> +#define REDFISH_HTTP_CACHE_FROM_LIST(a) BASE_CR (a,
> REDFISH_HTTP_CACHE_DATA, List)
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_LIST
> +///
> +typedef struct {
> + LIST_ENTRY Head;
> + UINTN Count;
> + UINTN Capacity;
> +} REDFISH_HTTP_CACHE_LIST;
> +
> +///
> +/// Definition of REDFISH_HTTP_CACHE_PRIVATE
> +///
> +typedef struct {
> + REDFISH_HTTP_CACHE_LIST CacheList;
> +} REDFISH_HTTP_CACHE_PRIVATE;
> +
> +#endif
> diff --git
> a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
> b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
> new file mode 100644
> index 00000000..c751e608
> --- /dev/null
> +++ b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
> @@ -0,0 +1,774 @@
> +/** @file
> + Redfish HTTP cache library helps Redfish application to get Redfish resource
> + from BMC with cache mechanism enabled.
> +
> + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights
> reserved.
> +
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "RedfishHttpCacheLibInternal.h"
> +
> +REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL;
> +
> +/**
> + This function copy the data in SrcResponse to DstResponse.
> +
> + @param[in] SrcResponse Source Response to copy.
> + @param[out] DstResponse Destination Response.
> +
> + @retval EFI_SUCCESS Response is copied successfully.
> + @retval Others Error occurs.
> +
> +**/
> +EFI_STATUS
> +CopyRedfishResponse (
> + IN REDFISH_RESPONSE *SrcResponse,
> + OUT REDFISH_RESPONSE *DstResponse
> + )
> +{
> + EDKII_JSON_VALUE JsonValue;
> + REDFISH_SERVICE Service;
> + UINTN Index;
> +
> + if ((SrcResponse == NULL) || (DstResponse == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (SrcResponse == DstResponse) {
> + return EFI_SUCCESS;
> + }
> +
> + //
> + // Status code
> + //
> + if (SrcResponse->StatusCode != NULL) {
> + DstResponse->StatusCode = AllocateCopyPool (sizeof
> (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
> + if (DstResponse->StatusCode == NULL) {
> + goto ON_ERROR;
> + }
> + }
> +
> + //
> + // Header
> + //
> + if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
> + DstResponse->HeaderCount = 0;
> + DstResponse->Headers = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) *
> SrcResponse->HeaderCount);
> + if (DstResponse->Headers == NULL) {
> + goto ON_ERROR;
> + }
> +
> + for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
> + DstResponse->Headers[Index].FieldName = AllocateCopyPool (AsciiStrSize
> (SrcResponse->Headers[Index].FieldName), SrcResponse-
> >Headers[Index].FieldName);
> + if (DstResponse->Headers[Index].FieldName == NULL) {
> + goto ON_ERROR;
> + }
> +
> + DstResponse->Headers[Index].FieldValue = AllocateCopyPool (AsciiStrSize
> (SrcResponse->Headers[Index].FieldValue), SrcResponse-
> >Headers[Index].FieldValue);
> + if (DstResponse->Headers[Index].FieldValue == NULL) {
> + goto ON_ERROR;
> + }
> +
> + DstResponse->HeaderCount += 1;
> + }
> + }
> +
> + //
> + // Payload
> + //
> + if (SrcResponse->Payload != NULL) {
> + Service = RedfishServiceInPayload (SrcResponse->Payload);
> + JsonValue = RedfishJsonInPayload (SrcResponse->Payload);
> + DstResponse->Payload = RedfishCreatePayload (JsonValue, Service);
> + if (DstResponse->Payload == NULL) {
> + goto ON_ERROR;
> + }
> + }
> +
> + return EFI_SUCCESS;
> +
> +ON_ERROR:
> +
> + RedfishFreeResponse (
> + DstResponse->StatusCode,
> + DstResponse->HeaderCount,
> + DstResponse->Headers,
> + DstResponse->Payload
> + );
> +
> + return EFI_OUT_OF_RESOURCES;
> +}
> +
> +/**
> + This function clone input response and return to caller
> +
> + @param[in] Response Response to clone.
> +
> + @retval REDFISH_RESPONSE * Response is cloned.
> + @retval NULL Errors occur.
> +
> +**/
> +REDFISH_RESPONSE *
> +CloneRedfishResponse (
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + EFI_STATUS Status;
> + REDFISH_RESPONSE *NewResponse;
> +
> + if (Response == NULL) {
> + return NULL;
> + }
> +
> + NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE));
> + if (NewResponse == NULL) {
> + return NULL;
> + }
> +
> + Status = CopyRedfishResponse (Response, NewResponse);
> + if (EFI_ERROR (Status)) {
> + FreePool (NewResponse);
> + return NULL;
> + }
> +
> + return NewResponse;
> +}
> +
> +/**
> +
> + Convert Unicode string to ASCII string. It's call responsibility to release
> returned buffer.
> +
> + @param[in] UnicodeStr Unicode string to convert.
> +
> + @retval CHAR8 * ASCII string returned.
> + @retval NULL Errors occur.
> +
> +**/
> +CHAR8 *
> +StringUnicodeToAscii (
> + IN EFI_STRING UnicodeStr
> + )
> +{
> + CHAR8 *AsciiStr;
> + UINTN AsciiStrSize;
> + EFI_STATUS Status;
> +
> + if (IS_EMPTY_STRING (UnicodeStr)) {
> + return NULL;
> + }
> +
> + AsciiStrSize = StrLen (UnicodeStr) + 1;
> + AsciiStr = AllocatePool (AsciiStrSize);
> + if (AsciiStr == NULL) {
> + return NULL;
> + }
> +
> + Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr, AsciiStrSize);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
> + FreePool (AsciiStr);
> + return NULL;
> + }
> +
> + return AsciiStr;
> +}
> +
> +/**
> + Release REDFISH_HTTP_CACHE_DATA resource
> +
> + @param[in] Data Pointer to REDFISH_HTTP_CACHE_DATA instance
> +
> + @retval EFI_SUCCESS REDFISH_HTTP_CACHE_DATA is released
> successfully.
> + @retval EFI_INVALID_PARAMETER Data is NULL
> +
> +**/
> +EFI_STATUS
> +ReleaseHttpCacheData (
> + IN REDFISH_HTTP_CACHE_DATA *Data
> + )
> +{
> + if (Data == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (Data->Uri != NULL) {
> + FreePool (Data->Uri);
> + }
> +
> + if (Data->Response != NULL) {
> + if (Data->Response->Payload != NULL) {
> + RedfishFreeResponse (
> + Data->Response->StatusCode,
> + Data->Response->HeaderCount,
> + Data->Response->Headers,
> + Data->Response->Payload
> + );
> + FreePool (Data->Response);
> + }
> + }
> +
> + FreePool (Data);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Create new cache data.
> +
> + @param[in] Uri The URI string matching to this cache data.
> + @param[in] Response HTTP response.
> +
> + @retval REDFISH_HTTP_CACHE_DATA * Pointer to newly created cache
> data.
> + @retval NULL No memory available.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +NewHttpCacheData (
> + IN EFI_STRING Uri,
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + REDFISH_HTTP_CACHE_DATA *NewData;
> + UINTN Size;
> +
> + if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> + return NULL;
> + }
> +
> + NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA));
> + if (NewData == NULL) {
> + return NULL;
> + }
> +
> + Size = StrSize (Uri);
> + NewData->Uri = AllocateCopyPool (Size, Uri);
> + if (NewData->Uri == NULL) {
> + goto ON_ERROR;
> + }
> +
> + NewData->Response = Response;
> + NewData->HitCount = 1;
> +
> + return NewData;
> +
> +ON_ERROR:
> +
> + if (NewData != NULL) {
> + ReleaseHttpCacheData (NewData);
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + Search on given ListHeader for given URI string.
> +
> + @param[in] ListHeader Target list to search.
> + @param[in] Uri Target URI to search.
> +
> + @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
> + @retval NULL No cache data with given URI is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindHttpCacheData (
> + IN LIST_ENTRY *ListHeader,
> + IN EFI_STRING Uri
> + )
> +{
> + LIST_ENTRY *List;
> + REDFISH_HTTP_CACHE_DATA *Data;
> +
> + if (IS_EMPTY_STRING (Uri)) {
> + return NULL;
> + }
> +
> + if (IsListEmpty (ListHeader)) {
> + return NULL;
> + }
> +
> + Data = NULL;
> + List = GetFirstNode (ListHeader);
> + while (!IsNull (ListHeader, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> + if (StrCmp (Data->Uri, Uri) == 0) {
> + return Data;
> + }
> +
> + List = GetNextNode (ListHeader, List);
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + Search on given ListHeader and return cache data with minimum hit count.
> +
> + @param[in] ListHeader Target list to search.
> +
> + @retval REDFISH_HTTP_CACHE_DATA Target cache data is returned.
> + @retval NULL No cache data is found.
> +
> +**/
> +REDFISH_HTTP_CACHE_DATA *
> +FindUnusedHttpCacheData (
> + IN LIST_ENTRY *ListHeader
> + )
> +{
> + LIST_ENTRY *List;
> + REDFISH_HTTP_CACHE_DATA *Data;
> + REDFISH_HTTP_CACHE_DATA *UnusedData;
> + UINTN HitCount;
> +
> + if (IsListEmpty (ListHeader)) {
> + return NULL;
> + }
> +
> + Data = NULL;
> + UnusedData = NULL;
> + HitCount = 0;
> +
> + List = GetFirstNode (ListHeader);
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> + UnusedData = Data;
> + HitCount = Data->HitCount;
> + List = GetNextNode (ListHeader, List);
> +
> + while (!IsNull (ListHeader, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> + if (Data->HitCount < HitCount) {
> + HitCount = Data->HitCount;
> + UnusedData = Data;
> + }
> +
> + List = GetNextNode (ListHeader, List);
> + }
> +
> + return UnusedData;
> +}
> +
> +/**
> + Delete a cache data by given cache instance.
> +
> + @param[in] List Target cache list to be removed.
> + @param[in] Data Pointer to the instance to be deleted.
> +
> + @retval EFI_SUCCESS Cache data is removed.
> + @retval Others Fail to remove cache data.
> +
> +**/
> +EFI_STATUS
> +DeleteHttpCacheData (
> + IN REDFISH_HTTP_CACHE_LIST *List,
> + IN REDFISH_HTTP_CACHE_DATA *Data
> + )
> +{
> + if ((List == NULL) || (Data == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__,
> Data->Uri));
> +
> + RemoveEntryList (&Data->List);
> + --List->Count;
> +
> + return ReleaseHttpCacheData (Data);
> +}
> +
> +/**
> + Add new cache by given URI and HTTP response to specify List.
> +
> + @param[in] List Target cache list to add.
> + @param[in] Uri The URI string matching to this cache data.
> + @param[in] Response HTTP response.
> +
> + @retval EFI_SUCCESS Cache data is added.
> + @retval Others Fail to add cache data.
> +
> +**/
> +EFI_STATUS
> +AddHttpCacheData (
> + IN REDFISH_HTTP_CACHE_LIST *List,
> + IN EFI_STRING Uri,
> + IN REDFISH_RESPONSE *Response
> + )
> +{
> + REDFISH_HTTP_CACHE_DATA *NewData;
> + REDFISH_HTTP_CACHE_DATA *OldData;
> + REDFISH_HTTP_CACHE_DATA *UnusedData;
> + REDFISH_RESPONSE *NewResponse;
> +
> + if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + //
> + // If same cache data exist, replace it with latest one.
> + //
> + OldData = FindHttpCacheData (&List->Head, Uri);
> + if (OldData != NULL) {
> + DeleteHttpCacheData (List, OldData);
> + }
> +
> + //
> + // Check capacity
> + //
> + if (List->Count >= List->Capacity) {
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire unused
> cache\n", __func__));
> + UnusedData = FindUnusedHttpCacheData (&List->Head);
> + if (UnusedData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + DeleteHttpCacheData (List, UnusedData);
> + }
> +
> + //
> + // Clone a local copy
> + //
> + NewResponse = CloneRedfishResponse (Response);
> + if (NewResponse == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + NewData = NewHttpCacheData (Uri, NewResponse);
> + if (NewData == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + InsertTailList (&List->Head, &NewData->List);
> + ++List->Count;
> +
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n",
> __func__, List->Count, List->Capacity, NewData->Uri));
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Release all cache from list.
> +
> + @param[in] CacheList The list to be released.
> +
> + @retval EFI_SUCCESS All cache data are released.
> + @retval EFI_INVALID_PARAMETER CacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +ReleaseCacheList (
> + IN REDFISH_HTTP_CACHE_LIST *CacheList
> + )
> +{
> + LIST_ENTRY *List;
> + LIST_ENTRY *Next;
> + REDFISH_HTTP_CACHE_DATA *Data;
> +
> + if (CacheList == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (IsListEmpty (&CacheList->Head)) {
> + return EFI_SUCCESS;
> + }
> +
> + Data = NULL;
> + Next = NULL;
> + List = GetFirstNode (&CacheList->Head);
> + while (!IsNull (&CacheList->Head, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> + Next = GetNextNode (&CacheList->Head, List);
> +
> + DeleteHttpCacheData (CacheList, Data);
> +
> + List = Next;
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Debug output the cache list.
> +
> + @param[in] Msg Debug message string.
> + @param[in] ErrorLevel Output error level.
> + @param[in] CacheList Target list to dump.
> +
> + @retval EFI_SUCCESS Debug dump finished.
> + @retval EFI_INVALID_PARAMETER HttpCacheList is NULL.
> +
> +**/
> +EFI_STATUS
> +DumpHttpCacheList (
> + IN CONST CHAR8 *Msg,
> + IN UINTN ErrorLevel,
> + IN REDFISH_HTTP_CACHE_LIST *CacheList
> + )
> +{
> + LIST_ENTRY *List;
> + REDFISH_HTTP_CACHE_DATA *Data;
> + UINTN Index;
> +
> + if (CacheList == NULL) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (!IS_EMPTY_STRING (Msg)) {
> + DEBUG ((ErrorLevel, "%a\n", Msg));
> + }
> +
> + if (IsListEmpty (&CacheList->Head)) {
> + DEBUG ((ErrorLevel, "list is empty\n"));
> + return EFI_NOT_FOUND;
> + }
> +
> + DEBUG ((ErrorLevel, "list count: %d capacity: %d\n", CacheList->Count,
> CacheList->Capacity));
> + Data = NULL;
> + Index = 0;
> + List = GetFirstNode (&CacheList->Head);
> + while (!IsNull (&CacheList->Head, List)) {
> + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> +
> + DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri, Data-
> >HitCount));
> +
> + List = GetNextNode (&CacheList->Head, List);
> + }
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Get redfish resource from given resource URI with cache mechanism
> + supported. It's caller's responsibility to Response by calling
> + RedfishFreeResponse ().
> +
> + @param[in] Service Redfish service instance to make query.
> + @param[in] Uri Target resource URI.
> + @param[out] Response HTTP response from redfish service.
> + @param[in] UseCache If it is TRUE, this function will search for
> + cache first. If it is FALSE, this function
> + will query Redfish URI directly.
> +
> + @retval EFI_SUCCESS Resrouce is returned successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +RedfishHttpGetResource (
> + IN REDFISH_SERVICE Service,
> + IN EFI_STRING Uri,
> + OUT REDFISH_RESPONSE *Response,
> + IN BOOLEAN UseCache
> + )
> +{
> + EFI_STATUS Status;
> + CHAR8 *AsciiUri;
> + REDFISH_HTTP_CACHE_DATA *CacheData;
> + UINTN RetryCount;
> +
> + if ((Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (mRedfishHttpCachePrivate == NULL) {
> + return EFI_NOT_READY;
> + }
> +
> + AsciiUri = NULL;
> + CacheData = NULL;
> + RetryCount = 0;
> +
> + //
> + // Search for cache list.
> + //
> + if (UseCache) {
> + CacheData = FindHttpCacheData (&mRedfishHttpCachePrivate-
> >CacheList.Head, Uri);
> + if (CacheData != NULL) {
> + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache hit! %s\n",
> __func__, Uri));
> +
> + //
> + // Copy cached response to caller's buffer.
> + //
> + Status = CopyRedfishResponse (CacheData->Response, Response);
> + CacheData->HitCount += 1;
> + return Status;
> + }
> + }
> +
> + AsciiUri = StringUnicodeToAscii (Uri);
> + if (AsciiUri == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Get resource from redfish service.
> + //
> + do {
> + RetryCount += 1;
> + Status = RedfishGetByUri (
> + Service,
> + AsciiUri,
> + Response
> + );
> + if (!EFI_ERROR (Status) || (RetryCount >=
> REDFISH_HTTP_GET_RETRY_MAX)) {
> + break;
> + }
> +
> + //
> + // Retry when BMC is not ready.
> + //
> + if ((Response->StatusCode != NULL)) {
> + DEBUG_CODE (
> + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> + );
> +
> + if (*Response->StatusCode !=
> HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
> + break;
> + }
> +
> + FreePool (Response->StatusCode);
> + Response->StatusCode = NULL;
> + }
> +
> + DEBUG ((DEBUG_WARN, "%a: RedfishGetByUri failed, retry (%d/%d)\n",
> __func__, RetryCount, REDFISH_HTTP_GET_RETRY_MAX));
> + gBS->Stall (REDFISH_HTTP_RETRY_WAIT);
> + } while (TRUE);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "%a: get %a failed (%d/%d): %r\n", __func__,
> AsciiUri, RetryCount, REDFISH_HTTP_GET_RETRY_MAX, Status));
> + if (Response->Payload != NULL) {
> + RedfishFreeResponse (
> + NULL,
> + 0,
> + NULL,
> + Response->Payload
> + );
> + Response->Payload = NULL;
> + }
> +
> + goto ON_RELEASE;
> + }
> +
> + //
> + // Keep response in cache list
> + //
> + Status = AddHttpCacheData (&mRedfishHttpCachePrivate->CacheList, Uri,
> Response);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__, Uri,
> Status));
> + goto ON_RELEASE;
> + }
> +
> + DEBUG_CODE (
> + DumpHttpCacheList (__func__, REDFISH_HTTP_CACHE_DEBUG_DUMP,
> &mRedfishHttpCachePrivate->CacheList);
> + );
> +
> +ON_RELEASE:
> +
> + if (AsciiUri != NULL) {
> + FreePool (AsciiUri);
> + }
> +
> + return Status;
> +}
> +
> +/**
> + Reset the cached data specified by given URI. When response data
> + returned by RedfishHttpResetResource() is modified, the response
> + data can not be used by other caller. Application calls this
> + function to make this data to be stale data and
> + RedfishHttpResetResource() will get latest data from remote server
> + again.
> +
> + @param[in] Uri Target resource URI.
> +
> + @retval EFI_SUCCESS Resrouce is reset successfully.
> + @retval Others Errors occur.
> +
> +**/
> +EFI_STATUS
> +RedfishHttpResetResource (
> + IN EFI_STRING Uri
> + )
> +{
> + REDFISH_HTTP_CACHE_DATA *CacheData;
> +
> + if (IS_EMPTY_STRING (Uri)) {
> + return EFI_INVALID_PARAMETER;
> + }
> +
> + if (mRedfishHttpCachePrivate == NULL) {
> + return EFI_NOT_READY;
> + }
> +
> + CacheData = FindHttpCacheData (&mRedfishHttpCachePrivate-
> >CacheList.Head, Uri);
> + if (CacheData == NULL) {
> + return EFI_NOT_FOUND;
> + }
> +
> + DeleteHttpCacheData (&mRedfishHttpCachePrivate->CacheList, CacheData);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> +
> + Initial HTTP cache library instance.
> +
> + @param[in] ImageHandle The image handle.
> + @param[in] SystemTable The system table.
> +
> + @retval EFI_SUCCESS Initial library successfully.
> + @retval Other Return error status.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishHttpCacheConstructor (
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + mRedfishHttpCachePrivate = AllocateZeroPool (sizeof
> (REDFISH_HTTP_CACHE_PRIVATE));
> + if (mRedfishHttpCachePrivate == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Initial cache list
> + //
> + mRedfishHttpCachePrivate->CacheList.Capacity =
> REDFISH_HTTP_CACHE_LIST_SIZE;
> + mRedfishHttpCachePrivate->CacheList.Count = 0x00;
> + InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
> +
> + return EFI_SUCCESS;
> +}
> +
> +/**
> + Release allocated resource.
> +
> + @param[in] ImageHandle Handle that identifies the image to be
> unloaded.
> + @param[in] SystemTable The system table.
> +
> + @retval EFI_SUCCESS The image has been unloaded.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +RedfishHttpCacheDestructor (
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + if (mRedfishHttpCachePrivate != NULL) {
> + if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
> + ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
> + }
> +
> + FreePool (mRedfishHttpCachePrivate);
> + mRedfishHttpCachePrivate = NULL;
> + }
> +
> + return EFI_SUCCESS;
> +}
> --
> 2.34.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113085): https://edk2.groups.io/g/devel/message/113085
Mute This Topic: https://groups.io/mt/103500371/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [edk2-devel] [edk2-redfish-client][PATCH 01/10] RedfishClientPkg: introduce Redfish HTTP cache library
2024-01-03 13:01 ` Chang, Abner via groups.io
@ 2024-01-04 2:33 ` Nickle Wang via groups.io
0 siblings, 0 replies; 3+ messages in thread
From: Nickle Wang via groups.io @ 2024-01-04 2:33 UTC (permalink / raw)
To: devel@edk2.groups.io, abner.chang@amd.com; +Cc: Igor Kulchytskyy, Nick Ramirez
Thanks Abner. Version 2 patch series is sent for review.
Regards,
Nickle
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Chang, Abner
> via groups.io
> Sent: Wednesday, January 3, 2024 9:02 PM
> To: Nickle Wang <nicklew@nvidia.com>; devel@edk2.groups.io
> Cc: Igor Kulchytskyy <igork@ami.com>; Nick Ramirez <nramirez@nvidia.com>
> Subject: Re: [edk2-devel] [edk2-redfish-client][PATCH 01/10] RedfishClientPkg:
> introduce Redfish HTTP cache library
>
> External email: Use caution opening links or attachments
>
>
> [AMD Official Use Only - General]
>
> Hi Nickle,
> I have no problem with this feature as we have been using this for a while. Per
> discussion off line, HttpCacheLibrary would be replaced with protocol version
> later. I would just give my RB to this patch as this patch is sent for cleaning up the
> backlogs we are going to upstream.
> But before we merging this patch, could you please replace all "BMC" term in
> these files with either Redfish service or service? Thanks.
>
> Abner
>
> > -----Original Message-----
> > From: Nickle Wang <nicklew@nvidia.com>
> > Sent: Wednesday, January 3, 2024 8:00 PM
> > To: devel@edk2.groups.io
> > Cc: Chang, Abner <Abner.Chang@amd.com>; Igor Kulchytskyy
> > <igork@ami.com>; Nick Ramirez <nramirez@nvidia.com>
> > Subject: [edk2-redfish-client][PATCH 01/10] RedfishClientPkg:
> > introduce Redfish HTTP cache library
> >
> > Caution: This message originated from an External Source. Use proper
> > caution when opening attachments, clicking links, or responding.
> >
> >
> > Introduce RedfishHttpCacheLib to improve HTTP GET performance in
> > Redfish feature drivers. Feature drivers often query same Redfish
> > resource multiple times for different purpose. Add HTTP cache
> > mechanism to improve the performance.
> >
> > Signed-off-by: Nickle Wang <nicklew@nvidia.com>
> > Cc: Abner Chang <abner.chang@amd.com>
> > Cc: Igor Kulchytskyy <igork@ami.com>
> > Cc: Nick Ramirez <nramirez@nvidia.com>
> > ---
> > RedfishClientPkg/RedfishClientPkg.dec | 3 +-
> > RedfishClientPkg/RedfishClientLibs.dsc.inc | 3 +-
> > RedfishClientPkg/RedfishClientPkg.dsc | 3 +-
> > .../RedfishHttpCacheLib.inf | 48 ++
> > .../Include/Library/RedfishHttpCacheLib.h | 59 ++
> > .../RedfishHttpCacheLibInternal.h | 63 ++
> > .../RedfishHttpCacheLib/RedfishHttpCacheLib.c | 774
> > ++++++++++++++++++
> > 7 files changed, 950 insertions(+), 3 deletions(-) create mode
> > 100644
> > RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> > create mode 100644
> > RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> > create mode 100644
> > RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibIntern
> > al.h
> > create mode 100644
> > RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
> >
> > diff --git a/RedfishClientPkg/RedfishClientPkg.dec
> > b/RedfishClientPkg/RedfishClientPkg.dec
> > index 5f8a0350..b350faca 100644
> > --- a/RedfishClientPkg/RedfishClientPkg.dec
> > +++ b/RedfishClientPkg/RedfishClientPkg.dec
> > @@ -2,7 +2,7 @@
> > # Redfish Client Package
> > #
> > # (C) Copyright 2021-2022 Hewlett Packard Enterprise Development
> > LP<BR> -# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES.
> > All rights reserved.
> > +# Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All
> > +rights
> > reserved.
> > #
> > # SPDX-License-Identifier: BSD-2-Clause-Patent ## @@ -26,6 +26,7 @@
> >
> > EdkIIRedfishResourceConfigLib|Include/Library/EdkIIRedfishResourceConf
> > EdkIIRedfishResourceConfigLib|igLi
> > b.h
> > RedfishEventLib|Include/Library/RedfishEventLib.h
> > RedfishVersionLib|Include/Library/RedfishVersionLib.h
> > + RedfishHttpCacheLib|Include/Library/RedfishHttpCacheLib.h
> >
> > [LibraryClasses.Common.Private]
> > ## @libraryclass Redfish Helper Library diff --git
> > a/RedfishClientPkg/RedfishClientLibs.dsc.inc
> > b/RedfishClientPkg/RedfishClientLibs.dsc.inc
> > index 5eae6711..572f426e 100644
> > --- a/RedfishClientPkg/RedfishClientLibs.dsc.inc
> > +++ b/RedfishClientPkg/RedfishClientLibs.dsc.inc
> > @@ -6,7 +6,7 @@
> > # of EDKII network library classes.
> > #
> > # (C) Copyright 2021-2022 Hewlett Packard Enterprise Development
> > LP<BR> -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All
> > rights reserved.
> > +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All
> > +rights
> > reserved.
> > #
> > # SPDX-License-Identifier: BSD-2-Clause-Patent
> > #
> > @@ -40,3 +40,4 @@
> >
> > RedfishVersionLib|RedfishClientPkg/Library/RedfishVersionLib/RedfishVe
> > RedfishVersionLib|rsio
> > nLib.inf
> >
> > RedfishAddendumLib|RedfishClientPkg/Library/RedfishAddendumLib/Redfis
> > hAddendumLib.inf
> >
> > RedfishDebugLib|RedfishPkg/Library/RedfishDebugLib/RedfishDebugLib.inf
> > +
> > RedfishHttpCacheLib|RedfishClientPkg/Library/RedfishHttpCacheLib/Redfi
> > RedfishHttpCacheLib|sh
> > HttpCacheLib.inf
> > diff --git a/RedfishClientPkg/RedfishClientPkg.dsc
> > b/RedfishClientPkg/RedfishClientPkg.dsc
> > index e16c91b8..0e3ef1ac 100644
> > --- a/RedfishClientPkg/RedfishClientPkg.dsc
> > +++ b/RedfishClientPkg/RedfishClientPkg.dsc
> > @@ -2,7 +2,7 @@
> > # Redfish Client Package
> > #
> > # (C) Copyright 2021 Hewlett-Packard Enterprise Development LP.
> > -# Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All
> > +rights
> > reserved.
> > #
> > # SPDX-License-Identifier: BSD-2-Clause-Patent
> > #
> > @@ -61,5 +61,6 @@
> >
> > RedfishClientPkg/Library/RedfishFeatureUtilityLib/RedfishFeatureUtilityLib.inf
> > RedfishClientPkg/PrivateLibrary/RedfishLib/RedfishLib.inf
> > RedfishClientPkg/Library/RedfishAddendumLib/RedfishAddendumLib.inf
> > +
> > + RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> >
> > !include RedfishClientPkg/RedfishClient.dsc.inc
> > diff --git
> > a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> > b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> > new file mode 100644
> > index 00000000..32da4f83
> > --- /dev/null
> > +++
> > b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.inf
> > @@ -0,0 +1,48 @@
> > +## @file
> > +# Redfish HTTP cache library helps Redfish application to get
> > +Redfish
> > resource
> > +# from BMC with cache mechanism enabled.
> > +#
> > +# Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All
> > +rights
> > reserved.
> > +#
> > +# SPDX-License-Identifier: BSD-2-Clause-Patent # ##
> > +
> > +[Defines]
> > + INF_VERSION = 0x00010006
> > + BASE_NAME = RedfishHttpCacheLib
> > + FILE_GUID = 21F8FEEC-023C-451D-824D-823058FD9481
> > + MODULE_TYPE = DXE_DRIVER
> > + VERSION_STRING = 1.0
> > + LIBRARY_CLASS = RedfishHttpCacheLib| DXE_DRIVER UEFI_DRIVER
> > + CONSTRUCTOR = RedfishHttpCacheConstructor
> > + DESTRUCTOR = RedfishHttpCacheDestructor
> > +
> > +#
> > +# VALID_ARCHITECTURES = IA32 X64 EBC
> > +#
> > +
> > +[Sources]
> > + RedfishHttpCacheLibInternal.h
> > + RedfishHttpCacheLib.c
> > +
> > +[Packages]
> > + MdePkg/MdePkg.dec
> > + MdeModulePkg/MdeModulePkg.dec
> > + RedfishPkg/RedfishPkg.dec
> > + RedfishClientPkg/RedfishClientPkg.dec
> > +
> > +[LibraryClasses]
> > + BaseLib
> > + DebugLib
> > + UefiBootServicesTableLib
> > + MemoryAllocationLib
> > + RedfishLib
> > + UefiLib
> > + RedfishDebugLib
> > + ReportStatusCodeLib
> > + PrintLib
> > +
> > +[depex]
> > + TRUE
> > +
> > diff --git a/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> > b/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> > new file mode 100644
> > index 00000000..1277b981
> > --- /dev/null
> > +++ b/RedfishClientPkg/Include/Library/RedfishHttpCacheLib.h
> > @@ -0,0 +1,59 @@
> > +/** @file
> > + This file defines the Redfish HTTP cache library interface.
> > +
> > + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All
> > + rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#ifndef REDFISH_HTTP_CACHE_LIB_H_
> > +#define REDFISH_HTTP_CACHE_LIB_H_
> > +
> > +#include <Uefi.h>
> > +#include <Library/RedfishLib.h>
> > +
> > +/**
> > + Get redfish resource from given resource URI with cache mechanism
> > + supported. It's caller's responsibility to Response by calling
> > + RedfishFreeResponse ().
> > +
> > + @param[in] Service Redfish service instance to make query.
> > + @param[in] Uri Target resource URI.
> > + @param[out] Response HTTP response from redfish service.
> > + @param[in] UseCache If it is TRUE, this function will search for
> > + cache first. If it is FALSE, this function
> > + will query Redfish URI directly.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +RedfishHttpGetResource (
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + OUT REDFISH_RESPONSE *Response,
> > + IN BOOLEAN UseCache
> > + );
> > +
> > +/**
> > + Reset the cached data specified by given URI. When response data
> > + returned by RedfishHttpResetResource() is modified, the response
> > + data can not be used by other caller. Application calls this
> > + function to make this data to be stale data and
> > + RedfishHttpResetResource() will get latest data from remote server
> > + again.
> > +
> > + @param[in] Uri Target resource URI.
> > +
> > + @retval EFI_SUCCESS Resrouce is reset successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +RedfishHttpResetResource (
> > + IN EFI_STRING Uri
> > + );
> > +
> > +#endif
> > diff --git
> > a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInte
> > rna
> > l.h
> > b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInte
> > rna
> > l.h
> > new file mode 100644
> > index 00000000..2549335d
> > --- /dev/null
> > +++
> > b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLibInte
> > rna
> > l.h
> > @@ -0,0 +1,63 @@
> > +/** @file
> > + This file defines the Redfish HTTP cache library internal headers.
> > +
> > + Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#ifndef REDFISH_HTTP_CACHE_INTERNAL_LIB_H_
> > +#define REDFISH_HTTP_CACHE_INTERNAL_LIB_H_
> > +
> > +#include <Uefi.h>
> > +#include <RedfishBase.h>
> > +
> > +#include <Library/UefiLib.h>
> > +#include <Library/BaseLib.h>
> > +#include <Library/DebugLib.h>
> > +#include <Library/RedfishLib.h>
> > +#include <Library/UefiBootServicesTableLib.h>
> > +#include <Library/MemoryAllocationLib.h> #include
> > +<Library/RedfishHttpCacheLib.h> #include <Library/RedfishDebugLib.h>
> > +#include <Library/ReportStatusCodeLib.h> #include
> > +<Library/PrintLib.h>
> > +
> > +#define REDFISH_HTTP_CACHE_LIST_SIZE 0x0F
> > +#define REDFISH_HTTP_GET_RETRY_MAX 0x0F
> > +#define REDFISH_HTTP_RETRY_WAIT (2 * 1000000U) ///< 1 second
> > +#define REDFISH_ERROR_MSG_MAX 128
> > +#define REDFISH_HTTP_ERROR_REPORT "Redfish HTTP failure(0x%x): %a"
> > +#define REDFISH_HTTP_CACHE_DEBUG DEBUG_VERBOSE
> > +#define REDFISH_HTTP_CACHE_DEBUG_DUMP DEBUG_VERBOSE
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_CACHE_DATA /// typedef struct {
> > + LIST_ENTRY List;
> > + EFI_STRING Uri;
> > + UINTN HitCount;
> > + REDFISH_RESPONSE *Response;
> > +} REDFISH_HTTP_CACHE_DATA;
> > +
> > +#define REDFISH_HTTP_CACHE_FROM_LIST(a) BASE_CR (a,
> > REDFISH_HTTP_CACHE_DATA, List)
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_CACHE_LIST /// typedef struct {
> > + LIST_ENTRY Head;
> > + UINTN Count;
> > + UINTN Capacity;
> > +} REDFISH_HTTP_CACHE_LIST;
> > +
> > +///
> > +/// Definition of REDFISH_HTTP_CACHE_PRIVATE /// typedef struct {
> > + REDFISH_HTTP_CACHE_LIST CacheList;
> > +} REDFISH_HTTP_CACHE_PRIVATE;
> > +
> > +#endif
> > diff --git
> > a/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
> > b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib.c
> > new file mode 100644
> > index 00000000..c751e608
> > --- /dev/null
> > +++ b/RedfishClientPkg/Library/RedfishHttpCacheLib/RedfishHttpCacheLib
> > +++ .c
> > @@ -0,0 +1,774 @@
> > +/** @file
> > + Redfish HTTP cache library helps Redfish application to get Redfish
> > +resource
> > + from BMC with cache mechanism enabled.
> > +
> > + Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All
> > + rights
> > reserved.
> > +
> > + SPDX-License-Identifier: BSD-2-Clause-Patent
> > +
> > +**/
> > +
> > +#include "RedfishHttpCacheLibInternal.h"
> > +
> > +REDFISH_HTTP_CACHE_PRIVATE *mRedfishHttpCachePrivate = NULL;
> > +
> > +/**
> > + This function copy the data in SrcResponse to DstResponse.
> > +
> > + @param[in] SrcResponse Source Response to copy.
> > + @param[out] DstResponse Destination Response.
> > +
> > + @retval EFI_SUCCESS Response is copied successfully.
> > + @retval Others Error occurs.
> > +
> > +**/
> > +EFI_STATUS
> > +CopyRedfishResponse (
> > + IN REDFISH_RESPONSE *SrcResponse,
> > + OUT REDFISH_RESPONSE *DstResponse
> > + )
> > +{
> > + EDKII_JSON_VALUE JsonValue;
> > + REDFISH_SERVICE Service;
> > + UINTN Index;
> > +
> > + if ((SrcResponse == NULL) || (DstResponse == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (SrcResponse == DstResponse) {
> > + return EFI_SUCCESS;
> > + }
> > +
> > + //
> > + // Status code
> > + //
> > + if (SrcResponse->StatusCode != NULL) {
> > + DstResponse->StatusCode = AllocateCopyPool (sizeof
> > (EFI_HTTP_STATUS_CODE), SrcResponse->StatusCode);
> > + if (DstResponse->StatusCode == NULL) {
> > + goto ON_ERROR;
> > + }
> > + }
> > +
> > + //
> > + // Header
> > + //
> > + if ((SrcResponse->HeaderCount > 0) && (SrcResponse->Headers != NULL)) {
> > + DstResponse->HeaderCount = 0;
> > + DstResponse->Headers = AllocateZeroPool (sizeof (EFI_HTTP_HEADER) *
> > SrcResponse->HeaderCount);
> > + if (DstResponse->Headers == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + for (Index = 0; Index < SrcResponse->HeaderCount; Index++) {
> > + DstResponse->Headers[Index].FieldName = AllocateCopyPool
> > + (AsciiStrSize
> > (SrcResponse->Headers[Index].FieldName), SrcResponse-
> > >Headers[Index].FieldName);
> > + if (DstResponse->Headers[Index].FieldName == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + DstResponse->Headers[Index].FieldValue = AllocateCopyPool
> > + (AsciiStrSize
> > (SrcResponse->Headers[Index].FieldValue), SrcResponse-
> > >Headers[Index].FieldValue);
> > + if (DstResponse->Headers[Index].FieldValue == NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + DstResponse->HeaderCount += 1;
> > + }
> > + }
> > +
> > + //
> > + // Payload
> > + //
> > + if (SrcResponse->Payload != NULL) {
> > + Service = RedfishServiceInPayload (SrcResponse->Payload);
> > + JsonValue = RedfishJsonInPayload (SrcResponse->Payload);
> > + DstResponse->Payload = RedfishCreatePayload (JsonValue, Service);
> > + if (DstResponse->Payload == NULL) {
> > + goto ON_ERROR;
> > + }
> > + }
> > +
> > + return EFI_SUCCESS;
> > +
> > +ON_ERROR:
> > +
> > + RedfishFreeResponse (
> > + DstResponse->StatusCode,
> > + DstResponse->HeaderCount,
> > + DstResponse->Headers,
> > + DstResponse->Payload
> > + );
> > +
> > + return EFI_OUT_OF_RESOURCES;
> > +}
> > +
> > +/**
> > + This function clone input response and return to caller
> > +
> > + @param[in] Response Response to clone.
> > +
> > + @retval REDFISH_RESPONSE * Response is cloned.
> > + @retval NULL Errors occur.
> > +
> > +**/
> > +REDFISH_RESPONSE *
> > +CloneRedfishResponse (
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + EFI_STATUS Status;
> > + REDFISH_RESPONSE *NewResponse;
> > +
> > + if (Response == NULL) {
> > + return NULL;
> > + }
> > +
> > + NewResponse = AllocateZeroPool (sizeof (REDFISH_RESPONSE)); if
> > + (NewResponse == NULL) {
> > + return NULL;
> > + }
> > +
> > + Status = CopyRedfishResponse (Response, NewResponse); if
> > + (EFI_ERROR (Status)) {
> > + FreePool (NewResponse);
> > + return NULL;
> > + }
> > +
> > + return NewResponse;
> > +}
> > +
> > +/**
> > +
> > + Convert Unicode string to ASCII string. It's call responsibility to
> > + release
> > returned buffer.
> > +
> > + @param[in] UnicodeStr Unicode string to convert.
> > +
> > + @retval CHAR8 * ASCII string returned.
> > + @retval NULL Errors occur.
> > +
> > +**/
> > +CHAR8 *
> > +StringUnicodeToAscii (
> > + IN EFI_STRING UnicodeStr
> > + )
> > +{
> > + CHAR8 *AsciiStr;
> > + UINTN AsciiStrSize;
> > + EFI_STATUS Status;
> > +
> > + if (IS_EMPTY_STRING (UnicodeStr)) {
> > + return NULL;
> > + }
> > +
> > + AsciiStrSize = StrLen (UnicodeStr) + 1;
> > + AsciiStr = AllocatePool (AsciiStrSize);
> > + if (AsciiStr == NULL) {
> > + return NULL;
> > + }
> > +
> > + Status = UnicodeStrToAsciiStrS (UnicodeStr, AsciiStr,
> > + AsciiStrSize); if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "UnicodeStrToAsciiStrS failed: %r\n", Status));
> > + FreePool (AsciiStr);
> > + return NULL;
> > + }
> > +
> > + return AsciiStr;
> > +}
> > +
> > +/**
> > + Release REDFISH_HTTP_CACHE_DATA resource
> > +
> > + @param[in] Data Pointer to REDFISH_HTTP_CACHE_DATA instance
> > +
> > + @retval EFI_SUCCESS REDFISH_HTTP_CACHE_DATA is released
> > successfully.
> > + @retval EFI_INVALID_PARAMETER Data is NULL
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_DATA *Data
> > + )
> > +{
> > + if (Data == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (Data->Uri != NULL) {
> > + FreePool (Data->Uri);
> > + }
> > +
> > + if (Data->Response != NULL) {
> > + if (Data->Response->Payload != NULL) {
> > + RedfishFreeResponse (
> > + Data->Response->StatusCode,
> > + Data->Response->HeaderCount,
> > + Data->Response->Headers,
> > + Data->Response->Payload
> > + );
> > + FreePool (Data->Response);
> > + }
> > + }
> > +
> > + FreePool (Data);
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Create new cache data.
> > +
> > + @param[in] Uri The URI string matching to this cache data.
> > + @param[in] Response HTTP response.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA * Pointer to newly created cache
> > data.
> > + @retval NULL No memory available.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +NewHttpCacheData (
> > + IN EFI_STRING Uri,
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + REDFISH_HTTP_CACHE_DATA *NewData;
> > + UINTN Size;
> > +
> > + if (IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> > + return NULL;
> > + }
> > +
> > + NewData = AllocateZeroPool (sizeof (REDFISH_HTTP_CACHE_DATA)); if
> > + (NewData == NULL) {
> > + return NULL;
> > + }
> > +
> > + Size = StrSize (Uri);
> > + NewData->Uri = AllocateCopyPool (Size, Uri); if (NewData->Uri ==
> > + NULL) {
> > + goto ON_ERROR;
> > + }
> > +
> > + NewData->Response = Response;
> > + NewData->HitCount = 1;
> > +
> > + return NewData;
> > +
> > +ON_ERROR:
> > +
> > + if (NewData != NULL) {
> > + ReleaseHttpCacheData (NewData);
> > + }
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + Search on given ListHeader for given URI string.
> > +
> > + @param[in] ListHeader Target list to search.
> > + @param[in] Uri Target URI to search.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA Target cache data is found.
> > + @retval NULL No cache data with given URI is found.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +FindHttpCacheData (
> > + IN LIST_ENTRY *ListHeader,
> > + IN EFI_STRING Uri
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > +
> > + if (IS_EMPTY_STRING (Uri)) {
> > + return NULL;
> > + }
> > +
> > + if (IsListEmpty (ListHeader)) {
> > + return NULL;
> > + }
> > +
> > + Data = NULL;
> > + List = GetFirstNode (ListHeader);
> > + while (!IsNull (ListHeader, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > +
> > + if (StrCmp (Data->Uri, Uri) == 0) {
> > + return Data;
> > + }
> > +
> > + List = GetNextNode (ListHeader, List); }
> > +
> > + return NULL;
> > +}
> > +
> > +/**
> > + Search on given ListHeader and return cache data with minimum hit count.
> > +
> > + @param[in] ListHeader Target list to search.
> > +
> > + @retval REDFISH_HTTP_CACHE_DATA Target cache data is returned.
> > + @retval NULL No cache data is found.
> > +
> > +**/
> > +REDFISH_HTTP_CACHE_DATA *
> > +FindUnusedHttpCacheData (
> > + IN LIST_ENTRY *ListHeader
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > + REDFISH_HTTP_CACHE_DATA *UnusedData;
> > + UINTN HitCount;
> > +
> > + if (IsListEmpty (ListHeader)) {
> > + return NULL;
> > + }
> > +
> > + Data = NULL;
> > + UnusedData = NULL;
> > + HitCount = 0;
> > +
> > + List = GetFirstNode (ListHeader);
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > + UnusedData = Data;
> > + HitCount = Data->HitCount;
> > + List = GetNextNode (ListHeader, List);
> > +
> > + while (!IsNull (ListHeader, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > +
> > + if (Data->HitCount < HitCount) {
> > + HitCount = Data->HitCount;
> > + UnusedData = Data;
> > + }
> > +
> > + List = GetNextNode (ListHeader, List); }
> > +
> > + return UnusedData;
> > +}
> > +
> > +/**
> > + Delete a cache data by given cache instance.
> > +
> > + @param[in] List Target cache list to be removed.
> > + @param[in] Data Pointer to the instance to be deleted.
> > +
> > + @retval EFI_SUCCESS Cache data is removed.
> > + @retval Others Fail to remove cache data.
> > +
> > +**/
> > +EFI_STATUS
> > +DeleteHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_LIST *List,
> > + IN REDFISH_HTTP_CACHE_DATA *Data
> > + )
> > +{
> > + if ((List == NULL) || (Data == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: delete: %s\n", __func__,
> > Data->Uri));
> > +
> > + RemoveEntryList (&Data->List);
> > + --List->Count;
> > +
> > + return ReleaseHttpCacheData (Data); }
> > +
> > +/**
> > + Add new cache by given URI and HTTP response to specify List.
> > +
> > + @param[in] List Target cache list to add.
> > + @param[in] Uri The URI string matching to this cache data.
> > + @param[in] Response HTTP response.
> > +
> > + @retval EFI_SUCCESS Cache data is added.
> > + @retval Others Fail to add cache data.
> > +
> > +**/
> > +EFI_STATUS
> > +AddHttpCacheData (
> > + IN REDFISH_HTTP_CACHE_LIST *List,
> > + IN EFI_STRING Uri,
> > + IN REDFISH_RESPONSE *Response
> > + )
> > +{
> > + REDFISH_HTTP_CACHE_DATA *NewData;
> > + REDFISH_HTTP_CACHE_DATA *OldData;
> > + REDFISH_HTTP_CACHE_DATA *UnusedData;
> > + REDFISH_RESPONSE *NewResponse;
> > +
> > + if ((List == NULL) || IS_EMPTY_STRING (Uri) || (Response == NULL)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + //
> > + // If same cache data exist, replace it with latest one.
> > + //
> > + OldData = FindHttpCacheData (&List->Head, Uri); if (OldData !=
> > + NULL) {
> > + DeleteHttpCacheData (List, OldData); }
> > +
> > + //
> > + // Check capacity
> > + //
> > + if (List->Count >= List->Capacity) {
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: list is full and retire
> > + unused
> > cache\n", __func__));
> > + UnusedData = FindUnusedHttpCacheData (&List->Head);
> > + if (UnusedData == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + DeleteHttpCacheData (List, UnusedData); }
> > +
> > + //
> > + // Clone a local copy
> > + //
> > + NewResponse = CloneRedfishResponse (Response); if (NewResponse ==
> > + NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + NewData = NewHttpCacheData (Uri, NewResponse); if (NewData ==
> > + NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + InsertTailList (&List->Head, &NewData->List); ++List->Count;
> > +
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache(%d/%d) %s\n",
> > __func__, List->Count, List->Capacity, NewData->Uri));
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Release all cache from list.
> > +
> > + @param[in] CacheList The list to be released.
> > +
> > + @retval EFI_SUCCESS All cache data are released.
> > + @retval EFI_INVALID_PARAMETER CacheList is NULL.
> > +
> > +**/
> > +EFI_STATUS
> > +ReleaseCacheList (
> > + IN REDFISH_HTTP_CACHE_LIST *CacheList
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + LIST_ENTRY *Next;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > +
> > + if (CacheList == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (IsListEmpty (&CacheList->Head)) {
> > + return EFI_SUCCESS;
> > + }
> > +
> > + Data = NULL;
> > + Next = NULL;
> > + List = GetFirstNode (&CacheList->Head); while (!IsNull
> > + (&CacheList->Head, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > + Next = GetNextNode (&CacheList->Head, List);
> > +
> > + DeleteHttpCacheData (CacheList, Data);
> > +
> > + List = Next;
> > + }
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Debug output the cache list.
> > +
> > + @param[in] Msg Debug message string.
> > + @param[in] ErrorLevel Output error level.
> > + @param[in] CacheList Target list to dump.
> > +
> > + @retval EFI_SUCCESS Debug dump finished.
> > + @retval EFI_INVALID_PARAMETER HttpCacheList is NULL.
> > +
> > +**/
> > +EFI_STATUS
> > +DumpHttpCacheList (
> > + IN CONST CHAR8 *Msg,
> > + IN UINTN ErrorLevel,
> > + IN REDFISH_HTTP_CACHE_LIST *CacheList
> > + )
> > +{
> > + LIST_ENTRY *List;
> > + REDFISH_HTTP_CACHE_DATA *Data;
> > + UINTN Index;
> > +
> > + if (CacheList == NULL) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (!IS_EMPTY_STRING (Msg)) {
> > + DEBUG ((ErrorLevel, "%a\n", Msg)); }
> > +
> > + if (IsListEmpty (&CacheList->Head)) {
> > + DEBUG ((ErrorLevel, "list is empty\n"));
> > + return EFI_NOT_FOUND;
> > + }
> > +
> > + DEBUG ((ErrorLevel, "list count: %d capacity: %d\n",
> > + CacheList->Count,
> > CacheList->Capacity));
> > + Data = NULL;
> > + Index = 0;
> > + List = GetFirstNode (&CacheList->Head); while (!IsNull
> > + (&CacheList->Head, List)) {
> > + Data = REDFISH_HTTP_CACHE_FROM_LIST (List);
> > +
> > + DEBUG ((ErrorLevel, "%d) Uri: %s Hit: %d\n", ++Index, Data->Uri,
> > + Data-
> > >HitCount));
> > +
> > + List = GetNextNode (&CacheList->Head, List); }
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Get redfish resource from given resource URI with cache mechanism
> > + supported. It's caller's responsibility to Response by calling
> > + RedfishFreeResponse ().
> > +
> > + @param[in] Service Redfish service instance to make query.
> > + @param[in] Uri Target resource URI.
> > + @param[out] Response HTTP response from redfish service.
> > + @param[in] UseCache If it is TRUE, this function will search for
> > + cache first. If it is FALSE, this function
> > + will query Redfish URI directly.
> > +
> > + @retval EFI_SUCCESS Resrouce is returned successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +RedfishHttpGetResource (
> > + IN REDFISH_SERVICE Service,
> > + IN EFI_STRING Uri,
> > + OUT REDFISH_RESPONSE *Response,
> > + IN BOOLEAN UseCache
> > + )
> > +{
> > + EFI_STATUS Status;
> > + CHAR8 *AsciiUri;
> > + REDFISH_HTTP_CACHE_DATA *CacheData;
> > + UINTN RetryCount;
> > +
> > + if ((Service == NULL) || (Response == NULL) || IS_EMPTY_STRING (Uri)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (mRedfishHttpCachePrivate == NULL) {
> > + return EFI_NOT_READY;
> > + }
> > +
> > + AsciiUri = NULL;
> > + CacheData = NULL;
> > + RetryCount = 0;
> > +
> > + //
> > + // Search for cache list.
> > + //
> > + if (UseCache) {
> > + CacheData = FindHttpCacheData (&mRedfishHttpCachePrivate-
> > >CacheList.Head, Uri);
> > + if (CacheData != NULL) {
> > + DEBUG ((REDFISH_HTTP_CACHE_DEBUG, "%a: cache hit! %s\n",
> > __func__, Uri));
> > +
> > + //
> > + // Copy cached response to caller's buffer.
> > + //
> > + Status = CopyRedfishResponse (CacheData->Response, Response);
> > + CacheData->HitCount += 1;
> > + return Status;
> > + }
> > + }
> > +
> > + AsciiUri = StringUnicodeToAscii (Uri); if (AsciiUri == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + //
> > + // Get resource from redfish service.
> > + //
> > + do {
> > + RetryCount += 1;
> > + Status = RedfishGetByUri (
> > + Service,
> > + AsciiUri,
> > + Response
> > + );
> > + if (!EFI_ERROR (Status) || (RetryCount >=
> > REDFISH_HTTP_GET_RETRY_MAX)) {
> > + break;
> > + }
> > +
> > + //
> > + // Retry when BMC is not ready.
> > + //
> > + if ((Response->StatusCode != NULL)) {
> > + DEBUG_CODE (
> > + DumpRedfishResponse (NULL, DEBUG_ERROR, Response);
> > + );
> > +
> > + if (*Response->StatusCode !=
> > HTTP_STATUS_500_INTERNAL_SERVER_ERROR) {
> > + break;
> > + }
> > +
> > + FreePool (Response->StatusCode);
> > + Response->StatusCode = NULL;
> > + }
> > +
> > + DEBUG ((DEBUG_WARN, "%a: RedfishGetByUri failed, retry
> > + (%d/%d)\n",
> > __func__, RetryCount, REDFISH_HTTP_GET_RETRY_MAX));
> > + gBS->Stall (REDFISH_HTTP_RETRY_WAIT); } while (TRUE);
> > +
> > + if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "%a: get %a failed (%d/%d): %r\n", __func__,
> > AsciiUri, RetryCount, REDFISH_HTTP_GET_RETRY_MAX, Status));
> > + if (Response->Payload != NULL) {
> > + RedfishFreeResponse (
> > + NULL,
> > + 0,
> > + NULL,
> > + Response->Payload
> > + );
> > + Response->Payload = NULL;
> > + }
> > +
> > + goto ON_RELEASE;
> > + }
> > +
> > + //
> > + // Keep response in cache list
> > + //
> > + Status = AddHttpCacheData (&mRedfishHttpCachePrivate->CacheList,
> > + Uri,
> > Response);
> > + if (EFI_ERROR (Status)) {
> > + DEBUG ((DEBUG_ERROR, "%a: failed to cache %s: %r\n", __func__,
> > + Uri,
> > Status));
> > + goto ON_RELEASE;
> > + }
> > +
> > + DEBUG_CODE (
> > + DumpHttpCacheList (__func__, REDFISH_HTTP_CACHE_DEBUG_DUMP,
> > &mRedfishHttpCachePrivate->CacheList);
> > + );
> > +
> > +ON_RELEASE:
> > +
> > + if (AsciiUri != NULL) {
> > + FreePool (AsciiUri);
> > + }
> > +
> > + return Status;
> > +}
> > +
> > +/**
> > + Reset the cached data specified by given URI. When response data
> > + returned by RedfishHttpResetResource() is modified, the response
> > + data can not be used by other caller. Application calls this
> > + function to make this data to be stale data and
> > + RedfishHttpResetResource() will get latest data from remote server
> > + again.
> > +
> > + @param[in] Uri Target resource URI.
> > +
> > + @retval EFI_SUCCESS Resrouce is reset successfully.
> > + @retval Others Errors occur.
> > +
> > +**/
> > +EFI_STATUS
> > +RedfishHttpResetResource (
> > + IN EFI_STRING Uri
> > + )
> > +{
> > + REDFISH_HTTP_CACHE_DATA *CacheData;
> > +
> > + if (IS_EMPTY_STRING (Uri)) {
> > + return EFI_INVALID_PARAMETER;
> > + }
> > +
> > + if (mRedfishHttpCachePrivate == NULL) {
> > + return EFI_NOT_READY;
> > + }
> > +
> > + CacheData = FindHttpCacheData (&mRedfishHttpCachePrivate-
> > >CacheList.Head, Uri);
> > + if (CacheData == NULL) {
> > + return EFI_NOT_FOUND;
> > + }
> > +
> > + DeleteHttpCacheData (&mRedfishHttpCachePrivate->CacheList,
> > + CacheData);
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > +
> > + Initial HTTP cache library instance.
> > +
> > + @param[in] ImageHandle The image handle.
> > + @param[in] SystemTable The system table.
> > +
> > + @retval EFI_SUCCESS Initial library successfully.
> > + @retval Other Return error status.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishHttpCacheConstructor (
> > + IN EFI_HANDLE ImageHandle,
> > + IN EFI_SYSTEM_TABLE *SystemTable
> > + )
> > +{
> > + mRedfishHttpCachePrivate = AllocateZeroPool (sizeof
> > (REDFISH_HTTP_CACHE_PRIVATE));
> > + if (mRedfishHttpCachePrivate == NULL) {
> > + return EFI_OUT_OF_RESOURCES;
> > + }
> > +
> > + //
> > + // Initial cache list
> > + //
> > + mRedfishHttpCachePrivate->CacheList.Capacity =
> > REDFISH_HTTP_CACHE_LIST_SIZE;
> > + mRedfishHttpCachePrivate->CacheList.Count = 0x00;
> > + InitializeListHead (&mRedfishHttpCachePrivate->CacheList.Head);
> > +
> > + return EFI_SUCCESS;
> > +}
> > +
> > +/**
> > + Release allocated resource.
> > +
> > + @param[in] ImageHandle Handle that identifies the image to be
> > unloaded.
> > + @param[in] SystemTable The system table.
> > +
> > + @retval EFI_SUCCESS The image has been unloaded.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +RedfishHttpCacheDestructor (
> > + IN EFI_HANDLE ImageHandle,
> > + IN EFI_SYSTEM_TABLE *SystemTable
> > + )
> > +{
> > + if (mRedfishHttpCachePrivate != NULL) {
> > + if (!IsListEmpty (&mRedfishHttpCachePrivate->CacheList.Head)) {
> > + ReleaseCacheList (&mRedfishHttpCachePrivate->CacheList);
> > + }
> > +
> > + FreePool (mRedfishHttpCachePrivate);
> > + mRedfishHttpCachePrivate = NULL;
> > + }
> > +
> > + return EFI_SUCCESS;
> > +}
> > --
> > 2.34.1
>
>
>
>
>
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113125): https://edk2.groups.io/g/devel/message/113125
Mute This Topic: https://groups.io/mt/103500371/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2024-01-04 2:33 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-03 11:59 [edk2-devel] [edk2-redfish-client][PATCH 01/10] RedfishClientPkg: introduce Redfish HTTP cache library Nickle Wang via groups.io
2024-01-03 13:01 ` Chang, Abner via groups.io
2024-01-04 2:33 ` Nickle Wang via groups.io
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox