]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-linux.c
0a1808f61102b2a81284a7996385638ac3e8f928
[mirror_spl-debian.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://github.com/behlendorf/spl/>.
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 "shrink_dcache"
34 #define SPLAT_LINUX_TEST1_DESC "Shrink dcache test"
35
36 #define SPLAT_LINUX_TEST2_ID 0x1002
37 #define SPLAT_LINUX_TEST2_NAME "shrink_icache"
38 #define SPLAT_LINUX_TEST2_DESC "Shrink icache test"
39
40 #define SPLAT_LINUX_TEST3_ID 0x1003
41 #define SPLAT_LINUX_TEST3_NAME "shrinker"
42 #define SPLAT_LINUX_TEST3_DESC "Shrinker test"
43
44
45 /*
46 * Attempt to shrink the dcache memory. This is simply a functional
47 * to ensure we can correctly call the shrinker. We don't check that
48 * the cache actually decreased because we have no control over what
49 * else may be running on the system. This avoid false positives.
50 */
51 static int
52 splat_linux_test1(struct file *file, void *arg)
53 {
54 int remain_before;
55 int remain_after;
56
57 remain_before = shrink_dcache_memory(0, GFP_KERNEL);
58 remain_after = shrink_dcache_memory(KMC_REAP_CHUNK, GFP_KERNEL);
59
60 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
61 "Shrink dcache memory, remain %d -> %d\n",
62 remain_before, remain_after);
63
64 return 0;
65 }
66
67 /*
68 * Attempt to shrink the icache memory. This is simply a functional
69 * to ensure we can correctly call the shrinker. We don't check that
70 * the cache actually decreased because we have no control over what
71 * else may be running on the system. This avoid false positives.
72 */
73 static int
74 splat_linux_test2(struct file *file, void *arg)
75 {
76 int remain_before;
77 int remain_after;
78
79 remain_before = shrink_icache_memory(0, GFP_KERNEL);
80 remain_after = shrink_icache_memory(KMC_REAP_CHUNK, GFP_KERNEL);
81
82 splat_vprint(file, SPLAT_LINUX_TEST2_NAME,
83 "Shrink icache memory, remain %d -> %d\n",
84 remain_before, remain_after);
85
86 return 0;
87 }
88
89 /*
90 * Wait queue used to eliminate race between dropping of slab
91 * and execution of the shrinker callback
92 */
93 DECLARE_WAIT_QUEUE_HEAD(shrinker_wait);
94
95 SPL_SHRINKER_CALLBACK_FWD_DECLARE(splat_linux_shrinker_fn);
96 SPL_SHRINKER_DECLARE(splat_linux_shrinker, splat_linux_shrinker_fn, 1);
97 static unsigned long splat_linux_shrinker_size = 0;
98 static struct file *splat_linux_shrinker_file = NULL;
99
100 static int
101 __splat_linux_shrinker_fn(struct shrinker *shrink, struct shrink_control *sc)
102 {
103 static int failsafe = 0;
104 static unsigned long last_splat_linux_shrinker_size = 0;
105
106 /*
107 * shrinker_size can only decrease or stay the same between callbacks
108 * in the same run, so Reset failsafe whenever shrinker increases
109 * as this indicates a new run.
110 */
111 if (last_splat_linux_shrinker_size < splat_linux_shrinker_size)
112 failsafe = 0;
113
114 last_splat_linux_shrinker_size = splat_linux_shrinker_size;
115
116 if (sc->nr_to_scan) {
117 splat_linux_shrinker_size = splat_linux_shrinker_size -
118 MIN(sc->nr_to_scan, splat_linux_shrinker_size);
119
120 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST3_NAME,
121 "Reclaimed %lu objects, size now %lu\n",
122 sc->nr_to_scan, splat_linux_shrinker_size);
123 } else {
124 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST3_NAME,
125 "Cache size is %lu\n", splat_linux_shrinker_size);
126 }
127
128 /* Far more calls than expected abort drop_slab as a failsafe */
129 if (failsafe > 100) {
130 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST3_NAME,
131 "Far more calls than expected (%d), size now %lu\n",
132 failsafe, splat_linux_shrinker_size);
133 return -1;
134 } else {
135 /*
136 * We only increment failsafe if it doesn't trigger. This
137 * makes any failsafe failure persistent until the next test.
138 */
139 failsafe++;
140 }
141
142 /* Shrinker has run, so signal back to test. */
143 wake_up(&shrinker_wait);
144
145 return (int)splat_linux_shrinker_size;
146 }
147
148 SPL_SHRINKER_CALLBACK_WRAPPER(splat_linux_shrinker_fn);
149
150 #define DROP_SLAB_CMD \
151 "exec 0</dev/null " \
152 " 1>/proc/sys/vm/drop_caches " \
153 " 2>/dev/null; " \
154 "echo 2"
155
156 static int
157 splat_linux_drop_slab(struct file *file)
158 {
159 char *argv[] = { "/bin/sh",
160 "-c",
161 DROP_SLAB_CMD,
162 NULL };
163 char *envp[] = { "HOME=/",
164 "TERM=linux",
165 "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
166 NULL };
167 int rc;
168
169 rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
170 if (rc)
171 splat_vprint(file, SPLAT_LINUX_TEST3_NAME,
172 "Failed user helper '%s %s %s', rc = %d\n",
173 argv[0], argv[1], argv[2], rc);
174
175 return rc;
176 }
177
178 /*
179 * Verify correct shrinker functionality by registering a shrinker
180 * with the required compatibility macros. We then use a simulated
181 * cache and force the systems caches to be dropped. The shrinker
182 * should be repeatedly called until it reports that the cache is
183 * empty. It is then cleanly unregistered and correct behavior is
184 * verified. There are now four slightly different supported shrinker
185 * API and this test ensures the compatibility code is correct.
186 */
187 static int
188 splat_linux_test3(struct file *file, void *arg)
189 {
190 int rc = -EINVAL;
191
192 /*
193 * Globals used by the shrinker, it is not safe to run this
194 * test concurrently this is a safe assumption for SPLAT tests.
195 * Regardless we do some minimal checking a bail if concurrent
196 * use is detected.
197 */
198 if (splat_linux_shrinker_size || splat_linux_shrinker_file) {
199 splat_vprint(file, SPLAT_LINUX_TEST3_NAME,
200 "Failed due to concurrent shrinker test, rc = %d\n", rc);
201 return (rc);
202 }
203
204 splat_linux_shrinker_size = 1024;
205 splat_linux_shrinker_file = file;
206
207 spl_register_shrinker(&splat_linux_shrinker);
208 rc = splat_linux_drop_slab(file);
209 if (rc)
210 goto out;
211
212 /*
213 * By the time we get here, it is possible that the shrinker has not
214 * yet run. splat_linux_drop_slab sends a signal for it to run, but
215 * there is no guarantee of when it will actually run. We wait for it
216 * to run here, terminating when either the shrinker size is now 0 or
217 * we timeout after 1 second, which should be an eternity (error).
218 */
219 rc = wait_event_timeout(shrinker_wait, !splat_linux_shrinker_size, HZ);
220 if (!rc) {
221 splat_vprint(file, SPLAT_LINUX_TEST3_NAME,
222 "Failed cache shrinking timed out, size now %lu",
223 splat_linux_shrinker_size);
224 rc = -ETIMEDOUT;
225 } else {
226 rc = 0;
227 }
228
229 if (!rc && splat_linux_shrinker_size != 0) {
230 splat_vprint(file, SPLAT_LINUX_TEST3_NAME,
231 "Failed cache was not shrunk to 0, size now %lu",
232 splat_linux_shrinker_size);
233 rc = -EDOM;
234 }
235 out:
236 spl_unregister_shrinker(&splat_linux_shrinker);
237
238 splat_linux_shrinker_size = 0;
239 splat_linux_shrinker_file = NULL;
240
241 return rc;
242 }
243
244 splat_subsystem_t *
245 splat_linux_init(void)
246 {
247 splat_subsystem_t *sub;
248
249 sub = kmalloc(sizeof(*sub), GFP_KERNEL);
250 if (sub == NULL)
251 return NULL;
252
253 memset(sub, 0, sizeof(*sub));
254 strncpy(sub->desc.name, SPLAT_LINUX_NAME, SPLAT_NAME_SIZE);
255 strncpy(sub->desc.desc, SPLAT_LINUX_DESC, SPLAT_DESC_SIZE);
256 INIT_LIST_HEAD(&sub->subsystem_list);
257 INIT_LIST_HEAD(&sub->test_list);
258 spin_lock_init(&sub->test_lock);
259 sub->desc.id = SPLAT_SUBSYSTEM_LINUX;
260
261 SPLAT_TEST_INIT(sub, SPLAT_LINUX_TEST1_NAME, SPLAT_LINUX_TEST1_DESC,
262 SPLAT_LINUX_TEST1_ID, splat_linux_test1);
263 SPLAT_TEST_INIT(sub, SPLAT_LINUX_TEST2_NAME, SPLAT_LINUX_TEST2_DESC,
264 SPLAT_LINUX_TEST2_ID, splat_linux_test2);
265 SPLAT_TEST_INIT(sub, SPLAT_LINUX_TEST3_NAME, SPLAT_LINUX_TEST3_DESC,
266 SPLAT_LINUX_TEST3_ID, splat_linux_test3);
267
268 return sub;
269 }
270
271 void
272 splat_linux_fini(splat_subsystem_t *sub)
273 {
274 ASSERT(sub);
275 SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST3_ID);
276 SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST2_ID);
277 SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST1_ID);
278
279 kfree(sub);
280 }
281
282 int
283 splat_linux_id(void) {
284 return SPLAT_SUBSYSTEM_LINUX;
285 }