diff --git a/sys/compat/linuxkpi/common/include/linux/shrinker.h b/sys/compat/linuxkpi/common/include/linux/shrinker.h new file mode 100644 --- /dev/null +++ b/sys/compat/linuxkpi/common/include/linux/shrinker.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2020 Emmanuel Vadot + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __LINUX_SHRINKER_H__ +#define __LINUX_SHRINKER_H__ + +#include + +struct shrink_control { + unsigned long nr_to_scan; + unsigned long nr_scanned; +}; + +struct shrinker { + unsigned long (*count_objects)(struct shrinker *, struct shrink_control *); + unsigned long (*scan_objects)(struct shrinker *, struct shrink_control *); + int seeks; + long batch; + TAILQ_ENTRY(shrinker) next; +}; + +#define SHRINK_STOP (~0UL) + +#define DEFAULT_SEEKS 2 + +int linuxkpi_register_shrinker(struct shrinker *s); +void linuxkpi_unregister_shrinker(struct shrinker *s); + +#define register_shrinker(s) linuxkpi_register_shrinker(s) +#define unregister_shrinker(s) linuxkpi_unregister_shrinker(s) + +#endif /* __LINUX_SHRINKER_H__ */ diff --git a/sys/compat/linuxkpi/common/src/linux_shrinker.c b/sys/compat/linuxkpi/common/src/linux_shrinker.c new file mode 100644 --- /dev/null +++ b/sys/compat/linuxkpi/common/src/linux_shrinker.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 2020 Emmanuel Vadot + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include + +TAILQ_HEAD(, shrinker) lkpi_shrinkers = TAILQ_HEAD_INITIALIZER(lkpi_shrinkers); +static struct mtx mtx_shrinker; + +int +linuxkpi_register_shrinker(struct shrinker *s) +{ + + KASSERT(s != NULL, ("NULL shrinker")); + KASSERT(s->count_objects != NULL, ("NULL shrinker")); + KASSERT(s->scan_objects != NULL, ("NULL shrinker")); + mtx_lock(&mtx_shrinker); + TAILQ_INSERT_TAIL(&lkpi_shrinkers, s, next); + mtx_unlock(&mtx_shrinker); + return (0); +} + +void +linuxkpi_unregister_shrinker(struct shrinker *s) +{ + + mtx_lock(&mtx_shrinker); + TAILQ_REMOVE(&lkpi_shrinkers, s, next); + mtx_unlock(&mtx_shrinker); +} + +#define SHRINKER_BATCH 512 + +static void +shrinker_shrink(struct shrinker *s) +{ + struct shrink_control sc; + unsigned long can_free; + unsigned long batch; + unsigned long freeed = 0; + unsigned long ret; + + can_free = s->count_objects(s, &sc); + if (can_free <= 0) + return; + + batch = s->batch ? s->batch : SHRINKER_BATCH; + while (freeed <= can_free) { + sc.nr_to_scan = batch; + ret = s->scan_objects(s, &sc); + if (ret == SHRINK_STOP) + break; + freeed += ret; + } +} + +static void +linuxkpi_vm_lowmem(void *arg __unused) +{ + struct shrinker *s; + + mtx_lock(&mtx_shrinker); + TAILQ_FOREACH(s, &lkpi_shrinkers, next) { + shrinker_shrink(s); + } + mtx_unlock(&mtx_shrinker); +} + +static eventhandler_tag lowmem_tag; + +static void +linuxkpi_sysinit_shrinker(void *arg __unused) +{ + + mtx_init(&mtx_shrinker, "lkpi-shrinker", NULL, MTX_DEF); + lowmem_tag = EVENTHANDLER_REGISTER(vm_lowmem, linuxkpi_vm_lowmem, + NULL, EVENTHANDLER_PRI_FIRST); +} + +static void +linuxkpi_sysuninit_shrinker(void *arg __unused) +{ + + mtx_destroy(&mtx_shrinker); + EVENTHANDLER_DEREGISTER(vm_lowmem, lowmem_tag); +} + +SYSINIT(linuxkpi_shrinker, SI_SUB_DRIVERS, SI_ORDER_ANY, + linuxkpi_sysinit_shrinker, NULL); +SYSUNINIT(linuxkpi_shrinker, SI_SUB_DRIVERS, SI_ORDER_ANY, + linuxkpi_sysuninit_shrinker, NULL); diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -4576,6 +4576,8 @@ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_shmemfs.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" +compat/linuxkpi/common/src/linux_shrinker.c optional compat_linuxkpi \ + compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_slab.c optional compat_linuxkpi \ compile-with "${LINUXKPI_C}" compat/linuxkpi/common/src/linux_usb.c optional compat_linuxkpi usb \ diff --git a/sys/modules/linuxkpi/Makefile b/sys/modules/linuxkpi/Makefile --- a/sys/modules/linuxkpi/Makefile +++ b/sys/modules/linuxkpi/Makefile @@ -17,6 +17,7 @@ linux_seq_file.c \ linux_schedule.c \ linux_shmemfs.c \ + linux_shrinker.c \ linux_slab.c \ linux_tasklet.c \ linux_usb.c \