* [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups
@ 2024-09-04 13:39 Zhen Lei
2024-09-04 13:39 ` [PATCH v2 1/6] debugobjects: Fix the compilation attributes of some global variables Zhen Lei
` (5 more replies)
0 siblings, 6 replies; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
v1 --> v2:
1. Fix the compilation attributes of some global variables
2. Update comments and commit messages.
v1:
The summary changes of the first two patches is as follows:
if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
return;
- while (READ_ONCE(obj_nr_tofree) && (READ_ONCE(obj_pool_free) < obj_pool_min_free)) {
+ if (READ_ONCE(obj_nr_tofree)) {
raw_spin_lock_irqsave(&pool_lock, flags);
- while (obj_nr_tofree && (obj_pool_free < obj_pool_min_free)) {
+ while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
... ...
}
raw_spin_unlock_irqrestore(&pool_lock, flags);
Zhen Lei (6):
debugobjects: Fix the compilation attributes of some global variables
debugobjects: Fix the misuse of global variables in fill_pool()
debugobjects: Remove redundant checks in fill_pool()
debugobjects: Don't start fill if there are remaining nodes locally
debugobjects: Use hlist_splice_init() to reduce lock conflicts
debugobjects: Delete a piece of redundant code
lib/debugobjects.c | 58 ++++++++++++++++++++++++++--------------------
1 file changed, 33 insertions(+), 25 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2 1/6] debugobjects: Fix the compilation attributes of some global variables
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
@ 2024-09-04 13:39 ` Zhen Lei
2024-09-09 14:59 ` [tip: core/debugobjects] " tip-bot2 for Zhen Lei
2024-09-04 13:39 ` [PATCH v2 2/6] debugobjects: Fix the misuse of global variables in fill_pool() Zhen Lei
` (4 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
1. Both debug_objects_pool_min_level and debug_objects_pool_size are
read-only after initialization, change attribute '__read_mostly' to
'__ro_after_init', and remove '__data_racy'.
2. Many global variables are read in the debug_stats_show() function, but
didn't mask KCSAN's detection. Add '__data_racy' for them.
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
lib/debugobjects.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 7cea91e193a8f04..7226fdb5e129a79 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -70,10 +70,10 @@ static HLIST_HEAD(obj_to_free);
* made at debug_stats_show(). Both obj_pool_min_free and obj_pool_max_used
* can be off.
*/
-static int obj_pool_min_free = ODEBUG_POOL_SIZE;
-static int obj_pool_free = ODEBUG_POOL_SIZE;
+static int __data_racy obj_pool_min_free = ODEBUG_POOL_SIZE;
+static int __data_racy obj_pool_free = ODEBUG_POOL_SIZE;
static int obj_pool_used;
-static int obj_pool_max_used;
+static int __data_racy obj_pool_max_used;
static bool obj_freeing;
/* The number of objs on the global free list */
static int obj_nr_tofree;
@@ -84,9 +84,9 @@ static int __data_racy debug_objects_fixups __read_mostly;
static int __data_racy debug_objects_warnings __read_mostly;
static int __data_racy debug_objects_enabled __read_mostly
= CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
-static int __data_racy debug_objects_pool_size __read_mostly
+static int debug_objects_pool_size __ro_after_init
= ODEBUG_POOL_SIZE;
-static int __data_racy debug_objects_pool_min_level __read_mostly
+static int debug_objects_pool_min_level __ro_after_init
= ODEBUG_POOL_MIN_LEVEL;
static const struct debug_obj_descr *descr_test __read_mostly;
@@ -95,8 +95,8 @@ static struct kmem_cache *obj_cache __ro_after_init;
/*
* Track numbers of kmem_cache_alloc()/free() calls done.
*/
-static int debug_objects_allocated;
-static int debug_objects_freed;
+static int __data_racy debug_objects_allocated;
+static int __data_racy debug_objects_freed;
static void free_obj_work(struct work_struct *work);
static DECLARE_DELAYED_WORK(debug_obj_work, free_obj_work);
--
2.34.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v2 2/6] debugobjects: Fix the misuse of global variables in fill_pool()
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
2024-09-04 13:39 ` [PATCH v2 1/6] debugobjects: Fix the compilation attributes of some global variables Zhen Lei
@ 2024-09-04 13:39 ` Zhen Lei
2024-09-09 14:59 ` [tip: core/debugobjects] debugobjects: Fix conditions " tip-bot2 for Zhen Lei
2024-09-04 13:39 ` [PATCH v2 3/6] debugobjects: Remove redundant checks " Zhen Lei
` (3 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
The global variable 'obj_pool_min_free' records the lowest historical
value of the number of nodes in the global list 'obj_pool', instead of
being used as the minimum level to keep around. It has to be replaced
with variable 'debug_objects_pool_min_level'.
Fixes: d26bf5056fc0 ("debugobjects: Reduce number of pool_lock acquisitions in fill_pool()")
Fixes: 36c4ead6f6df ("debugobjects: Add global free list and the counter")
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
lib/debugobjects.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 7226fdb5e129a79..6329a86edcf12ac 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -142,13 +142,14 @@ static void fill_pool(void)
* READ_ONCE()s pair with the WRITE_ONCE()s in pool_lock critical
* sections.
*/
- while (READ_ONCE(obj_nr_tofree) && (READ_ONCE(obj_pool_free) < obj_pool_min_free)) {
+ while (READ_ONCE(obj_nr_tofree) &&
+ READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
raw_spin_lock_irqsave(&pool_lock, flags);
/*
* Recheck with the lock held as the worker thread might have
* won the race and freed the global free list already.
*/
- while (obj_nr_tofree && (obj_pool_free < obj_pool_min_free)) {
+ while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
hlist_del(&obj->node);
WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
--
2.34.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v2 3/6] debugobjects: Remove redundant checks in fill_pool()
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
2024-09-04 13:39 ` [PATCH v2 1/6] debugobjects: Fix the compilation attributes of some global variables Zhen Lei
2024-09-04 13:39 ` [PATCH v2 2/6] debugobjects: Fix the misuse of global variables in fill_pool() Zhen Lei
@ 2024-09-04 13:39 ` Zhen Lei
2024-09-09 14:59 ` [tip: core/debugobjects] " tip-bot2 for Zhen Lei
2024-09-04 13:39 ` [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally Zhen Lei
` (2 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
(1) if (READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level)
return;
(2) while (READ_ONCE(obj_nr_tofree) &&
READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
raw_spin_lock_irqsave(&pool_lock, flags);
(3) while (obj_nr_tofree &&
(3) (obj_pool_free < debug_objects_pool_min_level)) {
... ...
}
raw_spin_unlock_irqrestore(&pool_lock, flags);
}
The conditions for the outer loop (2) and inner loop (3) are exactly the
same. The inner loop is completed under the protection of the spinlock.
When the inner loop ends, at least one of the two loop conditions must be
met. The time from the end of the inner loop to the restart of the outer
loop is extremely short, and the probability of other cores modifying
'obj_nr_tofree' and 'obj_pool_free' is almost zero during this time. In
fact, after the outer loop ends, it is still possible for other cores to
modify the values of these two variables. Therefore, restarting the outer
loop has no practical effect except for an additional check. So the outer
'while' should be changed to 'if'. Then we'll see that the second
condition of the new 'if' is already guaranteed above (1) and can be
removed.
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
lib/debugobjects.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 6329a86edcf12ac..7a8ccc94cb037ba 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -135,15 +135,13 @@ static void fill_pool(void)
return;
/*
- * Reuse objs from the global free list; they will be reinitialized
+ * Reuse objs from the global tofree list; they will be reinitialized
* when allocating.
*
- * Both obj_nr_tofree and obj_pool_free are checked locklessly; the
- * READ_ONCE()s pair with the WRITE_ONCE()s in pool_lock critical
- * sections.
+ * The obj_nr_tofree is checked locklessly; the READ_ONCE() pair with
+ * the WRITE_ONCE() in pool_lock critical sections.
*/
- while (READ_ONCE(obj_nr_tofree) &&
- READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
+ if (READ_ONCE(obj_nr_tofree)) {
raw_spin_lock_irqsave(&pool_lock, flags);
/*
* Recheck with the lock held as the worker thread might have
--
2.34.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
` (2 preceding siblings ...)
2024-09-04 13:39 ` [PATCH v2 3/6] debugobjects: Remove redundant checks " Zhen Lei
@ 2024-09-04 13:39 ` Zhen Lei
2024-09-05 3:11 ` Leizhen (ThunderTown)
2024-09-04 13:39 ` [PATCH v2 5/6] debugobjects: Use hlist_splice_init() to reduce lock conflicts Zhen Lei
2024-09-04 13:39 ` [PATCH v2 6/6] debugobjects: Delete a piece of redundant code Zhen Lei
5 siblings, 1 reply; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
If the conditions for starting fill are met, it means that all cores that
call fill() later are blocked until the first core completes the fill
operation. But obviously, for a core that has free nodes locally, it does
not need to be blocked(see below for why). This is good in stress
situations.
1. In the case of no nesting, a core uses only one node at a time. As long
as there is a local node, there is no need to use the free node in
obj_pool.
2. In the case of nesting depth is one, nodes in obj_pool need to be used
only when there is only one local node.
#define ODEBUG_POOL_PERCPU_SIZE 64
#define ODEBUG_BATCH_SIZE 16
Assume that when nested, the probability of percpu_obj_pool having each
number of nodes is the same. The probability of only one node is less
than 1/17=6%. Assuming the probability of nesting is 5%, that's a
pretty high estimate. Then the probability of using obj_pool is
6% * 5% = 0.3%. In other words, a 333-core environment produces only
one core to compete for obj_pool.
#define ODEBUG_POOL_MIN_LEVEL 256
#define ODEBUG_BATCH_SIZE 16
But we can tolerate "256 / (16 + 1)" = 15 cores competing at the same
time.
3. In the case of nesting depth more than one, the probability is lower
and negligible.
Nesting Depth=2: "2/17 * 5% * 5%" = 0.03%
Nesting Depth=3: "3/17 * 5% * 5% * 5%" = 0.002%
However, to ensure sufficient reliability, obj_pool is not filled only
when there are more than two local nodes, reduce the probability of
problems to the impossible.
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
lib/debugobjects.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 7a8ccc94cb037ba..4f64b5d4329c27d 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -131,6 +131,16 @@ static void fill_pool(void)
struct debug_obj *obj;
unsigned long flags;
+ /*
+ * The upper-layer function uses only one node at a time. If there are
+ * more than two local nodes, it means that even if nesting occurs, it
+ * doesn't matter. The probability of nesting depth >= 2 is extremely
+ * low, and the number of global free nodes guarded by
+ * debug_objects_pool_min_level is adequate.
+ */
+ if (likely(obj_cache) && this_cpu_read(percpu_obj_pool.obj_free) >= 2)
+ return;
+
if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
return;
--
2.34.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v2 5/6] debugobjects: Use hlist_splice_init() to reduce lock conflicts
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
` (3 preceding siblings ...)
2024-09-04 13:39 ` [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally Zhen Lei
@ 2024-09-04 13:39 ` Zhen Lei
2024-09-04 13:39 ` [PATCH v2 6/6] debugobjects: Delete a piece of redundant code Zhen Lei
5 siblings, 0 replies; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
The sub list can be prepared in advance outside the lock, so that the
operation time inside the lock can be reduced and the possibility of
lock conflict can be reduced.
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
lib/debugobjects.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 4f64b5d4329c27d..9876e7ad5291492 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -171,23 +171,25 @@ static void fill_pool(void)
return;
while (READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
- struct debug_obj *new[ODEBUG_BATCH_SIZE];
+ struct debug_obj *new, *last = NULL;
+ HLIST_HEAD(freelist);
int cnt;
for (cnt = 0; cnt < ODEBUG_BATCH_SIZE; cnt++) {
- new[cnt] = kmem_cache_zalloc(obj_cache, gfp);
- if (!new[cnt])
+ new = kmem_cache_zalloc(obj_cache, gfp);
+ if (!new)
break;
+ hlist_add_head(&new->node, &freelist);
+ if (!last)
+ last = new;
}
if (!cnt)
return;
raw_spin_lock_irqsave(&pool_lock, flags);
- while (cnt) {
- hlist_add_head(&new[--cnt]->node, &obj_pool);
- debug_objects_allocated++;
- WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
- }
+ hlist_splice_init(&freelist, &last->node, &obj_pool);
+ debug_objects_allocated += cnt;
+ WRITE_ONCE(obj_pool_free, obj_pool_free + cnt);
raw_spin_unlock_irqrestore(&pool_lock, flags);
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v2 6/6] debugobjects: Delete a piece of redundant code
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
` (4 preceding siblings ...)
2024-09-04 13:39 ` [PATCH v2 5/6] debugobjects: Use hlist_splice_init() to reduce lock conflicts Zhen Lei
@ 2024-09-04 13:39 ` Zhen Lei
5 siblings, 0 replies; 22+ messages in thread
From: Zhen Lei @ 2024-09-04 13:39 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel; +Cc: Zhen Lei
The statically allocated objects are all located in obj_static_pool[],
the whole memory of obj_static_pool[] will be reclaimed later. Therefore,
there is no need to split the remaining statically nodes in list obj_pool
into isolated ones, no one will use them anymore. Just write
INIT_HLIST_HEAD(&obj_pool) is enough. Since hlist_move_list() directly
discards the old list, even this can be omitted.
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
lib/debugobjects.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 9876e7ad5291492..65ecc611ac24720 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -1337,10 +1337,7 @@ static int __init debug_objects_replace_static_objects(void)
* active object references.
*/
- /* Remove the statically allocated objects from the pool */
- hlist_for_each_entry_safe(obj, tmp, &obj_pool, node)
- hlist_del(&obj->node);
- /* Move the allocated objects to the pool */
+ /* Replace the statically allocated objects list with the allocated objects list */
hlist_move_list(&objects, &obj_pool);
/* Replace the active object references */
--
2.34.1
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-04 13:39 ` [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally Zhen Lei
@ 2024-09-05 3:11 ` Leizhen (ThunderTown)
2024-09-05 3:45 ` Leizhen (ThunderTown)
0 siblings, 1 reply; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-05 3:11 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel
On 2024/9/4 21:39, Zhen Lei wrote:
> If the conditions for starting fill are met, it means that all cores that
> call fill() later are blocked until the first core completes the fill
> operation. But obviously, for a core that has free nodes locally, it does
> not need to be blocked(see below for why). This is good in stress
> situations.
>
> 1. In the case of no nesting, a core uses only one node at a time. As long
> as there is a local node, there is no need to use the free node in
> obj_pool.
> 2. In the case of nesting depth is one, nodes in obj_pool need to be used
> only when there is only one local node.
> #define ODEBUG_POOL_PERCPU_SIZE 64
> #define ODEBUG_BATCH_SIZE 16
> Assume that when nested, the probability of percpu_obj_pool having each
> number of nodes is the same. The probability of only one node is less
> than 1/17=6%. Assuming the probability of nesting is 5%, that's a
> pretty high estimate. Then the probability of using obj_pool is
> 6% * 5% = 0.3%. In other words, a 333-core environment produces only
> one core to compete for obj_pool.
> #define ODEBUG_POOL_MIN_LEVEL 256
> #define ODEBUG_BATCH_SIZE 16
> But we can tolerate "256 / (16 + 1)" = 15 cores competing at the same
> time.
One detail is omitted. In function debug_objects_mem_init(), an extra batch
is reserved for each core.
extras = num_possible_cpus() * ODEBUG_BATCH_SIZE;
debug_objects_pool_min_level += extras;
In addition, above method of calculating probabilities is wrong. The correct
calculation method is as follows:
When the number of local nodes is 0, fill is performed. When the number of
local nodes is 1 and nested, 16 nodes are moved from obj_pool to obj_pool.
As a result, the obj_pool resource pool keeps decreasing. When this happens
continuously(The number of local nodes equal 0 is not met), the resource
pool will eventually be exhausted. The error probability is:
(1/2)^((256+16^ncpus)/17) * (5% + 5%^2 + ... + 5%^N) * 2/17 < 1e-7 (ncpus=1).
1/2 ==> denominator sequence: 0,1; numerator sequence: 1
(5% + 5%^2 + ... + 5%^N) < 5% + (5%^2) * 2 = 0.055
17 = ODEBUG_BATCH_SIZ + 1, amount moved from obj_pool when the number of local nodes is 0.
2/17 ==> denominator sequence: 0-16; numerator sequence: 0,1
The more cores, the lower the probability of exhaustion.
If obj_pool is not filled only when there are more than two local nodes,
the probability of exhaustion is:
(1/3)^((256+16^ncpus)/17) * (5% + 5%^2 + ... + 5%^N) * 3/17 < 2.3e-10
1/3 ==> denominator sequence: 0,1,2; numerator sequence: 2
3/17 ==> denominator sequence: 0-16; numerator sequence: 0,1,2
> 3. In the case of nesting depth more than one, the probability is lower
> and negligible.
> Nesting Depth=2: "2/17 * 5% * 5%" = 0.03%
> Nesting Depth=3: "3/17 * 5% * 5% * 5%" = 0.002%
>
> However, to ensure sufficient reliability, obj_pool is not filled only
> when there are more than two local nodes, reduce the probability of
> problems to the impossible.
>
> Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
> ---
> lib/debugobjects.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/lib/debugobjects.c b/lib/debugobjects.c
> index 7a8ccc94cb037ba..4f64b5d4329c27d 100644
> --- a/lib/debugobjects.c
> +++ b/lib/debugobjects.c
> @@ -131,6 +131,16 @@ static void fill_pool(void)
> struct debug_obj *obj;
> unsigned long flags;
>
> + /*
> + * The upper-layer function uses only one node at a time. If there are
> + * more than two local nodes, it means that even if nesting occurs, it
> + * doesn't matter. The probability of nesting depth >= 2 is extremely
> + * low, and the number of global free nodes guarded by
> + * debug_objects_pool_min_level is adequate.
> + */
> + if (likely(obj_cache) && this_cpu_read(percpu_obj_pool.obj_free) >= 2)
> + return;
> +
> if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
> return;
>
>
--
Regards,
Zhen Lei
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-05 3:11 ` Leizhen (ThunderTown)
@ 2024-09-05 3:45 ` Leizhen (ThunderTown)
2024-09-05 7:04 ` Leizhen (ThunderTown)
2024-09-09 9:22 ` Thomas Gleixner
0 siblings, 2 replies; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-05 3:45 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel
On 2024/9/5 11:11, Leizhen (ThunderTown) wrote:
>
>
> On 2024/9/4 21:39, Zhen Lei wrote:
>> If the conditions for starting fill are met, it means that all cores that
>> call fill() later are blocked until the first core completes the fill
>> operation. But obviously, for a core that has free nodes locally, it does
>> not need to be blocked(see below for why). This is good in stress
>> situations.
>>
>> 1. In the case of no nesting, a core uses only one node at a time. As long
>> as there is a local node, there is no need to use the free node in
>> obj_pool.
>> 2. In the case of nesting depth is one, nodes in obj_pool need to be used
>> only when there is only one local node.
>> #define ODEBUG_POOL_PERCPU_SIZE 64
>> #define ODEBUG_BATCH_SIZE 16
>> Assume that when nested, the probability of percpu_obj_pool having each
>> number of nodes is the same. The probability of only one node is less
>> than 1/17=6%. Assuming the probability of nesting is 5%, that's a
>> pretty high estimate. Then the probability of using obj_pool is
>> 6% * 5% = 0.3%. In other words, a 333-core environment produces only
>> one core to compete for obj_pool.
>> #define ODEBUG_POOL_MIN_LEVEL 256
>> #define ODEBUG_BATCH_SIZE 16
>> But we can tolerate "256 / (16 + 1)" = 15 cores competing at the same
>> time.
>
> One detail is omitted. In function debug_objects_mem_init(), an extra batch
> is reserved for each core.
> extras = num_possible_cpus() * ODEBUG_BATCH_SIZE;
> debug_objects_pool_min_level += extras;
>
> In addition, above method of calculating probabilities is wrong. The correct
> calculation method is as follows:
> When the number of local nodes is 0, fill is performed. When the number of
> local nodes is 1 and nested, 16 nodes are moved from obj_pool to obj_pool.
> As a result, the obj_pool resource pool keeps decreasing. When this happens
> continuously(The number of local nodes equal 0 is not met), the resource
> pool will eventually be exhausted. The error probability is:
> (1/2)^((256+16^ncpus)/17) * (5% + 5%^2 + ... + 5%^N) * 2/17 < 1e-7 (ncpus=1).
Should be:
==> (1/2)^((256+16^ncpus)/17) * 5% * 2/17 < 9e-8 (ncpus=1).
> 1/2 ==> denominator sequence: 0,1; numerator sequence: 1
> (5% + 5%^2 + ... + 5%^N) < 5% + (5%^2) * 2 = 0.055
> 17 = ODEBUG_BATCH_SIZ + 1, amount moved from obj_pool when the number of local nodes is 0.
> 2/17 ==> denominator sequence: 0-16; numerator sequence: 0,1
> The more cores, the lower the probability of exhaustion.
>
> If obj_pool is not filled only when there are more than two local nodes,
> the probability of exhaustion is:
> (1/3)^((256+16^ncpus)/17) * (5% + 5%^2 + ... + 5%^N) * 3/17 < < 2.3e-10
Should be:
==> (1/3)^((256+16^ncpus)/17) * (5%^2) * 3/17 < 1.03e-11 (ncpus=1).
> 1/3 ==> denominator sequence: 0,1,2; numerator sequence: 2
> 3/17 ==> denominator sequence: 0-16; numerator sequence: 0,1,2
Hi, Thomas Gleixner:
Seems to need to add an additional patch as follows to be foolproof.
I'll prepare it.
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index e175cc74f7b7899..d3f8cc7dc1c9291 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -245,6 +245,21 @@ alloc_object(void *addr, struct debug_bucket *b, const struct debug_obj_descr *d
raw_spin_lock(&pool_lock);
obj = __alloc_object(&obj_pool);
+ if (!obj) {
+ raw_spin_unlock(&pool_lock);
+ obj = kmem_cache_zalloc(obj_cache, __GFP_HIGH | GFP_NOWAIT);
+ if (!obj)
+ return NULL;
+
+ raw_spin_lock(&pool_lock);
+ debug_objects_allocated++;
+
+ /*
+ * It can be understood that obj is allocated immediately after
+ * being added to obj_pool.
+ */
+ obj_pool_used++;
+ }
if (obj) {
int cnt = 0;
>
>> 3. In the case of nesting depth more than one, the probability is lower
>> and negligible.
>> Nesting Depth=2: "2/17 * 5% * 5%" = 0.03%
>> Nesting Depth=3: "3/17 * 5% * 5% * 5%" = 0.002%
>>
>> However, to ensure sufficient reliability, obj_pool is not filled only
>> when there are more than two local nodes, reduce the probability of
>> problems to the impossible.
>>
>> Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
>> ---
>> lib/debugobjects.c | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/lib/debugobjects.c b/lib/debugobjects.c
>> index 7a8ccc94cb037ba..4f64b5d4329c27d 100644
>> --- a/lib/debugobjects.c
>> +++ b/lib/debugobjects.c
>> @@ -131,6 +131,16 @@ static void fill_pool(void)
>> struct debug_obj *obj;
>> unsigned long flags;
>>
>> + /*
>> + * The upper-layer function uses only one node at a time. If there are
>> + * more than two local nodes, it means that even if nesting occurs, it
>> + * doesn't matter. The probability of nesting depth >= 2 is extremely
>> + * low, and the number of global free nodes guarded by
>> + * debug_objects_pool_min_level is adequate.
>> + */
>> + if (likely(obj_cache) && this_cpu_read(percpu_obj_pool.obj_free) >= 2)
>> + return;
>> +
>> if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
>> return;
>>
>>
>
--
Regards,
Zhen Lei
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-05 3:45 ` Leizhen (ThunderTown)
@ 2024-09-05 7:04 ` Leizhen (ThunderTown)
2024-09-09 9:22 ` Thomas Gleixner
1 sibling, 0 replies; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-05 7:04 UTC (permalink / raw)
To: Andrew Morton, Thomas Gleixner, linux-kernel
On 2024/9/5 11:45, Leizhen (ThunderTown) wrote:
>
>
> On 2024/9/5 11:11, Leizhen (ThunderTown) wrote:
>>
>>
>> On 2024/9/4 21:39, Zhen Lei wrote:
>>> If the conditions for starting fill are met, it means that all cores that
>>> call fill() later are blocked until the first core completes the fill
>>> operation. But obviously, for a core that has free nodes locally, it does
>>> not need to be blocked(see below for why). This is good in stress
>>> situations.
>>>
>>> 1. In the case of no nesting, a core uses only one node at a time. As long
>>> as there is a local node, there is no need to use the free node in
>>> obj_pool.
>>> 2. In the case of nesting depth is one, nodes in obj_pool need to be used
>>> only when there is only one local node.
>>> #define ODEBUG_POOL_PERCPU_SIZE 64
>>> #define ODEBUG_BATCH_SIZE 16
>>> Assume that when nested, the probability of percpu_obj_pool having each
>>> number of nodes is the same. The probability of only one node is less
>>> than 1/17=6%. Assuming the probability of nesting is 5%, that's a
>>> pretty high estimate. Then the probability of using obj_pool is
>>> 6% * 5% = 0.3%. In other words, a 333-core environment produces only
>>> one core to compete for obj_pool.
>>> #define ODEBUG_POOL_MIN_LEVEL 256
>>> #define ODEBUG_BATCH_SIZE 16
>>> But we can tolerate "256 / (16 + 1)" = 15 cores competing at the same
>>> time.
>>
>> One detail is omitted. In function debug_objects_mem_init(), an extra batch
>> is reserved for each core.
>> extras = num_possible_cpus() * ODEBUG_BATCH_SIZE;
>> debug_objects_pool_min_level += extras;
>>
>> In addition, above method of calculating probabilities is wrong. The correct
>> calculation method is as follows:
>> When the number of local nodes is 0, fill is performed. When the number of
>> local nodes is 1 and nested, 16 nodes are moved from obj_pool to obj_pool.
>> As a result, the obj_pool resource pool keeps decreasing. When this happens
>> continuously(The number of local nodes equal 0 is not met), the resource
>> pool will eventually be exhausted. The error probability is:
>> (1/2)^((256+16^ncpus)/17) * (5% + 5%^2 + ... + 5%^N) * 2/17 < 1e-7 (ncpus=1).
>
> Should be:
> ==> (1/2)^((256+16^ncpus)/17) * 5% * 2/17 < 9e-8 (ncpus=1).
>
>> 1/2 ==> denominator sequence: 0,1; numerator sequence: 1
>> (5% + 5%^2 + ... + 5%^N) < 5% + (5%^2) * 2 = 0.055
>> 17 = ODEBUG_BATCH_SIZ + 1, amount moved from obj_pool when the number of local nodes is 0.
>> 2/17 ==> denominator sequence: 0-16; numerator sequence: 0,1
>> The more cores, the lower the probability of exhaustion.
>>
>> If obj_pool is not filled only when there are more than two local nodes,
>> the probability of exhaustion is:
>> (1/3)^((256+16^ncpus)/17) * (5% + 5%^2 + ... + 5%^N) * 3/17 < < 2.3e-10
>
> Should be:
> ==> (1/3)^((256+16^ncpus)/17) * (5%^2) * 3/17 < 1.03e-11 (ncpus=1).
>
>> 1/3 ==> denominator sequence: 0,1,2; numerator sequence: 2
>> 3/17 ==> denominator sequence: 0-16; numerator sequence: 0,1,2
>
> Hi, Thomas Gleixner:
> Seems to need to add an additional patch as follows to be foolproof.
> I'll prepare it.
I've rethinked this problem, and there's another remedy. When the number
of remaining free nodes is less than half of debug_objects_pool_min_level,
still do fill. In this way, "nr_cpus/2 + 256/(16+1)" cores are required to
bypass the check before obj_pool_free is updated, which is almost impossible.
But then again, I'm just theoretical, and I don't have the data, so maybe
the best solution is to give up this patch and talk about it in future.
By the way, I've found a new concern.
static int debug_objects_pool_size = ODEBUG_POOL_SIZE; //1024
static int debug_objects_pool_min_level = ODEBUG_POOL_MIN_LEVEL; //256
extras = num_possible_cpus() * ODEBUG_BATCH_SIZE; //16
debug_objects_pool_size += extras;
debug_objects_pool_min_level += extras;
When there are so many cores, it should be easier to walk back and forth
around debug_objects_pool_min_level. For example, nr_cpus=128,
debug_objects_pool_min_level = 128 *16 + 256 = 2304
debug_objects_pool_size - debug_objects_pool_min_level = 768 //fixed
There are many more loafers than workers. As above, it's better to
discuss it in future.
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 58de57090ac6389..2eb246901cf5367 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -135,6 +135,10 @@ static void fill_pool(void)
if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
return;
+ if (likely(obj_cache) &&
+ this_cpu_read(percpu_obj_pool.obj_free) > 0 &&
+ likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level / 2))
+ return;
/*
* Reuse objs from the global tofree list; they will be reinitialized
* when allocating.
>
> diff --git a/lib/debugobjects.c b/lib/debugobjects.c
> index e175cc74f7b7899..d3f8cc7dc1c9291 100644
> --- a/lib/debugobjects.c
> +++ b/lib/debugobjects.c
> @@ -245,6 +245,21 @@ alloc_object(void *addr, struct debug_bucket *b, const struct debug_obj_descr *d
>
> raw_spin_lock(&pool_lock);
> obj = __alloc_object(&obj_pool);
> + if (!obj) {
> + raw_spin_unlock(&pool_lock);
> + obj = kmem_cache_zalloc(obj_cache, __GFP_HIGH | GFP_NOWAIT);
> + if (!obj)
> + return NULL;
> +
> + raw_spin_lock(&pool_lock);
> + debug_objects_allocated++;
> +
> + /*
> + * It can be understood that obj is allocated immediately after
> + * being added to obj_pool.
> + */
> + obj_pool_used++;
> + }
> if (obj) {
> int cnt = 0;
>
>
>
>>
>>> 3. In the case of nesting depth more than one, the probability is lower
>>> and negligible.
>>> Nesting Depth=2: "2/17 * 5% * 5%" = 0.03%
>>> Nesting Depth=3: "3/17 * 5% * 5% * 5%" = 0.002%
>>>
>>> However, to ensure sufficient reliability, obj_pool is not filled only
>>> when there are more than two local nodes, reduce the probability of
>>> problems to the impossible.
>>>
>>> Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
>>> ---
>>> lib/debugobjects.c | 10 ++++++++++
>>> 1 file changed, 10 insertions(+)
>>>
>>> diff --git a/lib/debugobjects.c b/lib/debugobjects.c
>>> index 7a8ccc94cb037ba..4f64b5d4329c27d 100644
>>> --- a/lib/debugobjects.c
>>> +++ b/lib/debugobjects.c
>>> @@ -131,6 +131,16 @@ static void fill_pool(void)
>>> struct debug_obj *obj;
>>> unsigned long flags;
>>>
>>> + /*
>>> + * The upper-layer function uses only one node at a time. If there are
>>> + * more than two local nodes, it means that even if nesting occurs, it
>>> + * doesn't matter. The probability of nesting depth >= 2 is extremely
>>> + * low, and the number of global free nodes guarded by
>>> + * debug_objects_pool_min_level is adequate.
>>> + */
>>> + if (likely(obj_cache) && this_cpu_read(percpu_obj_pool.obj_free) >= 2)
>>> + return;
>>> +
>>> if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
>>> return;
>>>
>>>
>>
>
--
Regards,
Zhen Lei
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-05 3:45 ` Leizhen (ThunderTown)
2024-09-05 7:04 ` Leizhen (ThunderTown)
@ 2024-09-09 9:22 ` Thomas Gleixner
2024-09-09 11:03 ` Leizhen (ThunderTown)
2024-09-09 12:10 ` Thomas Gleixner
1 sibling, 2 replies; 22+ messages in thread
From: Thomas Gleixner @ 2024-09-09 9:22 UTC (permalink / raw)
To: Leizhen (ThunderTown), Andrew Morton, linux-kernel
On Thu, Sep 05 2024 at 11:45, Leizhen wrote:
> On 2024/9/5 11:11, Leizhen (ThunderTown) wrote:
> Seems to need to add an additional patch as follows to be foolproof.
> I'll prepare it.
>
> diff --git a/lib/debugobjects.c b/lib/debugobjects.c
> index e175cc74f7b7899..d3f8cc7dc1c9291 100644
> --- a/lib/debugobjects.c
> +++ b/lib/debugobjects.c
> @@ -245,6 +245,21 @@ alloc_object(void *addr, struct debug_bucket *b, const struct debug_obj_descr *d
>
> raw_spin_lock(&pool_lock);
> obj = __alloc_object(&obj_pool);
> + if (!obj) {
> + raw_spin_unlock(&pool_lock);
> + obj = kmem_cache_zalloc(obj_cache, __GFP_HIGH | GFP_NOWAIT);
> + if (!obj)
> + return NULL;
> +
> + raw_spin_lock(&pool_lock);
> + debug_objects_allocated++;
> +
> + /*
> + * It can be understood that obj is allocated immediately after
> + * being added to obj_pool.
> + */
> + obj_pool_used++;
> + }
> if (obj) {
> int cnt = 0;
No. That fails on RT. See debug_object_fill_pool().
Thanks,
tglx
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 9:22 ` Thomas Gleixner
@ 2024-09-09 11:03 ` Leizhen (ThunderTown)
2024-09-09 12:10 ` Thomas Gleixner
1 sibling, 0 replies; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-09 11:03 UTC (permalink / raw)
To: Thomas Gleixner, Andrew Morton, linux-kernel
On 2024/9/9 17:22, Thomas Gleixner wrote:
> On Thu, Sep 05 2024 at 11:45, Leizhen wrote:
>> On 2024/9/5 11:11, Leizhen (ThunderTown) wrote:
>> Seems to need to add an additional patch as follows to be foolproof.
>> I'll prepare it.
>>
>> diff --git a/lib/debugobjects.c b/lib/debugobjects.c
>> index e175cc74f7b7899..d3f8cc7dc1c9291 100644
>> --- a/lib/debugobjects.c
>> +++ b/lib/debugobjects.c
>> @@ -245,6 +245,21 @@ alloc_object(void *addr, struct debug_bucket *b, const struct debug_obj_descr *d
>>
>> raw_spin_lock(&pool_lock);
>> obj = __alloc_object(&obj_pool);
>> + if (!obj) {
>> + raw_spin_unlock(&pool_lock);
>> + obj = kmem_cache_zalloc(obj_cache, __GFP_HIGH | GFP_NOWAIT);
>> + if (!obj)
>> + return NULL;
>> +
>> + raw_spin_lock(&pool_lock);
>> + debug_objects_allocated++;
>> +
>> + /*
>> + * It can be understood that obj is allocated immediately after
>> + * being added to obj_pool.
>> + */
>> + obj_pool_used++;
>> + }
>> if (obj) {
>> int cnt = 0;
>
> No. That fails on RT. See debug_object_fill_pool().
OK, I got it, thanks.
>
> Thanks,
>
> tglx
> .
>
--
Regards,
Zhen Lei
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 9:22 ` Thomas Gleixner
2024-09-09 11:03 ` Leizhen (ThunderTown)
@ 2024-09-09 12:10 ` Thomas Gleixner
2024-09-09 13:51 ` Leizhen (ThunderTown)
1 sibling, 1 reply; 22+ messages in thread
From: Thomas Gleixner @ 2024-09-09 12:10 UTC (permalink / raw)
To: Leizhen (ThunderTown), Andrew Morton, linux-kernel
On Mon, Sep 09 2024 at 11:22, Thomas Gleixner wrote:
> On Thu, Sep 05 2024 at 11:45, Leizhen wrote:
>> + obj = kmem_cache_zalloc(obj_cache, __GFP_HIGH | GFP_NOWAIT);
>> + if (!obj)
>> + return NULL;
>
> No. That fails on RT. See debug_object_fill_pool().
Some more thoughts on this. The goal is to reduce contention on pool
lock. At the same time, this needs to ensure that the opportunistic fill
mechanism actually works reliably.
debug_object_fill_pool() is invoked from
- debug_object_init()
- debug_object_assert_init()
- debug_object_activate()
debug_object_init() is usually invoked from preemptible context. It will
most of the time consume a tracking object from the per CPU or the
global pool because re-initialization of a tracked object is rather
rare.
debug_object_assert_init() and debug_object_activate() only consume a
tracking object, when the to be tracked object is statically
initialized or the call site failed to initialize the object. Both can
be called from arbitrary contexts even under PREEMPT_RT, where
preemptible context is a prerequisite for pool refill via allocations.
And of course any CPU which sits in fill_pool() can be preempted if the
calling context is preemptible. And no, we can't disable preemption
accross the whole thing due to RT.
So something like the uncompiled below should reduce lock contention
significantly with a reasonable safety net.
Thanks,
tglx
---
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -125,14 +125,10 @@ static const char *obj_states[ODEBUG_STA
[ODEBUG_STATE_NOTAVAILABLE] = "not available",
};
-static void fill_pool(void)
+static void fill_pool_from_freelist(void)
{
- gfp_t gfp = __GFP_HIGH | __GFP_NOWARN;
+ static unsigned long state;
struct debug_obj *obj;
- unsigned long flags;
-
- if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
- return;
/*
* Reuse objs from the global obj_to_free list; they will be
@@ -141,25 +137,57 @@ static void fill_pool(void)
* obj_nr_tofree is checked locklessly; the READ_ONCE() pairs with
* the WRITE_ONCE() in pool_lock critical sections.
*/
- if (READ_ONCE(obj_nr_tofree)) {
- raw_spin_lock_irqsave(&pool_lock, flags);
- /*
- * Recheck with the lock held as the worker thread might have
- * won the race and freed the global free list already.
- */
- while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
- obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
- hlist_del(&obj->node);
- WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
- hlist_add_head(&obj->node, &obj_pool);
- WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
- }
- raw_spin_unlock_irqrestore(&pool_lock, flags);
+ if (!READ_ONCE(obj_nr_tofree))
+ return;
+
+ /*
+ * Prevent the context from being scheduled or interrupted after
+ * setting the state flag;
+ */
+ guard(irqsave)();
+
+ /*
+ * Avoid lock contention on &pool_lock and avoid making the cache
+ * line exclusive by testing the bit before attempting to set it.
+ */
+ if (test_bit(0, &state) || test_and_set_bit(0, &state))
+ return;
+
+ guard(raw_spinlock)(&pool_lock);
+ /*
+ * Recheck with the lock held as the worker thread might have
+ * won the race and freed the global free list already.
+ */
+ while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
+ obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
+ hlist_del(&obj->node);
+ WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
+ hlist_add_head(&obj->node, &obj_pool);
+ WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
}
+ clear_bit(0, &state);
+}
+
+static void fill_pool(void)
+{
+ gfp_t gfp = __GFP_HIGH | __GFP_NOWARN;
+ static atomic_t cpus_allocating;
if (unlikely(!obj_cache))
return;
+ /*
+ * Avoid allocation and lock contention when
+ *
+ * - the CPU local pool has at least 2 objects left
+ * - another CPU is already in the allocation path
+ * - the global pool has not reached the critical level yet
+ */
+ if (this_cpu_read(percpu_obj_pool.obj_free) > 1 && atomic_read(&cpus_allocating) &&
+ READ_ONCE(obj_pool_free) > (debug_objects_pool_min_level / 2))
+ return;
+
+ atomic_inc(&cpus_allocating);
while (READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
struct debug_obj *new[ODEBUG_BATCH_SIZE];
int cnt;
@@ -172,14 +200,14 @@ static void fill_pool(void)
if (!cnt)
return;
- raw_spin_lock_irqsave(&pool_lock, flags);
+ guard(raw_spinlock_irqsave)(&pool_lock);
while (cnt) {
hlist_add_head(&new[--cnt]->node, &obj_pool);
debug_objects_allocated++;
WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
}
- raw_spin_unlock_irqrestore(&pool_lock, flags);
}
+ atomic_dec(&cpus_allocating);
}
/*
@@ -598,6 +626,15 @@ static struct debug_obj *lookup_object_o
static void debug_objects_fill_pool(void)
{
+ if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
+ return;
+
+ /* Try reusing objects from obj_to_free_list */
+ fill_pool_from_freelist();
+
+ if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
+ return;
+
/*
* On RT enabled kernels the pool refill must happen in preemptible
* context -- for !RT kernels we rely on the fact that spinlock_t and
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 12:10 ` Thomas Gleixner
@ 2024-09-09 13:51 ` Leizhen (ThunderTown)
2024-09-09 14:35 ` Thomas Gleixner
2024-09-10 15:29 ` Leizhen (ThunderTown)
0 siblings, 2 replies; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-09 13:51 UTC (permalink / raw)
To: Thomas Gleixner, Andrew Morton, linux-kernel
On 2024/9/9 20:10, Thomas Gleixner wrote:
> On Mon, Sep 09 2024 at 11:22, Thomas Gleixner wrote:
>> On Thu, Sep 05 2024 at 11:45, Leizhen wrote:
>>> + obj = kmem_cache_zalloc(obj_cache, __GFP_HIGH | GFP_NOWAIT);
>>> + if (!obj)
>>> + return NULL;
>>
>> No. That fails on RT. See debug_object_fill_pool().
>
> Some more thoughts on this. The goal is to reduce contention on pool
> lock. At the same time, this needs to ensure that the opportunistic fill
> mechanism actually works reliably.
>
> debug_object_fill_pool() is invoked from
>
> - debug_object_init()
> - debug_object_assert_init()
> - debug_object_activate()
>
> debug_object_init() is usually invoked from preemptible context. It will
> most of the time consume a tracking object from the per CPU or the
> global pool because re-initialization of a tracked object is rather
> rare.
>
> debug_object_assert_init() and debug_object_activate() only consume a
> tracking object, when the to be tracked object is statically
> initialized or the call site failed to initialize the object. Both can
> be called from arbitrary contexts even under PREEMPT_RT, where
> preemptible context is a prerequisite for pool refill via allocations.
>
> And of course any CPU which sits in fill_pool() can be preempted if the
> calling context is preemptible. And no, we can't disable preemption
> accross the whole thing due to RT.
>
> So something like the uncompiled below should reduce lock contention
> significantly with a reasonable safety net.
It looks very good. Especially flexible use of 'state' and 'cpus_allocating'.
In this way, there is almost no conflict of lock 'pool_lock', and the more
cores, the less possible conflict.
Hi Thomas Gleixner:
Do you plan to post this patch? But this patch will conflict with my patch 5/6.
If you're going to merge my patch 5/6, hopefully yours will be applied after mine.
By the way, Do you have time to review the patches in the link below?
https://lkml.org/lkml/2024/9/4/1094
>
> Thanks,
>
> tglx
> ---
> --- a/lib/debugobjects.c
> +++ b/lib/debugobjects.c
> @@ -125,14 +125,10 @@ static const char *obj_states[ODEBUG_STA
> [ODEBUG_STATE_NOTAVAILABLE] = "not available",
> };
>
> -static void fill_pool(void)
> +static void fill_pool_from_freelist(void)
> {
> - gfp_t gfp = __GFP_HIGH | __GFP_NOWARN;
> + static unsigned long state;
> struct debug_obj *obj;
> - unsigned long flags;
> -
> - if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
> - return;
>
> /*
> * Reuse objs from the global obj_to_free list; they will be
> @@ -141,25 +137,57 @@ static void fill_pool(void)
> * obj_nr_tofree is checked locklessly; the READ_ONCE() pairs with
> * the WRITE_ONCE() in pool_lock critical sections.
> */
> - if (READ_ONCE(obj_nr_tofree)) {
> - raw_spin_lock_irqsave(&pool_lock, flags);
> - /*
> - * Recheck with the lock held as the worker thread might have
> - * won the race and freed the global free list already.
> - */
> - while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
> - obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
> - hlist_del(&obj->node);
> - WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
> - hlist_add_head(&obj->node, &obj_pool);
> - WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
> - }
> - raw_spin_unlock_irqrestore(&pool_lock, flags);
> + if (!READ_ONCE(obj_nr_tofree))
> + return;
> +
> + /*
> + * Prevent the context from being scheduled or interrupted after
> + * setting the state flag;
> + */
> + guard(irqsave)();
> +
> + /*
> + * Avoid lock contention on &pool_lock and avoid making the cache
> + * line exclusive by testing the bit before attempting to set it.
> + */
> + if (test_bit(0, &state) || test_and_set_bit(0, &state))
> + return;
> +
> + guard(raw_spinlock)(&pool_lock);
> + /*
> + * Recheck with the lock held as the worker thread might have
> + * won the race and freed the global free list already.
> + */
> + while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
> + obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
> + hlist_del(&obj->node);
> + WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
> + hlist_add_head(&obj->node, &obj_pool);
> + WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
> }
> + clear_bit(0, &state);
> +}
> +
> +static void fill_pool(void)
> +{
> + gfp_t gfp = __GFP_HIGH | __GFP_NOWARN;
> + static atomic_t cpus_allocating;
>
> if (unlikely(!obj_cache))
> return;
>
> + /*
> + * Avoid allocation and lock contention when
> + *
> + * - the CPU local pool has at least 2 objects left
> + * - another CPU is already in the allocation path
> + * - the global pool has not reached the critical level yet
> + */
> + if (this_cpu_read(percpu_obj_pool.obj_free) > 1 && atomic_read(&cpus_allocating) &&
> + READ_ONCE(obj_pool_free) > (debug_objects_pool_min_level / 2))
> + return;
> +
> + atomic_inc(&cpus_allocating);
> while (READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
> struct debug_obj *new[ODEBUG_BATCH_SIZE];
> int cnt;
> @@ -172,14 +200,14 @@ static void fill_pool(void)
> if (!cnt)
> return;
>
> - raw_spin_lock_irqsave(&pool_lock, flags);
> + guard(raw_spinlock_irqsave)(&pool_lock);
> while (cnt) {
> hlist_add_head(&new[--cnt]->node, &obj_pool);
> debug_objects_allocated++;
> WRITE_ONCE(obj_pool_free, obj_pool_free + 1);
> }
> - raw_spin_unlock_irqrestore(&pool_lock, flags);
> }
> + atomic_dec(&cpus_allocating);
> }
>
> /*
> @@ -598,6 +626,15 @@ static struct debug_obj *lookup_object_o
>
> static void debug_objects_fill_pool(void)
> {
> + if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
> + return;
> +
> + /* Try reusing objects from obj_to_free_list */
> + fill_pool_from_freelist();
> +
> + if (likely(READ_ONCE(obj_pool_free) >= debug_objects_pool_min_level))
> + return;
> +
> /*
> * On RT enabled kernels the pool refill must happen in preemptible
> * context -- for !RT kernels we rely on the fact that spinlock_t and
> .
>
--
Regards,
Zhen Lei
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 13:51 ` Leizhen (ThunderTown)
@ 2024-09-09 14:35 ` Thomas Gleixner
2024-09-09 14:41 ` Thomas Gleixner
2024-09-10 6:13 ` Leizhen (ThunderTown)
2024-09-10 15:29 ` Leizhen (ThunderTown)
1 sibling, 2 replies; 22+ messages in thread
From: Thomas Gleixner @ 2024-09-09 14:35 UTC (permalink / raw)
To: Leizhen (ThunderTown), Andrew Morton, linux-kernel
On Mon, Sep 09 2024 at 21:51, Leizhen wrote:
> On 2024/9/9 20:10, Thomas Gleixner wrote:
>> So something like the uncompiled below should reduce lock contention
>> significantly with a reasonable safety net.
>
> It looks very good. Especially flexible use of 'state' and 'cpus_allocating'.
> In this way, there is almost no conflict of lock 'pool_lock', and the more
> cores, the less possible conflict.
>
> Hi Thomas Gleixner:
> Do you plan to post this patch? But this patch will conflict with my patch 5/6.
> If you're going to merge my patch 5/6, hopefully yours will be applied
> after mine.
I'm short of cycles, so the best way is when you pick it up and
integrate it instead of 4/6 and post a v3.
> By the way, Do you have time to review the patches in the link below?
> https://lkml.org/lkml/2024/9/4/1094
Please use lore.kernel.org links. I've seen it but did not come around
to it yet because I was staring at this one. I'm single threaded :)
Thanks,
tglx
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 14:35 ` Thomas Gleixner
@ 2024-09-09 14:41 ` Thomas Gleixner
2024-09-10 6:13 ` Leizhen (ThunderTown)
1 sibling, 0 replies; 22+ messages in thread
From: Thomas Gleixner @ 2024-09-09 14:41 UTC (permalink / raw)
To: Leizhen (ThunderTown), Andrew Morton, linux-kernel
On Mon, Sep 09 2024 at 16:35, Thomas Gleixner wrote:
> On Mon, Sep 09 2024 at 21:51, Leizhen wrote:
>> On 2024/9/9 20:10, Thomas Gleixner wrote:
>>> So something like the uncompiled below should reduce lock contention
>>> significantly with a reasonable safety net.
>>
>> It looks very good. Especially flexible use of 'state' and 'cpus_allocating'.
>> In this way, there is almost no conflict of lock 'pool_lock', and the more
>> cores, the less possible conflict.
>>
>> Hi Thomas Gleixner:
>> Do you plan to post this patch? But this patch will conflict with my patch 5/6.
>> If you're going to merge my patch 5/6, hopefully yours will be applied
>> after mine.
>
> I'm short of cycles, so the best way is when you pick it up and
> integrate it instead of 4/6 and post a v3.
I'm picked up 1-3 from this series now. So you can drop them and work
against
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core/debugobjects
Thanks,
tglx
^ permalink raw reply [flat|nested] 22+ messages in thread
* [tip: core/debugobjects] debugobjects: Remove redundant checks in fill_pool()
2024-09-04 13:39 ` [PATCH v2 3/6] debugobjects: Remove redundant checks " Zhen Lei
@ 2024-09-09 14:59 ` tip-bot2 for Zhen Lei
0 siblings, 0 replies; 22+ messages in thread
From: tip-bot2 for Zhen Lei @ 2024-09-09 14:59 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Zhen Lei, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the core/debugobjects branch of tip:
Commit-ID: 63a4a9b52c3c7f86351710739011717a36652b72
Gitweb: https://git.kernel.org/tip/63a4a9b52c3c7f86351710739011717a36652b72
Author: Zhen Lei <thunder.leizhen@huawei.com>
AuthorDate: Wed, 04 Sep 2024 21:39:41 +08:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Mon, 09 Sep 2024 16:40:26 +02:00
debugobjects: Remove redundant checks in fill_pool()
fill_pool() checks locklessly at the beginning whether the pool has to be
refilled. After that it checks locklessly in a loop whether the free list
contains objects and repeats the refill check.
If both conditions are true, it acquires the pool lock and tries to move
objects from the free list to the pool repeating the same checks again.
There are two redundant issues with that:
1) The repeated check for the fill condition
2) The loop processing
The repeated check is pointless as it was just established that fill is
required. The condition has to be re-evaluated under the lock anyway.
The loop processing is not required either because there is practically
zero chance that a repeated attempt will succeed if the checks under the
lock terminate the moving of objects.
Remove the redundant check and replace the loop with a simple if condition.
[ tglx: Massaged change log ]
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240904133944.2124-4-thunder.leizhen@huawei.com
---
lib/debugobjects.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 6329a86..5ce473a 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -135,15 +135,13 @@ static void fill_pool(void)
return;
/*
- * Reuse objs from the global free list; they will be reinitialized
- * when allocating.
+ * Reuse objs from the global obj_to_free list; they will be
+ * reinitialized when allocating.
*
- * Both obj_nr_tofree and obj_pool_free are checked locklessly; the
- * READ_ONCE()s pair with the WRITE_ONCE()s in pool_lock critical
- * sections.
+ * obj_nr_tofree is checked locklessly; the READ_ONCE() pairs with
+ * the WRITE_ONCE() in pool_lock critical sections.
*/
- while (READ_ONCE(obj_nr_tofree) &&
- READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
+ if (READ_ONCE(obj_nr_tofree)) {
raw_spin_lock_irqsave(&pool_lock, flags);
/*
* Recheck with the lock held as the worker thread might have
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [tip: core/debugobjects] debugobjects: Fix conditions in fill_pool()
2024-09-04 13:39 ` [PATCH v2 2/6] debugobjects: Fix the misuse of global variables in fill_pool() Zhen Lei
@ 2024-09-09 14:59 ` tip-bot2 for Zhen Lei
0 siblings, 0 replies; 22+ messages in thread
From: tip-bot2 for Zhen Lei @ 2024-09-09 14:59 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Zhen Lei, Thomas Gleixner, stable, x86, linux-kernel
The following commit has been merged into the core/debugobjects branch of tip:
Commit-ID: 684d28feb8546d1e9597aa363c3bfcf52fe250b7
Gitweb: https://git.kernel.org/tip/684d28feb8546d1e9597aa363c3bfcf52fe250b7
Author: Zhen Lei <thunder.leizhen@huawei.com>
AuthorDate: Wed, 04 Sep 2024 21:39:40 +08:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Mon, 09 Sep 2024 16:40:25 +02:00
debugobjects: Fix conditions in fill_pool()
fill_pool() uses 'obj_pool_min_free' to decide whether objects should be
handed back to the kmem cache. But 'obj_pool_min_free' records the lowest
historical value of the number of objects in the object pool and not the
minimum number of objects which should be kept in the pool.
Use 'debug_objects_pool_min_level' instead, which holds the minimum number
which was scaled to the number of CPUs at boot time.
[ tglx: Massage change log ]
Fixes: d26bf5056fc0 ("debugobjects: Reduce number of pool_lock acquisitions in fill_pool()")
Fixes: 36c4ead6f6df ("debugobjects: Add global free list and the counter")
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/20240904133944.2124-3-thunder.leizhen@huawei.com
---
lib/debugobjects.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 7226fdb..6329a86 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -142,13 +142,14 @@ static void fill_pool(void)
* READ_ONCE()s pair with the WRITE_ONCE()s in pool_lock critical
* sections.
*/
- while (READ_ONCE(obj_nr_tofree) && (READ_ONCE(obj_pool_free) < obj_pool_min_free)) {
+ while (READ_ONCE(obj_nr_tofree) &&
+ READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
raw_spin_lock_irqsave(&pool_lock, flags);
/*
* Recheck with the lock held as the worker thread might have
* won the race and freed the global free list already.
*/
- while (obj_nr_tofree && (obj_pool_free < obj_pool_min_free)) {
+ while (obj_nr_tofree && (obj_pool_free < debug_objects_pool_min_level)) {
obj = hlist_entry(obj_to_free.first, typeof(*obj), node);
hlist_del(&obj->node);
WRITE_ONCE(obj_nr_tofree, obj_nr_tofree - 1);
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [tip: core/debugobjects] debugobjects: Fix the compilation attributes of some global variables
2024-09-04 13:39 ` [PATCH v2 1/6] debugobjects: Fix the compilation attributes of some global variables Zhen Lei
@ 2024-09-09 14:59 ` tip-bot2 for Zhen Lei
0 siblings, 0 replies; 22+ messages in thread
From: tip-bot2 for Zhen Lei @ 2024-09-09 14:59 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Thomas Gleixner, Zhen Lei, x86, linux-kernel
The following commit has been merged into the core/debugobjects branch of tip:
Commit-ID: e4757c710ba27a1d499cf5941fc3950e9e542731
Gitweb: https://git.kernel.org/tip/e4757c710ba27a1d499cf5941fc3950e9e542731
Author: Zhen Lei <thunder.leizhen@huawei.com>
AuthorDate: Wed, 04 Sep 2024 21:39:39 +08:00
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Mon, 09 Sep 2024 16:40:25 +02:00
debugobjects: Fix the compilation attributes of some global variables
1. Both debug_objects_pool_min_level and debug_objects_pool_size are
read-only after initialization, change attribute '__read_mostly' to
'__ro_after_init', and remove '__data_racy'.
2. Many global variables are read in the debug_stats_show() function, but
didn't mask KCSAN's detection. Add '__data_racy' for them.
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20240904133944.2124-2-thunder.leizhen@huawei.com
---
lib/debugobjects.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/lib/debugobjects.c b/lib/debugobjects.c
index 7cea91e..7226fdb 100644
--- a/lib/debugobjects.c
+++ b/lib/debugobjects.c
@@ -70,10 +70,10 @@ static HLIST_HEAD(obj_to_free);
* made at debug_stats_show(). Both obj_pool_min_free and obj_pool_max_used
* can be off.
*/
-static int obj_pool_min_free = ODEBUG_POOL_SIZE;
-static int obj_pool_free = ODEBUG_POOL_SIZE;
+static int __data_racy obj_pool_min_free = ODEBUG_POOL_SIZE;
+static int __data_racy obj_pool_free = ODEBUG_POOL_SIZE;
static int obj_pool_used;
-static int obj_pool_max_used;
+static int __data_racy obj_pool_max_used;
static bool obj_freeing;
/* The number of objs on the global free list */
static int obj_nr_tofree;
@@ -84,9 +84,9 @@ static int __data_racy debug_objects_fixups __read_mostly;
static int __data_racy debug_objects_warnings __read_mostly;
static int __data_racy debug_objects_enabled __read_mostly
= CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT;
-static int __data_racy debug_objects_pool_size __read_mostly
+static int debug_objects_pool_size __ro_after_init
= ODEBUG_POOL_SIZE;
-static int __data_racy debug_objects_pool_min_level __read_mostly
+static int debug_objects_pool_min_level __ro_after_init
= ODEBUG_POOL_MIN_LEVEL;
static const struct debug_obj_descr *descr_test __read_mostly;
@@ -95,8 +95,8 @@ static struct kmem_cache *obj_cache __ro_after_init;
/*
* Track numbers of kmem_cache_alloc()/free() calls done.
*/
-static int debug_objects_allocated;
-static int debug_objects_freed;
+static int __data_racy debug_objects_allocated;
+static int __data_racy debug_objects_freed;
static void free_obj_work(struct work_struct *work);
static DECLARE_DELAYED_WORK(debug_obj_work, free_obj_work);
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 14:35 ` Thomas Gleixner
2024-09-09 14:41 ` Thomas Gleixner
@ 2024-09-10 6:13 ` Leizhen (ThunderTown)
1 sibling, 0 replies; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-10 6:13 UTC (permalink / raw)
To: Thomas Gleixner, Andrew Morton, linux-kernel
On 2024/9/9 22:35, Thomas Gleixner wrote:
> On Mon, Sep 09 2024 at 21:51, Leizhen wrote:
>> On 2024/9/9 20:10, Thomas Gleixner wrote:
>>> So something like the uncompiled below should reduce lock contention
>>> significantly with a reasonable safety net.
>>
>> It looks very good. Especially flexible use of 'state' and 'cpus_allocating'.
>> In this way, there is almost no conflict of lock 'pool_lock', and the more
>> cores, the less possible conflict.
>>
>> Hi Thomas Gleixner:
>> Do you plan to post this patch? But this patch will conflict with my patch 5/6.
>> If you're going to merge my patch 5/6, hopefully yours will be applied
>> after mine.
>
> I'm short of cycles, so the best way is when you pick it up and
> integrate it instead of 4/6 and post a v3.
OK
>
>> By the way, Do you have time to review the patches in the link below?
>> https://lkml.org/lkml/2024/9/4/1094
>
> Please use lore.kernel.org links. I've seen it but did not come around
OK
> to it yet because I was staring at this one. I'm single threaded :)
Mostly you big shot are too busy.
>
> Thanks,
>
> tglx
>
>
>
> .
>
--
Regards,
Zhen Lei
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-09 13:51 ` Leizhen (ThunderTown)
2024-09-09 14:35 ` Thomas Gleixner
@ 2024-09-10 15:29 ` Leizhen (ThunderTown)
2024-09-11 1:21 ` Leizhen (ThunderTown)
1 sibling, 1 reply; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-10 15:29 UTC (permalink / raw)
To: Thomas Gleixner, Andrew Morton, linux-kernel
On 2024/9/9 21:51, Leizhen (ThunderTown) wrote:
> +static void fill_pool(void)
> +{
> + gfp_t gfp = __GFP_HIGH | __GFP_NOWARN;
> + static atomic_t cpus_allocating;
>
> if (unlikely(!obj_cache))
> return;
>
> + /*
> + * Avoid allocation and lock contention when
> + *
> + * - the CPU local pool has at least 2 objects left
> + * - another CPU is already in the allocation path
> + * - the global pool has not reached the critical level yet
> + */
> + if (this_cpu_read(percpu_obj_pool.obj_free) > 1 && atomic_read(&cpus_allocating) &&
I rethink that 'cpus_allocating' and 'percpu_obj_pool.obj_free > 1' should be
contradictory. We can only choose one of them. If 'cpus_allocating' can work,
there's no need for another. Since I had no way to prove that 'cpus_allocating'
would work, I removed it in V3.
> + READ_ONCE(obj_pool_free) > (debug_objects_pool_min_level / 2))
> + return;
> +
> + atomic_inc(&cpus_allocating);
> while (READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
> struct debug_obj *new[ODEBUG_BATCH_SIZE];
> int cnt;
--
Regards,
Zhen Lei
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally
2024-09-10 15:29 ` Leizhen (ThunderTown)
@ 2024-09-11 1:21 ` Leizhen (ThunderTown)
0 siblings, 0 replies; 22+ messages in thread
From: Leizhen (ThunderTown) @ 2024-09-11 1:21 UTC (permalink / raw)
To: Thomas Gleixner, Andrew Morton, linux-kernel
On 2024/9/10 23:29, Leizhen (ThunderTown) wrote:
>
>
> On 2024/9/9 21:51, Leizhen (ThunderTown) wrote:
>> +static void fill_pool(void)
>> +{
>> + gfp_t gfp = __GFP_HIGH | __GFP_NOWARN;
>> + static atomic_t cpus_allocating;
>>
>> if (unlikely(!obj_cache))
>> return;
>>
>> + /*
>> + * Avoid allocation and lock contention when
>> + *
>> + * - the CPU local pool has at least 2 objects left
>> + * - another CPU is already in the allocation path
>> + * - the global pool has not reached the critical level yet
>> + */
>> + if (this_cpu_read(percpu_obj_pool.obj_free) > 1 && atomic_read(&cpus_allocating) &&
>
> I rethink that 'cpus_allocating' and 'percpu_obj_pool.obj_free > 1' should be
> contradictory. We can only choose one of them. If 'cpus_allocating' can work,
> there's no need for another. Since I had no way to prove that 'cpus_allocating'
Sorry, I mistyped in a hurry yesterday, and it's 'percpu_obj_pool.obj_free > 1'
that should be deleted.
> would work, I removed it in V3.
>
>> + READ_ONCE(obj_pool_free) > (debug_objects_pool_min_level / 2))
>> + return;
>> +
>> + atomic_inc(&cpus_allocating);
>> while (READ_ONCE(obj_pool_free) < debug_objects_pool_min_level) {
>> struct debug_obj *new[ODEBUG_BATCH_SIZE];
>> int cnt;
>
--
Regards,
Zhen Lei
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2024-09-11 1:21 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-04 13:39 [PATCH v2 0/6] debugobjects: Do some minor optimizations, fixes and cleaups Zhen Lei
2024-09-04 13:39 ` [PATCH v2 1/6] debugobjects: Fix the compilation attributes of some global variables Zhen Lei
2024-09-09 14:59 ` [tip: core/debugobjects] " tip-bot2 for Zhen Lei
2024-09-04 13:39 ` [PATCH v2 2/6] debugobjects: Fix the misuse of global variables in fill_pool() Zhen Lei
2024-09-09 14:59 ` [tip: core/debugobjects] debugobjects: Fix conditions " tip-bot2 for Zhen Lei
2024-09-04 13:39 ` [PATCH v2 3/6] debugobjects: Remove redundant checks " Zhen Lei
2024-09-09 14:59 ` [tip: core/debugobjects] " tip-bot2 for Zhen Lei
2024-09-04 13:39 ` [PATCH v2 4/6] debugobjects: Don't start fill if there are remaining nodes locally Zhen Lei
2024-09-05 3:11 ` Leizhen (ThunderTown)
2024-09-05 3:45 ` Leizhen (ThunderTown)
2024-09-05 7:04 ` Leizhen (ThunderTown)
2024-09-09 9:22 ` Thomas Gleixner
2024-09-09 11:03 ` Leizhen (ThunderTown)
2024-09-09 12:10 ` Thomas Gleixner
2024-09-09 13:51 ` Leizhen (ThunderTown)
2024-09-09 14:35 ` Thomas Gleixner
2024-09-09 14:41 ` Thomas Gleixner
2024-09-10 6:13 ` Leizhen (ThunderTown)
2024-09-10 15:29 ` Leizhen (ThunderTown)
2024-09-11 1:21 ` Leizhen (ThunderTown)
2024-09-04 13:39 ` [PATCH v2 5/6] debugobjects: Use hlist_splice_init() to reduce lock conflicts Zhen Lei
2024-09-04 13:39 ` [PATCH v2 6/6] debugobjects: Delete a piece of redundant code Zhen Lei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox