1 /*-
2 * Copyright (c) 2020 Emmanuel Vadot <manu@FreeBSD.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/kernel.h>
29 #include <sys/queue.h>
30 #include <sys/eventhandler.h>
31 #include <sys/sx.h>
32
33 #include <linux/compat.h>
34 #include <linux/shrinker.h>
35
36 TAILQ_HEAD(, shrinker) lkpi_shrinkers = TAILQ_HEAD_INITIALIZER(lkpi_shrinkers);
37 static struct sx sx_shrinker;
38
39 int
linuxkpi_register_shrinker(struct shrinker * s)40 linuxkpi_register_shrinker(struct shrinker *s)
41 {
42
43 KASSERT(s != NULL, ("NULL shrinker"));
44 KASSERT(s->count_objects != NULL, ("NULL shrinker"));
45 KASSERT(s->scan_objects != NULL, ("NULL shrinker"));
46 sx_xlock(&sx_shrinker);
47 TAILQ_INSERT_TAIL(&lkpi_shrinkers, s, next);
48 sx_xunlock(&sx_shrinker);
49 return (0);
50 }
51
52 void
linuxkpi_unregister_shrinker(struct shrinker * s)53 linuxkpi_unregister_shrinker(struct shrinker *s)
54 {
55
56 sx_xlock(&sx_shrinker);
57 TAILQ_REMOVE(&lkpi_shrinkers, s, next);
58 sx_xunlock(&sx_shrinker);
59 }
60
61 void
linuxkpi_synchronize_shrinkers(void)62 linuxkpi_synchronize_shrinkers(void)
63 {
64
65 sx_xlock(&sx_shrinker);
66 sx_xunlock(&sx_shrinker);
67 }
68
69 #define SHRINKER_BATCH 512
70
71 static void
shrinker_shrink(struct shrinker * s)72 shrinker_shrink(struct shrinker *s)
73 {
74 struct shrink_control sc;
75 unsigned long can_free;
76 unsigned long batch;
77 unsigned long scanned = 0;
78 unsigned long ret;
79
80 can_free = s->count_objects(s, &sc);
81 if (can_free <= 0)
82 return;
83
84 batch = s->batch ? s->batch : SHRINKER_BATCH;
85 while (scanned <= can_free) {
86 sc.nr_to_scan = batch;
87 ret = s->scan_objects(s, &sc);
88 if (ret == SHRINK_STOP)
89 break;
90 scanned += batch;
91 }
92 }
93
94 static void
linuxkpi_vm_lowmem(void * arg __unused)95 linuxkpi_vm_lowmem(void *arg __unused)
96 {
97 struct shrinker *s;
98
99 sx_xlock(&sx_shrinker);
100 TAILQ_FOREACH(s, &lkpi_shrinkers, next) {
101 shrinker_shrink(s);
102 }
103 sx_xunlock(&sx_shrinker);
104 }
105
106 static eventhandler_tag lowmem_tag;
107
108 static void
linuxkpi_sysinit_shrinker(void * arg __unused)109 linuxkpi_sysinit_shrinker(void *arg __unused)
110 {
111
112 sx_init(&sx_shrinker, "lkpi-shrinker");
113 lowmem_tag = EVENTHANDLER_REGISTER(vm_lowmem, linuxkpi_vm_lowmem,
114 NULL, EVENTHANDLER_PRI_FIRST);
115 }
116
117 static void
linuxkpi_sysuninit_shrinker(void * arg __unused)118 linuxkpi_sysuninit_shrinker(void *arg __unused)
119 {
120
121 sx_destroy(&sx_shrinker);
122 EVENTHANDLER_DEREGISTER(vm_lowmem, lowmem_tag);
123 }
124
125 SYSINIT(linuxkpi_shrinker, SI_SUB_DRIVERS, SI_ORDER_ANY,
126 linuxkpi_sysinit_shrinker, NULL);
127 SYSUNINIT(linuxkpi_shrinker, SI_SUB_DRIVERS, SI_ORDER_ANY,
128 linuxkpi_sysuninit_shrinker, NULL);
129