]> git.proxmox.com Git - mirror_spl.git/blob - module/splat/splat-linux.c
Linux 3.12 compat: shrinker semantics
[mirror_spl.git] / module / splat / splat-linux.c
1 /*****************************************************************************\
2 * Copyright (C) 2011 Lawrence Livermore National Security, LLC.
3 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
4 * Written by Brian Behlendorf <behlendorf1@llnl.gov>.
5 * UCRL-CODE-235197
6 *
7 * This file is part of the SPL, Solaris Porting Layer.
8 * For details, see <http://zfsonlinux.org/>.
9 *
10 * The SPL is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 * The SPL is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 * for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
22 *****************************************************************************
23 * Solaris Porting LAyer Tests (SPLAT) Kernel Compatibility Tests.
24 \*****************************************************************************/
25
26 #include <sys/kmem.h>
27 #include "splat-internal.h"
28
29 #define SPLAT_LINUX_NAME "linux"
30 #define SPLAT_LINUX_DESC "Kernel Compatibility Tests"
31
32 #define SPLAT_LINUX_TEST1_ID 0x1001
33 #define SPLAT_LINUX_TEST1_NAME "shrinker"
34 #define SPLAT_LINUX_TEST1_DESC "Shrinker test"
35
36 /*
37 * Wait queue used to eliminate race between dropping of slab
38 * and execution of the shrinker callback
39 */
40 DECLARE_WAIT_QUEUE_HEAD(shrinker_wait);
41
42 SPL_SHRINKER_CALLBACK_FWD_DECLARE(splat_linux_shrinker_fn);
43 SPL_SHRINKER_DECLARE(splat_linux_shrinker, splat_linux_shrinker_fn, 1);
44 static unsigned long splat_linux_shrinker_size = 0;
45 static struct file *splat_linux_shrinker_file = NULL;
46
47 static spl_shrinker_t
48 __splat_linux_shrinker_fn(struct shrinker *shrink, struct shrink_control *sc)
49 {
50 static int failsafe = 0;
51 static unsigned long last_splat_linux_shrinker_size = 0;
52 unsigned long size;
53 spl_shrinker_t count;
54
55 /*
56 * shrinker_size can only decrease or stay the same between callbacks
57 * in the same run, so Reset failsafe whenever shrinker increases
58 * as this indicates a new run.
59 */
60 if (last_splat_linux_shrinker_size < splat_linux_shrinker_size)
61 failsafe = 0;
62
63 last_splat_linux_shrinker_size = splat_linux_shrinker_size;
64
65 if (sc->nr_to_scan) {
66 size = MIN(sc->nr_to_scan, splat_linux_shrinker_size);
67 splat_linux_shrinker_size -= size;
68
69 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
70 "Reclaimed %lu objects, size now %lu\n",
71 size, splat_linux_shrinker_size);
72
73 #ifdef HAVE_SPLIT_SHRINKER_CALLBACK
74 count = size;
75 #else
76 count = splat_linux_shrinker_size;
77 #endif /* HAVE_SPLIT_SHRINKER_CALLBACK */
78
79 } else {
80 count = splat_linux_shrinker_size;
81 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
82 "Cache size is %lu\n", splat_linux_shrinker_size);
83 }
84
85 /* Far more calls than expected abort drop_slab as a failsafe */
86 if (failsafe > 100) {
87 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
88 "Far more calls than expected (%d), size now %lu\n",
89 failsafe, splat_linux_shrinker_size);
90 return (SHRINK_STOP);
91 } else {
92 /*
93 * We only increment failsafe if it doesn't trigger. This
94 * makes any failsafe failure persistent until the next test.
95 */
96 failsafe++;
97 }
98
99 /* Shrinker has run, so signal back to test. */
100 wake_up(&shrinker_wait);
101
102 return (count);
103 }
104
105 SPL_SHRINKER_CALLBACK_WRAPPER(splat_linux_shrinker_fn);
106
107 #define DROP_SLAB_CMD \
108 "exec 0</dev/null " \
109 " 1>/proc/sys/vm/drop_caches " \
110 " 2>/dev/null; " \
111 "echo 2"
112
113 static int
114 splat_linux_drop_slab(struct file *file)
115 {
116 char *argv[] = { "/bin/sh",
117 "-c",
118 DROP_SLAB_CMD,
119 NULL };
120 char *envp[] = { "HOME=/",
121 "TERM=linux",
122 "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
123 NULL };
124 int rc;
125
126 rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
127 if (rc)
128 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
129 "Failed user helper '%s %s %s', rc = %d\n",
130 argv[0], argv[1], argv[2], rc);
131
132 return rc;
133 }
134
135 /*
136 * Verify correct shrinker functionality by registering a shrinker
137 * with the required compatibility macros. We then use a simulated
138 * cache and force the systems caches to be dropped. The shrinker
139 * should be repeatedly called until it reports that the cache is
140 * empty. It is then cleanly unregistered and correct behavior is
141 * verified. There are now four slightly different supported shrinker
142 * API and this test ensures the compatibility code is correct.
143 */
144 static int
145 splat_linux_test1(struct file *file, void *arg)
146 {
147 int rc = -EINVAL;
148
149 /*
150 * Globals used by the shrinker, it is not safe to run this
151 * test concurrently this is a safe assumption for SPLAT tests.
152 * Regardless we do some minimal checking a bail if concurrent
153 * use is detected.
154 */
155 if (splat_linux_shrinker_size || splat_linux_shrinker_file) {
156 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
157 "Failed due to concurrent shrinker test, rc = %d\n", rc);
158 return (rc);
159 }
160
161 splat_linux_shrinker_size = 1024;
162 splat_linux_shrinker_file = file;
163
164 spl_register_shrinker(&splat_linux_shrinker);
165 rc = splat_linux_drop_slab(file);
166 if (rc)
167 goto out;
168
169 /*
170 * By the time we get here, it is possible that the shrinker has not
171 * yet run. splat_linux_drop_slab sends a signal for it to run, but
172 * there is no guarantee of when it will actually run. We wait for it
173 * to run here, terminating when either the shrinker size is now 0 or
174 * we timeout after 1 second, which should be an eternity (error).
175 */
176 rc = wait_event_timeout(shrinker_wait, !splat_linux_shrinker_size, HZ);
177 if (!rc) {
178 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
179 "Failed cache shrinking timed out, size now %lu",
180 splat_linux_shrinker_size);
181 rc = -ETIMEDOUT;
182 } else {
183 rc = 0;
184 }
185
186 if (!rc && splat_linux_shrinker_size != 0) {
187 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
188 "Failed cache was not shrunk to 0, size now %lu",
189 splat_linux_shrinker_size);
190 rc = -EDOM;
191 }
192 out:
193 spl_unregister_shrinker(&splat_linux_shrinker);
194
195 splat_linux_shrinker_size = 0;
196 splat_linux_shrinker_file = NULL;
197
198 return rc;
199 }
200
201 splat_subsystem_t *
202 splat_linux_init(void)
203 {
204 splat_subsystem_t *sub;
205
206 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
207 if (sub == NULL)
208 return NULL;
209
210 memset(sub, 0, sizeof(*sub));
211 strncpy(sub->desc.name, SPLAT_LINUX_NAME, SPLAT_NAME_SIZE);
212 strncpy(sub->desc.desc, SPLAT_LINUX_DESC, SPLAT_DESC_SIZE);
213 INIT_LIST_HEAD(&sub->subsystem_list);
214 INIT_LIST_HEAD(&sub->test_list);
215 spin_lock_init(&sub->test_lock);
216 sub->desc.id = SPLAT_SUBSYSTEM_LINUX;
217
218 SPLAT_TEST_INIT(sub, SPLAT_LINUX_TEST1_NAME, SPLAT_LINUX_TEST1_DESC,
219 SPLAT_LINUX_TEST1_ID, splat_linux_test1);
220
221 return sub;
222 }
223
224 void
225 splat_linux_fini(splat_subsystem_t *sub)
226 {
227 ASSERT(sub);
228 SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST1_ID);
229
230 kfree(sub);
231 }
232
233 int
234 splat_linux_id(void) {
235 return SPLAT_SUBSYSTEM_LINUX;
236 }