diff options
Diffstat (limited to 'lib/debugobjects.c')
-rw-r--r-- | lib/debugobjects.c | 61 |
1 files changed, 49 insertions, 12 deletions
diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 7ea19fa63561..ede96c659552 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c | |||
@@ -32,6 +32,14 @@ | |||
32 | #define ODEBUG_CHUNK_SIZE (1 << ODEBUG_CHUNK_SHIFT) | 32 | #define ODEBUG_CHUNK_SIZE (1 << ODEBUG_CHUNK_SHIFT) |
33 | #define ODEBUG_CHUNK_MASK (~(ODEBUG_CHUNK_SIZE - 1)) | 33 | #define ODEBUG_CHUNK_MASK (~(ODEBUG_CHUNK_SIZE - 1)) |
34 | 34 | ||
35 | /* | ||
36 | * We limit the freeing of debug objects via workqueue at a maximum | ||
37 | * frequency of 10Hz and about 1024 objects for each freeing operation. | ||
38 | * So it is freeing at most 10k debug objects per second. | ||
39 | */ | ||
40 | #define ODEBUG_FREE_WORK_MAX 1024 | ||
41 | #define ODEBUG_FREE_WORK_DELAY DIV_ROUND_UP(HZ, 10) | ||
42 | |||
35 | struct debug_bucket { | 43 | struct debug_bucket { |
36 | struct hlist_head list; | 44 | struct hlist_head list; |
37 | raw_spinlock_t lock; | 45 | raw_spinlock_t lock; |
@@ -68,6 +76,7 @@ static int obj_pool_min_free = ODEBUG_POOL_SIZE; | |||
68 | static int obj_pool_free = ODEBUG_POOL_SIZE; | 76 | static int obj_pool_free = ODEBUG_POOL_SIZE; |
69 | static int obj_pool_used; | 77 | static int obj_pool_used; |
70 | static int obj_pool_max_used; | 78 | static int obj_pool_max_used; |
79 | static bool obj_freeing; | ||
71 | /* The number of objs on the global free list */ | 80 | /* The number of objs on the global free list */ |
72 | static int obj_nr_tofree; | 81 | static int obj_nr_tofree; |
73 | 82 | ||
@@ -91,7 +100,7 @@ static int debug_objects_allocated; | |||
91 | static int debug_objects_freed; | 100 | static int debug_objects_freed; |
92 | 101 | ||
93 | static void free_obj_work(struct work_struct *work); | 102 | static void free_obj_work(struct work_struct *work); |
94 | static DECLARE_WORK(debug_obj_work, free_obj_work); | 103 | static DECLARE_DELAYED_WORK(debug_obj_work, free_obj_work); |
95 | 104 | ||
96 | static int __init enable_object_debug(char *str) | 105 | static int __init enable_object_debug(char *str) |
97 | { | 106 | { |
@@ -282,13 +291,19 @@ static void free_obj_work(struct work_struct *work) | |||
282 | unsigned long flags; | 291 | unsigned long flags; |
283 | HLIST_HEAD(tofree); | 292 | HLIST_HEAD(tofree); |
284 | 293 | ||
294 | WRITE_ONCE(obj_freeing, false); | ||
285 | if (!raw_spin_trylock_irqsave(&pool_lock, flags)) | 295 | if (!raw_spin_trylock_irqsave(&pool_lock, flags)) |
286 | return; | 296 | return; |
287 | 297 | ||
298 | if (obj_pool_free >= debug_objects_pool_size) | ||
299 | goto free_objs; | ||
300 | |||
288 | /* | 301 | /* |
289 | * The objs on the pool list might be allocated before the work is | 302 | * The objs on the pool list might be allocated before the work is |
290 | * run, so recheck if pool list it full or not, if not fill pool | 303 | * run, so recheck if pool list it full or not, if not fill pool |
291 | * list from the global free list. | 304 | * list from the global free list. As it is likely that a workload |
305 | * may be gearing up to use more and more objects, don't free any | ||
306 | * of them until the next round. | ||
292 | */ | 307 | */ |
293 | while (obj_nr_tofree && obj_pool_free < debug_objects_pool_size) { | 308 | while (obj_nr_tofree && obj_pool_free < debug_objects_pool_size) { |
294 | obj = hlist_entry(obj_to_free.first, typeof(*obj), node); | 309 | obj = hlist_entry(obj_to_free.first, typeof(*obj), node); |
@@ -297,7 +312,10 @@ static void free_obj_work(struct work_struct *work) | |||
297 | obj_pool_free++; | 312 | obj_pool_free++; |
298 | obj_nr_tofree--; | 313 | obj_nr_tofree--; |
299 | } | 314 | } |
315 | raw_spin_unlock_irqrestore(&pool_lock, flags); | ||
316 | return; | ||
300 | 317 | ||
318 | free_objs: | ||
301 | /* | 319 | /* |
302 | * Pool list is already full and there are still objs on the free | 320 | * Pool list is already full and there are still objs on the free |
303 | * list. Move remaining free objs to a temporary list to free the | 321 | * list. Move remaining free objs to a temporary list to free the |
@@ -316,7 +334,7 @@ static void free_obj_work(struct work_struct *work) | |||
316 | } | 334 | } |
317 | } | 335 | } |
318 | 336 | ||
319 | static bool __free_object(struct debug_obj *obj) | 337 | static void __free_object(struct debug_obj *obj) |
320 | { | 338 | { |
321 | struct debug_obj *objs[ODEBUG_BATCH_SIZE]; | 339 | struct debug_obj *objs[ODEBUG_BATCH_SIZE]; |
322 | struct debug_percpu_free *percpu_pool; | 340 | struct debug_percpu_free *percpu_pool; |
@@ -336,7 +354,7 @@ static bool __free_object(struct debug_obj *obj) | |||
336 | hlist_add_head(&obj->node, &percpu_pool->free_objs); | 354 | hlist_add_head(&obj->node, &percpu_pool->free_objs); |
337 | percpu_pool->obj_free++; | 355 | percpu_pool->obj_free++; |
338 | local_irq_restore(flags); | 356 | local_irq_restore(flags); |
339 | return false; | 357 | return; |
340 | } | 358 | } |
341 | 359 | ||
342 | /* | 360 | /* |
@@ -352,7 +370,8 @@ static bool __free_object(struct debug_obj *obj) | |||
352 | 370 | ||
353 | free_to_obj_pool: | 371 | free_to_obj_pool: |
354 | raw_spin_lock(&pool_lock); | 372 | raw_spin_lock(&pool_lock); |
355 | work = (obj_pool_free > debug_objects_pool_size) && obj_cache; | 373 | work = (obj_pool_free > debug_objects_pool_size) && obj_cache && |
374 | (obj_nr_tofree < ODEBUG_FREE_WORK_MAX); | ||
356 | obj_pool_used--; | 375 | obj_pool_used--; |
357 | 376 | ||
358 | if (work) { | 377 | if (work) { |
@@ -366,6 +385,21 @@ free_to_obj_pool: | |||
366 | &obj_to_free); | 385 | &obj_to_free); |
367 | } | 386 | } |
368 | } | 387 | } |
388 | |||
389 | if ((obj_pool_free > debug_objects_pool_size) && | ||
390 | (obj_nr_tofree < ODEBUG_FREE_WORK_MAX)) { | ||
391 | int i; | ||
392 | |||
393 | /* | ||
394 | * Free one more batch of objects from obj_pool. | ||
395 | */ | ||
396 | for (i = 0; i < ODEBUG_BATCH_SIZE; i++) { | ||
397 | obj = __alloc_object(&obj_pool); | ||
398 | hlist_add_head(&obj->node, &obj_to_free); | ||
399 | obj_pool_free--; | ||
400 | obj_nr_tofree++; | ||
401 | } | ||
402 | } | ||
369 | } else { | 403 | } else { |
370 | obj_pool_free++; | 404 | obj_pool_free++; |
371 | hlist_add_head(&obj->node, &obj_pool); | 405 | hlist_add_head(&obj->node, &obj_pool); |
@@ -380,7 +414,6 @@ free_to_obj_pool: | |||
380 | } | 414 | } |
381 | raw_spin_unlock(&pool_lock); | 415 | raw_spin_unlock(&pool_lock); |
382 | local_irq_restore(flags); | 416 | local_irq_restore(flags); |
383 | return work; | ||
384 | } | 417 | } |
385 | 418 | ||
386 | /* | 419 | /* |
@@ -389,8 +422,11 @@ free_to_obj_pool: | |||
389 | */ | 422 | */ |
390 | static void free_object(struct debug_obj *obj) | 423 | static void free_object(struct debug_obj *obj) |
391 | { | 424 | { |
392 | if (__free_object(obj)) | 425 | __free_object(obj); |
393 | schedule_work(&debug_obj_work); | 426 | if (!obj_freeing && obj_nr_tofree) { |
427 | WRITE_ONCE(obj_freeing, true); | ||
428 | schedule_delayed_work(&debug_obj_work, ODEBUG_FREE_WORK_DELAY); | ||
429 | } | ||
394 | } | 430 | } |
395 | 431 | ||
396 | /* | 432 | /* |
@@ -880,7 +916,6 @@ static void __debug_check_no_obj_freed(const void *address, unsigned long size) | |||
880 | struct hlist_node *tmp; | 916 | struct hlist_node *tmp; |
881 | struct debug_obj *obj; | 917 | struct debug_obj *obj; |
882 | int cnt, objs_checked = 0; | 918 | int cnt, objs_checked = 0; |
883 | bool work = false; | ||
884 | 919 | ||
885 | saddr = (unsigned long) address; | 920 | saddr = (unsigned long) address; |
886 | eaddr = saddr + size; | 921 | eaddr = saddr + size; |
@@ -911,7 +946,7 @@ repeat: | |||
911 | goto repeat; | 946 | goto repeat; |
912 | default: | 947 | default: |
913 | hlist_del(&obj->node); | 948 | hlist_del(&obj->node); |
914 | work |= __free_object(obj); | 949 | __free_object(obj); |
915 | break; | 950 | break; |
916 | } | 951 | } |
917 | } | 952 | } |
@@ -927,8 +962,10 @@ repeat: | |||
927 | debug_objects_maxchecked = objs_checked; | 962 | debug_objects_maxchecked = objs_checked; |
928 | 963 | ||
929 | /* Schedule work to actually kmem_cache_free() objects */ | 964 | /* Schedule work to actually kmem_cache_free() objects */ |
930 | if (work) | 965 | if (!obj_freeing && obj_nr_tofree) { |
931 | schedule_work(&debug_obj_work); | 966 | WRITE_ONCE(obj_freeing, true); |
967 | schedule_delayed_work(&debug_obj_work, ODEBUG_FREE_WORK_DELAY); | ||
968 | } | ||
932 | } | 969 | } |
933 | 970 | ||
934 | void debug_check_no_obj_freed(const void *address, unsigned long size) | 971 | void debug_check_no_obj_freed(const void *address, unsigned long size) |