]> git.proxmox.com Git - mirror_spl-debian.git/blob - module/splat/splat-linux.c
New upstream version 0.7.2
[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://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 <linux/mm_compat.h>
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
34 #define SPLAT_LINUX_TEST1_NAME "shrinker"
35 #define SPLAT_LINUX_TEST1_DESC "Shrinker test"
36
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
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
48 static spl_shrinker_t
49 __splat_linux_shrinker_fn(struct shrinker *shrink, struct shrink_control *sc)
50 {
51 static int failsafe = 0;
52 static unsigned long last_splat_linux_shrinker_size = 0;
53 unsigned long size;
54 spl_shrinker_t count;
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;
65
66 if (sc->nr_to_scan) {
67 size = MIN(sc->nr_to_scan, splat_linux_shrinker_size);
68 splat_linux_shrinker_size -= size;
69
70 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
71 "Reclaimed %lu objects, size now %lu\n",
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
80 } else {
81 count = splat_linux_shrinker_size;
82 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
83 "Cache size is %lu\n", splat_linux_shrinker_size);
84 }
85
86 /* Far more calls than expected abort drop_slab as a failsafe */
87 if (failsafe > 100) {
88 splat_vprint(splat_linux_shrinker_file, SPLAT_LINUX_TEST1_NAME,
89 "Far more calls than expected (%d), size now %lu\n",
90 failsafe, splat_linux_shrinker_size);
91 return (SHRINK_STOP);
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++;
98 }
99
100 /* Shrinker has run, so signal back to test. */
101 wake_up(&shrinker_wait);
102
103 return (count);
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
127 rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
128 if (rc)
129 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
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
146 splat_linux_test1(struct file *file, void *arg)
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) {
157 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
158 "Failed due to concurrent shrinker test, rc = %d\n", rc);
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
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) {
179 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
180 "Failed cache shrinking timed out, size now %lu",
181 splat_linux_shrinker_size);
182 rc = -ETIMEDOUT;
183 } else {
184 rc = 0;
185 }
186
187 if (!rc && splat_linux_shrinker_size != 0) {
188 splat_vprint(file, SPLAT_LINUX_TEST1_NAME,
189 "Failed cache was not shrunk to 0, size now %lu",
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);
221
222 return sub;
223 }
224
225 void
226 splat_linux_fini(splat_subsystem_t *sub)
227 {
228 ASSERT(sub);
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 }