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