From: Haitao Huang <haitao.huang@linux.intel.com>
To: jarkko@kernel.org, dave.hansen@linux.intel.com,
kai.huang@intel.com, tj@kernel.org, mkoutny@suse.com,
linux-kernel@vger.kernel.org, linux-sgx@vger.kernel.org,
x86@kernel.org, cgroups@vger.kernel.org, tglx@linutronix.de,
mingo@redhat.com, bp@alien8.de, hpa@zytor.com,
sohil.mehta@intel.com, tim.c.chen@linux.intel.com
Cc: zhiquan1.li@intel.com, kristen@linux.intel.com,
seanjc@google.com, zhanb@microsoft.com, anakrish@microsoft.com,
mikko.ylinen@linux.intel.com, yangjie@microsoft.com,
chrisyan@microsoft.com
Subject: [PATCH v10 09/14] x86/sgx: Implement async reclamation for cgroup
Date: Wed, 27 Mar 2024 17:22:24 -0700 [thread overview]
Message-ID: <20240328002229.30264-10-haitao.huang@linux.intel.com> (raw)
In-Reply-To: <20240328002229.30264-1-haitao.huang@linux.intel.com>
From: Kristen Carlson Accardi <kristen@linux.intel.com>
In cases EPC pages need be allocated during a page fault and the cgroup
usage is near its limit, an asynchronous reclamation needs be triggered
to avoid blocking the page fault handling.
Create a workqueue, corresponding work item and function definitions
for EPC cgroup to support the asynchronous reclamation.
In case the workqueue allocation is failed during init, disable cgroup.
In sgx_cgroup_try_charge(), if caller does not allow synchronous
reclamation, queue an asynchronous work into the workqueue.
Co-developed-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Kristen Carlson Accardi <kristen@linux.intel.com>
Co-developed-by: Haitao Huang <haitao.huang@linux.intel.com>
Signed-off-by: Haitao Huang <haitao.huang@linux.intel.com>
---
V10:
- Split asynchronous flow in separate patch. (Kai)
- Consider cgroup disabled when the workqueue allocation fail during
init. (Kai)
- Abstract out sgx_cgroup_should_reclaim().
V9:
- Add comments for static variables. (Jarkko)
V8:
- Remove alignment for substructure variables. (Jarkko)
V7:
- Split this out from the big patch, #10 in V6. (Dave, Kai)
---
arch/x86/kernel/cpu/sgx/epc_cgroup.c | 134 ++++++++++++++++++++++++++-
arch/x86/kernel/cpu/sgx/epc_cgroup.h | 1 +
arch/x86/kernel/cpu/sgx/main.c | 8 +-
3 files changed, 135 insertions(+), 8 deletions(-)
diff --git a/arch/x86/kernel/cpu/sgx/epc_cgroup.c b/arch/x86/kernel/cpu/sgx/epc_cgroup.c
index f7a487a29ed1..3ca89b1fb7e2 100644
--- a/arch/x86/kernel/cpu/sgx/epc_cgroup.c
+++ b/arch/x86/kernel/cpu/sgx/epc_cgroup.c
@@ -5,9 +5,63 @@
#include <linux/kernel.h>
#include "epc_cgroup.h"
+/*
+ * The minimal free pages maintained by per-cgroup reclaimer
+ * Set this to the low threshold used by the global reclaimer, ksgxd.
+ */
+#define SGX_CG_MIN_FREE_PAGE (SGX_NR_LOW_PAGES)
+
+/*
+ * If the cgroup limit is close to SGX_CG_MIN_FREE_PAGE, maintaining the minimal
+ * free pages would barely leave any page for use, causing excessive reclamation
+ * and thrashing.
+ *
+ * Define the following limit, below which cgroup does not maintain the minimal
+ * free page threshold. Set this to quadruple of the minimal so at least 75%
+ * pages used without being reclaimed.
+ */
+#define SGX_CG_LOW_LIMIT (SGX_CG_MIN_FREE_PAGE * 4)
+
/* The root SGX EPC cgroup */
static struct sgx_cgroup sgx_cg_root;
+/*
+ * The work queue that reclaims EPC pages in the background for cgroups.
+ *
+ * A cgroup schedules a work item into this queue to reclaim pages within the
+ * same cgroup when its usage limit is reached and synchronous reclamation is not
+ * an option, i.e., in a page fault handler.
+ */
+static struct workqueue_struct *sgx_cg_wq;
+
+static inline u64 sgx_cgroup_page_counter_read(struct sgx_cgroup *sgx_cg)
+{
+ return atomic64_read(&sgx_cg->cg->res[MISC_CG_RES_SGX_EPC].usage) / PAGE_SIZE;
+}
+
+static inline u64 sgx_cgroup_max_pages(struct sgx_cgroup *sgx_cg)
+{
+ return READ_ONCE(sgx_cg->cg->res[MISC_CG_RES_SGX_EPC].max) / PAGE_SIZE;
+}
+
+/*
+ * Get the lower bound of limits of a cgroup and its ancestors. Used in
+ * sgx_cgroup_should_reclaim() to determine if EPC usage of a cgroup is
+ * close to its limit or its ancestors' hence reclamation is needed.
+ */
+static inline u64 sgx_cgroup_max_pages_to_root(struct sgx_cgroup *sgx_cg)
+{
+ struct misc_cg *i = sgx_cg->cg;
+ u64 m = U64_MAX;
+
+ while (i) {
+ m = min(m, READ_ONCE(i->res[MISC_CG_RES_SGX_EPC].max));
+ i = misc_cg_parent(i);
+ }
+
+ return m / PAGE_SIZE;
+}
+
/**
* sgx_cgroup_lru_empty() - check if a cgroup tree has no pages on its LRUs
* @root: Root of the tree to check
@@ -99,6 +153,61 @@ static void sgx_cgroup_reclaim_pages(struct misc_cg *root)
rcu_read_unlock();
}
+/**
+ * sgx_cgroup_should_reclaim() - check if EPC reclamation is needed for a cgroup
+ * @sgx_cg: The cgroup to be checked.
+ *
+ * This function can be used to guard a call to sgx_cgroup_reclaim_pages() where
+ * the minimal number of free page needs be maintained for the cgroup to make
+ * good forward progress.
+ *
+ * Return: %true if number of free pages available for the cgroup below a
+ * threshold (%SGX_CG_MIN_FREE_PAGE) and there are reclaimable pages within the
+ * cgroup.
+ */
+static bool sgx_cgroup_should_reclaim(struct sgx_cgroup *sgx_cg)
+{
+ u64 cur, max;
+
+ if (sgx_cgroup_lru_empty(sgx_cg->cg))
+ return false;
+
+ max = sgx_cgroup_max_pages_to_root(sgx_cg);
+
+ /*
+ * Unless the limit is very low, maintain a minimal number of free pages
+ * so there is always a few pages available to serve new allocation
+ * requests quickly.
+ */
+ if (max > SGX_CG_LOW_LIMIT)
+ max -= SGX_CG_MIN_FREE_PAGE;
+
+ cur = sgx_cgroup_page_counter_read(sgx_cg);
+
+ return (cur >= max);
+}
+
+/*
+ * Asynchronous work flow to reclaim pages from the cgroup when the cgroup is
+ * at/near its maximum capacity.
+ */
+static void sgx_cgroup_reclaim_work_func(struct work_struct *work)
+{
+ struct sgx_cgroup *sgx_cg = container_of(work, struct sgx_cgroup, reclaim_work);
+
+ /*
+ * This work func is scheduled by sgx_cgroup_try_charge() when it cannot
+ * directly reclaim, i.e., EPC allocation in a fault handler. Waiting to
+ * reclaim until the cgroup is actually at its limit is less performant,
+ * as it means the task scheduling this asynchronous work is effectively
+ * blocked until a worker makes its way through the global work queue.
+ */
+ while (sgx_cgroup_should_reclaim(sgx_cg)) {
+ sgx_cgroup_reclaim_pages(sgx_cg->cg);
+ cond_resched();
+ }
+}
+
static int __sgx_cgroup_try_charge(struct sgx_cgroup *epc_cg)
{
if (!misc_cg_try_charge(MISC_CG_RES_SGX_EPC, epc_cg->cg, PAGE_SIZE))
@@ -125,14 +234,24 @@ int sgx_cgroup_try_charge(struct sgx_cgroup *sgx_cg, enum sgx_reclaim reclaim)
{
int ret;
+ /* cgroup disabled due to wq allocation failure during sgx_cgroup_init(). */
+ if (!sgx_cg_wq)
+ return 0;
+
+ /* Should not happen if the wq is allocated and SGX cgroup initialized properly. */
+ if (WARN_ON_ONCE(!sgx_cg))
+ return 0;
+
for (;;) {
ret = __sgx_cgroup_try_charge(sgx_cg);
if (ret != -EBUSY)
return ret;
- if (reclaim == SGX_NO_RECLAIM)
- return -ENOMEM;
+ if (reclaim == SGX_NO_RECLAIM) {
+ queue_work(sgx_cg_wq, &sgx_cg->reclaim_work);
+ return -EBUSY;
+ }
sgx_cgroup_reclaim_pages(sgx_cg->cg);
cond_resched();
@@ -147,7 +266,8 @@ int sgx_cgroup_try_charge(struct sgx_cgroup *sgx_cg, enum sgx_reclaim reclaim)
*/
void sgx_cgroup_uncharge(struct sgx_cgroup *sgx_cg)
{
- misc_cg_uncharge(MISC_CG_RES_SGX_EPC, sgx_cg->cg, PAGE_SIZE);
+ if (sgx_cg)
+ misc_cg_uncharge(MISC_CG_RES_SGX_EPC, sgx_cg->cg, PAGE_SIZE);
}
static void sgx_cgroup_free(struct misc_cg *cg)
@@ -158,6 +278,7 @@ static void sgx_cgroup_free(struct misc_cg *cg)
if (!sgx_cg)
return;
+ cancel_work_sync(&sgx_cg->reclaim_work);
kfree(sgx_cg);
}
@@ -171,6 +292,7 @@ const struct misc_res_ops sgx_cgroup_ops = {
static void sgx_cgroup_misc_init(struct misc_cg *cg, struct sgx_cgroup *sgx_cg)
{
sgx_lru_init(&sgx_cg->lru);
+ INIT_WORK(&sgx_cg->reclaim_work, sgx_cgroup_reclaim_work_func);
cg->res[MISC_CG_RES_SGX_EPC].priv = sgx_cg;
sgx_cg->cg = cg;
}
@@ -190,6 +312,12 @@ static int sgx_cgroup_alloc(struct misc_cg *cg)
void sgx_cgroup_init(void)
{
+ sgx_cg_wq = alloc_workqueue("sgx_cg_wq", WQ_UNBOUND | WQ_FREEZABLE, WQ_UNBOUND_MAX_ACTIVE);
+
+ /* All Cgroups functionalities are disabled. */
+ if (WARN_ON(!sgx_cg_wq))
+ return;
+
misc_cg_set_ops(MISC_CG_RES_SGX_EPC, &sgx_cgroup_ops);
sgx_cgroup_misc_init(misc_cg_root(), &sgx_cg_root);
}
diff --git a/arch/x86/kernel/cpu/sgx/epc_cgroup.h b/arch/x86/kernel/cpu/sgx/epc_cgroup.h
index f62dce0cac51..f66570d3ef42 100644
--- a/arch/x86/kernel/cpu/sgx/epc_cgroup.h
+++ b/arch/x86/kernel/cpu/sgx/epc_cgroup.h
@@ -32,6 +32,7 @@ static inline void sgx_cgroup_init(void) { }
struct sgx_cgroup {
struct misc_cg *cg;
struct sgx_epc_lru_list lru;
+ struct work_struct reclaim_work;
};
static inline struct sgx_cgroup *sgx_cgroup_from_misc_cg(struct misc_cg *cg)
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index aaf341abc641..9f6594317ce4 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -650,11 +650,9 @@ void sgx_free_epc_page(struct sgx_epc_page *page)
struct sgx_numa_node *node = section->node;
#ifdef CONFIG_CGROUP_SGX_EPC
- if (page->sgx_cg) {
- sgx_cgroup_uncharge(page->sgx_cg);
- sgx_put_cg(page->sgx_cg);
- page->sgx_cg = NULL;
- }
+ sgx_cgroup_uncharge(page->sgx_cg);
+ sgx_put_cg(page->sgx_cg);
+ page->sgx_cg = NULL;
#endif
spin_lock(&node->lock);
--
2.25.1
next prev parent reply other threads:[~2024-03-28 0:22 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-03-28 0:22 [PATCH v10 00/14] Add Cgroup support for SGX EPC memory Haitao Huang
2024-03-28 0:22 ` [PATCH v10 01/14] x86/sgx: Replace boolean parameters with enums Haitao Huang
2024-03-28 0:22 ` [PATCH v10 02/14] cgroup/misc: Add per resource callbacks for CSS events Haitao Huang
2024-03-28 0:22 ` [PATCH v10 03/14] cgroup/misc: Export APIs for SGX driver Haitao Huang
2024-03-28 0:22 ` [PATCH v10 04/14] cgroup/misc: Add SGX EPC resource type Haitao Huang
2024-03-28 0:22 ` [PATCH v10 05/14] x86/sgx: Implement basic EPC misc cgroup functionality Haitao Huang
2024-03-28 12:53 ` Huang, Kai
2024-03-30 11:17 ` Jarkko Sakkinen
2024-04-01 9:29 ` Huang, Kai
2024-04-01 14:30 ` Jarkko Sakkinen
2024-04-05 1:24 ` Haitao Huang
2024-04-05 2:55 ` Huang, Kai
2024-03-28 0:22 ` [PATCH v10 06/14] x86/sgx: Add sgx_epc_lru_list to encapsulate LRU list Haitao Huang
2024-03-28 0:22 ` [PATCH v10 07/14] x86/sgx: Abstract tracking reclaimable pages in LRU Haitao Huang
2024-03-28 0:22 ` [PATCH v10 08/14] x86/sgx: Add basic EPC reclamation flow for cgroup Haitao Huang
2024-04-03 13:08 ` Huang, Kai
2024-04-04 17:05 ` Haitao Huang
2024-04-05 2:59 ` Huang, Kai
2024-04-05 3:07 ` Huang, Kai
2024-04-13 20:56 ` Jarkko Sakkinen
2024-03-28 0:22 ` Haitao Huang [this message]
2024-04-04 11:16 ` [PATCH v10 09/14] x86/sgx: Implement async reclamation " Huang, Kai
2024-04-04 15:39 ` Haitao Huang
2024-03-28 0:22 ` [PATCH v10 10/14] x86/sgx: Charge mem_cgroup for per-cgroup reclamation Haitao Huang
2024-03-28 0:22 ` [PATCH v10 11/14] x86/sgx: Abstract check for global reclaimable pages Haitao Huang
2024-03-28 0:22 ` [PATCH v10 12/14] x86/sgx: Turn on per-cgroup EPC reclamation Haitao Huang
2024-04-08 12:20 ` Huang, Kai
2024-04-08 18:03 ` Haitao Huang
2024-04-08 22:37 ` Huang, Kai
2024-04-09 4:23 ` Haitao Huang
2024-04-09 9:03 ` Michal Koutný
2024-04-09 15:34 ` Haitao Huang
2024-04-10 18:28 ` Haitao Huang
2024-03-28 0:22 ` [PATCH v10 13/14] Docs/x86/sgx: Add description for cgroup support Haitao Huang
2024-03-28 0:22 ` [PATCH v10 14/14] selftests/sgx: Add scripts for EPC cgroup testing Haitao Huang
2024-03-30 11:29 ` Jarkko Sakkinen
2024-03-31 17:44 ` [PATCH] selftests/sgx: Improve cgroup test scripts Haitao Huang
2024-04-01 14:22 ` Jarkko Sakkinen
2024-04-01 22:55 ` Haitao Huang
2024-04-02 7:37 ` Jarkko Sakkinen
2024-04-02 1:42 ` [PATCH v2] " Haitao Huang
2024-04-02 7:43 ` Jarkko Sakkinen
2024-04-02 17:31 ` Haitao Huang
2024-04-03 15:34 ` Jarkko Sakkinen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240328002229.30264-10-haitao.huang@linux.intel.com \
--to=haitao.huang@linux.intel.com \
--cc=anakrish@microsoft.com \
--cc=bp@alien8.de \
--cc=cgroups@vger.kernel.org \
--cc=chrisyan@microsoft.com \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=jarkko@kernel.org \
--cc=kai.huang@intel.com \
--cc=kristen@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sgx@vger.kernel.org \
--cc=mikko.ylinen@linux.intel.com \
--cc=mingo@redhat.com \
--cc=mkoutny@suse.com \
--cc=seanjc@google.com \
--cc=sohil.mehta@intel.com \
--cc=tglx@linutronix.de \
--cc=tim.c.chen@linux.intel.com \
--cc=tj@kernel.org \
--cc=x86@kernel.org \
--cc=yangjie@microsoft.com \
--cc=zhanb@microsoft.com \
--cc=zhiquan1.li@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®