Re: [PATCH v8 1/6] cgroup/dmem: Add queries for protection values
[email protected] Tue, 04 Aug 2026 08:41:18 +0000
| Newsgroups | org.freedesktop.lists.dri-devel |
|---|---|
| Message-ID | <[email protected]> |
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: New issues: - [High] Missing ancestry check can result in using stale protection values for non-descendants. Pre-existing issues: - [High] O(N) cgroup tree traversal per isolated query causes massive overhead on eviction paths. -- --- Patch [1]: [PATCH v8 1/6] cgroup/dmem: Add queries for protection values --- commit def4bae1e53d26c789e6edbd5c551a0f50ade339 Author: Natalie Vock <[email protected]> cgroup/dmem: Add queries for protection values Callers can use this feedback to be more aggressive in making space for allocations of a cgroup if they know it is protected. These are counterparts to memcg's mem_cgroup_below_{min,low}. > diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c > index 39930c59cb769..9df3b33c65046 100644 > --- a/kernel/cgroup/dmem.c > +++ b/kernel/cgroup/dmem.c [ ... ] > @@ -700,6 +700,68 @@ int dmem_cgroup_try_charge(struct dmem_cgroup_region *region, u64 size, > } > EXPORT_SYMBOL_GPL(dmem_cgroup_try_charge); > > +/** > + * dmem_cgroup_below_min() - Tests whether current usage is within min limit. > + * > + * @root: Root of the subtree to calculate protection for, or NULL to calculate global protection. > + * @test: The pool to test the usage/min limit of. > + * > + * Return: true if usage is below min and the cgroup is protected, false otherwise. > + */ > +bool dmem_cgroup_below_min(struct dmem_cgroup_pool_state *root, > + struct dmem_cgroup_pool_state *test) > +{ > + if (root == test || !pool_parent(test)) > + return false; > + > + if (!root) { > + for (root = test; pool_parent(root); root = pool_parent(root)) > + {} > + } > + > + /* > + * In mem_cgroup_below_min(), the memcg pendant, this call is missing. > + * mem_cgroup_below_min() gets called during traversal of the cgroup tree, where > + * protection is already calculated as part of the traversal. dmem cgroup eviction > + * does not traverse the cgroup tree, so we need to recalculate effective protection > + * here. > + */ > + dmem_cgroup_calculate_protection(root, test); [Severity: High] This isn't a bug introduced by this patch, but does calling dmem_cgroup_calculate_protection() here for isolated per-item queries cause massive overhead on eviction paths? Looking at the pre-existing dmem_cgroup_calculate_protection() implementation: kernel/cgroup/dmem.c:dmem_cgroup_calculate_protection() { ... css_for_each_descendant_pre(css, &limit_pool->cs->css) { ... page_counter_calculate_protection(climit, &found_pool->cnt, true); if (found_pool == test_pool) break; } ... } It performs a top-down pre-order traversal starting from root. When root is NULL and falls back to the global root, it forces a traversal over almost the entire system's cgroups. Executing this full subtree walk repeatedly during eviction scanning could lead to severe performance degradation. > + return page_counter_read(&test->cnt) <= READ_ONCE(test->cnt.emin); [Severity: High] If the caller passes a root and a test pool where test is not a descendant of root, dmem_cgroup_calculate_protection() will iterate only the descendants of root, never find test, and exit without updating test->cnt.emin. Could this cause the function to read a stale emin value from a prior calculation and incorrectly return true, incorrectly shielding the cgroup from eviction? Since dmem uses global lists and doesn't have per-cgroup LRUs, encountering non-descendants during eviction seems likely. Would it be safer to verify that test is a descendant of root and return false otherwise? > +} > +EXPORT_SYMBOL_GPL(dmem_cgroup_below_min); > + > +/** > + * dmem_cgroup_below_low() - Tests whether current usage is within low limit. > + * > + * @root: Root of the subtree to calculate protection for, or NULL to calculate global protection. > + * @test: The pool to test the usage/low limit of. > + * > + * Return: true if usage is below low and the cgroup is protected, false otherwise. > + */ > +bool dmem_cgroup_below_low(struct dmem_cgroup_pool_state *root, > + struct dmem_cgroup_pool_state *test) > +{ > + if (root == test || !pool_parent(test)) > + return false; > + > + if (!root) { > + for (root = test; pool_parent(root); root = pool_parent(root)) > + {} > + } > + > + /* > + * In mem_cgroup_below_low(), the memcg pendant, this call is missing. > + * mem_cgroup_below_low() gets called during traversal of the cgroup tree, where > + * protection is already calculated as part of the traversal. dmem cgroup eviction > + * does not traverse the cgroup tree, so we need to recalculate effective protection > + * here. > + */ > + dmem_cgroup_calculate_protection(root, test); > + return page_counter_read(&test->cnt) <= READ_ONCE(test->cnt.elow); [Severity: High] Similarly to dmem_cgroup_below_min() above, does this missing ancestry check also expose dmem_cgroup_below_low() to using stale elow protection values if test is not a descendant of root? > +} > +EXPORT_SYMBOL_GPL(dmem_cgroup_below_low); -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1