]>
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. | |
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 | ||
df870a69 | 26 | #include <sys/kmem.h> |
bf0c60c0 BB |
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 | SPL_SHRINKER_CALLBACK_FWD_DECLARE(splat_linux_shrinker_fn); | |
90 | SPL_SHRINKER_DECLARE(splat_linux_shrinker, splat_linux_shrinker_fn, 1); | |
91 | static unsigned long splat_linux_shrinker_size = 0; | |
92 | static struct file *splat_linux_shrinker_file = NULL; | |
93 | ||
94 | static int | |
95 | __splat_linux_shrinker_fn(struct shrinker *shrink, struct shrink_control *sc) | |
96 | { | |
97 | static int failsafe = 0; | |
98 | ||
99 | if (sc->nr_to_scan) { | |
100 | splat_linux_shrinker_size = splat_linux_shrinker_size - | |
101 | MIN(sc->nr_to_scan, splat_linux_shrinker_size); | |
102 | ||
103 | splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST3_NAME, | |
104 | "Reclaimed %lu objects, size now %lu\n", | |
105 | sc->nr_to_scan, splat_linux_shrinker_size); | |
106 | } else { | |
107 | splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST3_NAME, | |
108 | "Cache size is %lu\n", splat_linux_shrinker_size); | |
109 | } | |
110 | ||
111 | /* Far more calls than expected abort drop_slab as a failsafe */ | |
112 | if ((++failsafe % 1000) == 0) { | |
113 | splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST3_NAME, | |
114 | "Far more calls than expected (%d), size now %lu\n", | |
115 | failsafe, splat_linux_shrinker_size); | |
116 | return -1; | |
117 | } | |
118 | ||
119 | return (int)splat_linux_shrinker_size; | |
120 | } | |
121 | ||
122 | SPL_SHRINKER_CALLBACK_WRAPPER(splat_linux_shrinker_fn); | |
123 | ||
124 | #define DROP_SLAB_CMD \ | |
125 | "exec 0</dev/null " \ | |
126 | " 1>/proc/sys/vm/drop_caches " \ | |
127 | " 2>/dev/null; " \ | |
128 | "echo 2" | |
129 | ||
130 | static int | |
131 | splat_linux_drop_slab(struct file *file) | |
132 | { | |
133 | char *argv[] = { "/bin/sh", | |
134 | "-c", | |
135 | DROP_SLAB_CMD, | |
136 | NULL }; | |
137 | char *envp[] = { "HOME=/", | |
138 | "TERM=linux", | |
139 | "PATH=/sbin:/usr/sbin:/bin:/usr/bin", | |
140 | NULL }; | |
141 | int rc; | |
142 | ||
143 | rc = call_usermodehelper(argv[0], argv, envp, 1); | |
144 | if (rc) | |
145 | splat_vprint(file, SPLAT_LINUX_TEST3_NAME, | |
146 | "Failed user helper '%s %s %s', rc = %d\n", | |
147 | argv[0], argv[1], argv[2], rc); | |
148 | ||
149 | return rc; | |
150 | } | |
151 | ||
152 | /* | |
153 | * Verify correct shrinker functionality by registering a shrinker | |
154 | * with the required compatibility macros. We then use a simulated | |
155 | * cache and force the systems caches to be dropped. The shrinker | |
156 | * should be repeatedly called until it reports that the cache is | |
157 | * empty. It is then cleanly unregistered and correct behavior is | |
158 | * verified. There are now four slightly different supported shrinker | |
159 | * API and this test ensures the compatibility code is correct. | |
160 | */ | |
161 | static int | |
162 | splat_linux_test3(struct file *file, void *arg) | |
163 | { | |
164 | int rc = -EINVAL; | |
165 | ||
166 | /* | |
167 | * Globals used by the shrinker, it is not safe to run this | |
168 | * test concurrently this is a safe assumption for SPLAT tests. | |
169 | * Regardless we do some minimal checking a bail if concurrent | |
170 | * use is detected. | |
171 | */ | |
172 | if (splat_linux_shrinker_size || splat_linux_shrinker_file) { | |
173 | splat_vprint(file, SPLAT_LINUX_TEST3_NAME, | |
174 | "Failed due to concurrent shrinker test, rc = %d\n", rc); | |
175 | return (rc); | |
176 | } | |
177 | ||
178 | splat_linux_shrinker_size = 1024; | |
179 | splat_linux_shrinker_file = file; | |
180 | ||
181 | spl_register_shrinker(&splat_linux_shrinker); | |
182 | rc = splat_linux_drop_slab(file); | |
183 | if (rc) | |
184 | goto out; | |
185 | ||
186 | if (splat_linux_shrinker_size != 0) { | |
187 | splat_vprint(file, SPLAT_LINUX_TEST3_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 | SPLAT_TEST_INIT(sub, SPLAT_LINUX_TEST2_NAME, SPLAT_LINUX_TEST2_DESC, | |
221 | SPLAT_LINUX_TEST2_ID, splat_linux_test2); | |
222 | SPLAT_TEST_INIT(sub, SPLAT_LINUX_TEST3_NAME, SPLAT_LINUX_TEST3_DESC, | |
223 | SPLAT_LINUX_TEST3_ID, splat_linux_test3); | |
224 | ||
225 | return sub; | |
226 | } | |
227 | ||
228 | void | |
229 | splat_linux_fini(splat_subsystem_t *sub) | |
230 | { | |
231 | ASSERT(sub); | |
232 | SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST3_ID); | |
233 | SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST2_ID); | |
234 | SPLAT_TEST_FINI(sub, SPLAT_LINUX_TEST1_ID); | |
235 | ||
236 | kfree(sub); | |
237 | } | |
238 | ||
239 | int | |
240 | splat_linux_id(void) { | |
241 | return SPLAT_SUBSYSTEM_LINUX; | |
242 | } |