X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=linux%2Fshrinker.c;h=ca34ebc7e02f09ef46f0c168b57cc5ade4ac142b;hb=5fe6f426350503aa6c0823e7e6bf81fd7f138d71;hp=13f0c4b979c968e89337a1d84cf212c96ec2e77a;hpb=8d6138baac3b4fcd715c34cf325ae11b01a4ca67;p=bcachefs-tools-debian diff --git a/linux/shrinker.c b/linux/shrinker.c index 13f0c4b..ca34ebc 100644 --- a/linux/shrinker.c +++ b/linux/shrinker.c @@ -1,7 +1,10 @@ #include +#include +#include #include +#include #include #include @@ -10,59 +13,27 @@ static LIST_HEAD(shrinker_list); static DEFINE_MUTEX(shrinker_lock); -int register_shrinker(struct shrinker *shrinker, const char *fmt, ...) +void shrinker_free(struct shrinker *s) { - mutex_lock(&shrinker_lock); - list_add_tail(&shrinker->list, &shrinker_list); - mutex_unlock(&shrinker_lock); - return 0; -} - -void unregister_shrinker(struct shrinker *shrinker) -{ - mutex_lock(&shrinker_lock); - list_del(&shrinker->list); - mutex_unlock(&shrinker_lock); + if (s->list.next) { + mutex_lock(&shrinker_lock); + list_del(&s->list); + mutex_unlock(&shrinker_lock); + } + free(s); } -struct meminfo { - u64 total; - u64 available; -}; - -static u64 parse_meminfo_line(const char *line) +struct shrinker *shrinker_alloc(unsigned int flags, const char *fmt, ...) { - u64 v; - - if (sscanf(line, " %llu kB", &v) < 1) - die("sscanf error"); - return v << 10; + return calloc(sizeof(struct shrinker), 1); } -static struct meminfo read_meminfo(void) +int shrinker_register(struct shrinker *shrinker) { - struct meminfo ret = { 0 }; - size_t len, n = 0; - char *line = NULL; - const char *v; - FILE *f; - - f = fopen("/proc/meminfo", "r"); - if (!f) - return ret; - - while ((len = getline(&line, &n, f)) != -1) { - if ((v = strcmp_prefix(line, "MemTotal:"))) - ret.total = parse_meminfo_line(v); - - if ((v = strcmp_prefix(line, "MemAvailable:"))) - ret.available = parse_meminfo_line(v); - } - - fclose(f); - free(line); - - return ret; + mutex_lock(&shrinker_lock); + list_add_tail(&shrinker->list, &shrinker_list); + mutex_unlock(&shrinker_lock); + return 0; } static void run_shrinkers_allocation_failed(gfp_t gfp_mask) @@ -85,9 +56,12 @@ static void run_shrinkers_allocation_failed(gfp_t gfp_mask) void run_shrinkers(gfp_t gfp_mask, bool allocation_failed) { struct shrinker *shrinker; - struct meminfo info; + struct sysinfo info; s64 want_shrink; + if (!(gfp_mask & GFP_KERNEL)) + return; + /* Fast out if there are no shrinkers to run. */ if (list_empty(&shrinker_list)) return; @@ -97,19 +71,13 @@ void run_shrinkers(gfp_t gfp_mask, bool allocation_failed) return; } - info = read_meminfo(); - - if (info.total && info.available) { - want_shrink = (info.total >> 2) - info.available; - - if (want_shrink <= 0) - return; - } else { - /* If we weren't able to read /proc/meminfo, we must be pretty - * low: */ + si_meminfo(&info); - want_shrink = 8 << 20; - } + /* Aim for 6% of physical RAM free without anything in swap */ + want_shrink = (info.totalram >> 4) - info.freeram + + info.totalswap - info.freeswap; + if (want_shrink <= 0) + return; mutex_lock(&shrinker_lock); list_for_each_entry(shrinker, &shrinker_list, list) { @@ -122,3 +90,51 @@ void run_shrinkers(gfp_t gfp_mask, bool allocation_failed) } mutex_unlock(&shrinker_lock); } + +static int shrinker_thread(void *arg) +{ + while (!kthread_should_stop()) { + struct timespec to; + int v; + + clock_gettime(CLOCK_MONOTONIC, &to); + to.tv_sec += 1; + __set_current_state(TASK_INTERRUPTIBLE); + errno = 0; + while ((v = READ_ONCE(current->state)) != TASK_RUNNING && + errno != ETIMEDOUT) + futex(¤t->state, FUTEX_WAIT_BITSET|FUTEX_PRIVATE_FLAG, + v, &to, NULL, (uint32_t)~0); + if (kthread_should_stop()) + break; + if (v != TASK_RUNNING) + __set_current_state(TASK_RUNNING); + run_shrinkers(GFP_KERNEL, false); + } + + return 0; +} + +struct task_struct *shrinker_task; + +__attribute__((constructor(103))) +static void shrinker_thread_init(void) +{ + shrinker_task = kthread_run(shrinker_thread, NULL, "shrinkers"); + BUG_ON(IS_ERR(shrinker_task)); +} + +#if 0 +/* + * We seem to be hitting a rare segfault when shutting down the shrinker thread. + * Disabling this is going to cause some harmless warnings about memory leaks: + */ +__attribute__((destructor(103))) +static void shrinker_thread_exit(void) +{ + int ret = kthread_stop(shrinker_task); + BUG_ON(ret); + + shrinker_task = NULL; +} +#endif