The patch introduces shrinker::id number, which is used to enumerate memcg-aware shrinkers. The number start from 0, and the code tries to maintain it as small as possible. This will be used as to represent a memcg-aware shrinkers in memcg shrinkers map. Signed-off-by: Kirill Tkhai <ktkhai@xxxxxxxxxxxxx> --- include/linux/shrinker.h | 1 + mm/vmscan.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h index a3894918a436..738de8ef5246 100644 --- a/include/linux/shrinker.h +++ b/include/linux/shrinker.h @@ -66,6 +66,7 @@ struct shrinker { /* These are for internal use */ struct list_head list; + int id; /* objs pending delete, per node */ atomic_long_t *nr_deferred; }; diff --git a/mm/vmscan.c b/mm/vmscan.c index 8fcd9f8d7390..91b5120b924f 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -159,6 +159,56 @@ unsigned long vm_total_pages; static LIST_HEAD(shrinker_list); static DECLARE_RWSEM(shrinker_rwsem); +#if defined(CONFIG_MEMCG) && !defined(CONFIG_SLOB) +static DEFINE_IDA(bitmap_id_ida); +static DECLARE_RWSEM(bitmap_rwsem); +static int bitmap_id_start; + +static int alloc_shrinker_id(struct shrinker *shrinker) +{ + int id, ret; + + if (!(shrinker->flags & SHRINKER_MEMCG_AWARE)) + return 0; +retry: + ida_pre_get(&bitmap_id_ida, GFP_KERNEL); + down_write(&bitmap_rwsem); + ret = ida_get_new_above(&bitmap_id_ida, bitmap_id_start, &id); + if (!ret) { + shrinker->id = id; + bitmap_id_start = shrinker->id + 1; + } + up_write(&bitmap_rwsem); + if (ret == -EAGAIN) + goto retry; + + return ret; +} + +static void free_shrinker_id(struct shrinker *shrinker) +{ + int id = shrinker->id; + + if (!(shrinker->flags & SHRINKER_MEMCG_AWARE)) + return; + + down_write(&bitmap_rwsem); + ida_remove(&bitmap_id_ida, id); + if (bitmap_id_start > id) + bitmap_id_start = id; + up_write(&bitmap_rwsem); +} +#else /* CONFIG_MEMCG && !CONFIG_SLOB */ +static int alloc_shrinker_id(struct shrinker *shrinker) +{ + return 0; +} + +static void free_shrinker_id(struct shrinker *shrinker) +{ +} +#endif /* CONFIG_MEMCG && !CONFIG_SLOB */ + #ifdef CONFIG_MEMCG static bool global_reclaim(struct scan_control *sc) { @@ -269,10 +319,18 @@ int register_shrinker(struct shrinker *shrinker) if (!shrinker->nr_deferred) return -ENOMEM; + if (alloc_shrinker_id(shrinker)) + goto free_deferred; + down_write(&shrinker_rwsem); list_add_tail(&shrinker->list, &shrinker_list); up_write(&shrinker_rwsem); return 0; + +free_deferred: + kfree(shrinker->nr_deferred); + shrinker->nr_deferred = NULL; + return -ENOMEM; } EXPORT_SYMBOL(register_shrinker); @@ -286,6 +344,7 @@ void unregister_shrinker(struct shrinker *shrinker) down_write(&shrinker_rwsem); list_del(&shrinker->list); up_write(&shrinker_rwsem); + free_shrinker_id(shrinker); kfree(shrinker->nr_deferred); shrinker->nr_deferred = NULL; }