Skip to content

Commit f57ebb9

Browse files
committed
debugobjects: Implement batch processing
Adding and removing single objects in a loop is bad in terms of lock contention and cache line accesses. To implement batching, record the last object in a batch in the object itself. This is trivialy possible as hlists are strictly stacks. At a batch boundary, when the first object is added to the list the object stores a pointer to itself in debug_obj::batch_last. When the next object is added to the list then the batch_last pointer is retrieved from the first object in the list and stored in the to be added one. That means for batch processing the first object always has a pointer to the last object in a batch, which allows to move batches in a cache line efficient way and reduces the lock held time. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Zhen Lei <thunder.leizhen@huawei.com> Link: https://lore.kernel.org/all/20241007164914.258995000@linutronix.de
1 parent aebbfe0 commit f57ebb9

File tree

1 file changed

+46
-15
lines changed

1 file changed

+46
-15
lines changed

lib/debugobjects.c

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,18 +149,31 @@ static __always_inline bool pool_must_refill(struct obj_pool *pool)
149149

150150
static bool pool_move_batch(struct obj_pool *dst, struct obj_pool *src)
151151
{
152-
if (dst->cnt + ODEBUG_BATCH_SIZE > dst->max_cnt || !src->cnt)
152+
struct hlist_node *last, *next_batch, *first_batch;
153+
struct debug_obj *obj;
154+
155+
if (dst->cnt >= dst->max_cnt || !src->cnt)
153156
return false;
154157

155-
for (int i = 0; i < ODEBUG_BATCH_SIZE && src->cnt; i++) {
156-
struct hlist_node *node = src->objects.first;
158+
first_batch = src->objects.first;
159+
obj = hlist_entry(first_batch, typeof(*obj), node);
160+
last = obj->batch_last;
161+
next_batch = last->next;
157162

158-
WRITE_ONCE(src->cnt, src->cnt - 1);
159-
WRITE_ONCE(dst->cnt, dst->cnt + 1);
163+
/* Move the next batch to the front of the source pool */
164+
src->objects.first = next_batch;
165+
if (next_batch)
166+
next_batch->pprev = &src->objects.first;
160167

161-
hlist_del(node);
162-
hlist_add_head(node, &dst->objects);
163-
}
168+
/* Add the extracted batch to the destination pool */
169+
last->next = dst->objects.first;
170+
if (last->next)
171+
last->next->pprev = &last->next;
172+
first_batch->pprev = &dst->objects.first;
173+
dst->objects.first = first_batch;
174+
175+
WRITE_ONCE(src->cnt, src->cnt - ODEBUG_BATCH_SIZE);
176+
WRITE_ONCE(dst->cnt, dst->cnt + ODEBUG_BATCH_SIZE);
164177
return true;
165178
}
166179

@@ -182,16 +195,27 @@ static bool pool_push_batch(struct obj_pool *dst, struct hlist_head *head)
182195

183196
static bool pool_pop_batch(struct hlist_head *head, struct obj_pool *src)
184197
{
198+
struct hlist_node *last, *next;
199+
struct debug_obj *obj;
200+
185201
if (!src->cnt)
186202
return false;
187203

188-
for (int i = 0; src->cnt && i < ODEBUG_BATCH_SIZE; i++) {
189-
struct hlist_node *node = src->objects.first;
204+
/* Move the complete list to the head */
205+
hlist_move_list(&src->objects, head);
190206

191-
WRITE_ONCE(src->cnt, src->cnt - 1);
192-
hlist_del(node);
193-
hlist_add_head(node, head);
194-
}
207+
obj = hlist_entry(head->first, typeof(*obj), node);
208+
last = obj->batch_last;
209+
next = last->next;
210+
/* Disconnect the batch from the list */
211+
last->next = NULL;
212+
213+
/* Move the node after last back to the source pool. */
214+
src->objects.first = next;
215+
if (next)
216+
next->pprev = &src->objects.first;
217+
218+
WRITE_ONCE(src->cnt, src->cnt - ODEBUG_BATCH_SIZE);
195219
return true;
196220
}
197221

@@ -226,7 +250,7 @@ static struct debug_obj *pcpu_alloc(void)
226250
if (!pool_move_batch(pcp, &pool_global))
227251
return NULL;
228252
}
229-
obj_pool_used += pcp->cnt;
253+
obj_pool_used += ODEBUG_BATCH_SIZE;
230254

231255
if (obj_pool_used > obj_pool_max_used)
232256
obj_pool_max_used = obj_pool_used;
@@ -239,9 +263,16 @@ static struct debug_obj *pcpu_alloc(void)
239263
static void pcpu_free(struct debug_obj *obj)
240264
{
241265
struct obj_pool *pcp = this_cpu_ptr(&pool_pcpu);
266+
struct debug_obj *first;
242267

243268
lockdep_assert_irqs_disabled();
244269

270+
if (!(pcp->cnt % ODEBUG_BATCH_SIZE)) {
271+
obj->batch_last = &obj->node;
272+
} else {
273+
first = hlist_entry(pcp->objects.first, typeof(*first), node);
274+
obj->batch_last = first->batch_last;
275+
}
245276
hlist_add_head(&obj->node, &pcp->objects);
246277
pcp->cnt++;
247278

0 commit comments

Comments
 (0)