]>
Commit | Line | Data |
---|---|---|
715f6251 | 1 | /* |
2 | * This file is part of the SPL: Solaris Porting Layer. | |
3 | * | |
4 | * Copyright (c) 2008 Lawrence Livermore National Security, LLC. | |
5 | * Produced at Lawrence Livermore National Laboratory | |
6 | * Written by: | |
7 | * Brian Behlendorf <behlendorf1@llnl.gov>, | |
8 | * Herb Wartens <wartens2@llnl.gov>, | |
9 | * Jim Garlick <garlick@llnl.gov> | |
10 | * UCRL-CODE-235197 | |
11 | * | |
12 | * This is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | * This is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
20 | * for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License along | |
23 | * with this program; if not, write to the Free Software Foundation, Inc., | |
24 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
25 | */ | |
26 | ||
7c50328b | 27 | #include "splat-internal.h" |
f1ca4da6 | 28 | |
7c50328b | 29 | #define SPLAT_SUBSYSTEM_KMEM 0x0100 |
30 | #define SPLAT_KMEM_NAME "kmem" | |
31 | #define SPLAT_KMEM_DESC "Kernel Malloc/Slab Tests" | |
f1ca4da6 | 32 | |
7c50328b | 33 | #define SPLAT_KMEM_TEST1_ID 0x0101 |
34 | #define SPLAT_KMEM_TEST1_NAME "kmem_alloc" | |
35 | #define SPLAT_KMEM_TEST1_DESC "Memory allocation test (kmem_alloc)" | |
f1ca4da6 | 36 | |
7c50328b | 37 | #define SPLAT_KMEM_TEST2_ID 0x0102 |
38 | #define SPLAT_KMEM_TEST2_NAME "kmem_zalloc" | |
39 | #define SPLAT_KMEM_TEST2_DESC "Memory allocation test (kmem_zalloc)" | |
f1ca4da6 | 40 | |
7c50328b | 41 | #define SPLAT_KMEM_TEST3_ID 0x0103 |
2fb9b26a | 42 | #define SPLAT_KMEM_TEST3_NAME "vmem_alloc" |
43 | #define SPLAT_KMEM_TEST3_DESC "Memory allocation test (vmem_alloc)" | |
f1ca4da6 | 44 | |
7c50328b | 45 | #define SPLAT_KMEM_TEST4_ID 0x0104 |
2fb9b26a | 46 | #define SPLAT_KMEM_TEST4_NAME "vmem_zalloc" |
47 | #define SPLAT_KMEM_TEST4_DESC "Memory allocation test (vmem_zalloc)" | |
f1ca4da6 | 48 | |
79b31f36 | 49 | #define SPLAT_KMEM_TEST5_ID 0x0105 |
48e0606a | 50 | #define SPLAT_KMEM_TEST5_NAME "kmem_small" |
2fb9b26a | 51 | #define SPLAT_KMEM_TEST5_DESC "Slab ctor/dtor test (small)" |
52 | ||
53 | #define SPLAT_KMEM_TEST6_ID 0x0106 | |
48e0606a | 54 | #define SPLAT_KMEM_TEST6_NAME "kmem_large" |
2fb9b26a | 55 | #define SPLAT_KMEM_TEST6_DESC "Slab ctor/dtor test (large)" |
56 | ||
57 | #define SPLAT_KMEM_TEST7_ID 0x0107 | |
58 | #define SPLAT_KMEM_TEST7_NAME "kmem_reap" | |
59 | #define SPLAT_KMEM_TEST7_DESC "Slab reaping test" | |
79b31f36 | 60 | |
44b8f176 | 61 | #define SPLAT_KMEM_TEST8_ID 0x0108 |
62 | #define SPLAT_KMEM_TEST8_NAME "kmem_lock" | |
63 | #define SPLAT_KMEM_TEST8_DESC "Slab locking test" | |
64 | ||
48e0606a BB |
65 | #define SPLAT_KMEM_TEST9_ID 0x0109 |
66 | #define SPLAT_KMEM_TEST9_NAME "kmem_align" | |
67 | #define SPLAT_KMEM_TEST9_DESC "Slab alignment test" | |
68 | ||
7c50328b | 69 | #define SPLAT_KMEM_ALLOC_COUNT 10 |
79b31f36 | 70 | #define SPLAT_VMEM_ALLOC_COUNT 10 |
71 | ||
44b8f176 | 72 | |
f1ca4da6 | 73 | /* XXX - This test may fail under tight memory conditions */ |
74 | static int | |
7c50328b | 75 | splat_kmem_test1(struct file *file, void *arg) |
f1ca4da6 | 76 | { |
7c50328b | 77 | void *ptr[SPLAT_KMEM_ALLOC_COUNT]; |
f1ca4da6 | 78 | int size = PAGE_SIZE; |
79 | int i, count, rc = 0; | |
80 | ||
c19c06f3 | 81 | /* We are intentionally going to push kmem_alloc to its max |
82 | * allocation size, so suppress the console warnings for now */ | |
83 | kmem_set_warning(0); | |
84 | ||
79b31f36 | 85 | while ((!rc) && (size <= (PAGE_SIZE * 32))) { |
f1ca4da6 | 86 | count = 0; |
87 | ||
7c50328b | 88 | for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) { |
f1ca4da6 | 89 | ptr[i] = kmem_alloc(size, KM_SLEEP); |
90 | if (ptr[i]) | |
91 | count++; | |
92 | } | |
93 | ||
7c50328b | 94 | for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) |
f1ca4da6 | 95 | if (ptr[i]) |
96 | kmem_free(ptr[i], size); | |
97 | ||
7c50328b | 98 | splat_vprint(file, SPLAT_KMEM_TEST1_NAME, |
f1ca4da6 | 99 | "%d byte allocations, %d/%d successful\n", |
7c50328b | 100 | size, count, SPLAT_KMEM_ALLOC_COUNT); |
101 | if (count != SPLAT_KMEM_ALLOC_COUNT) | |
f1ca4da6 | 102 | rc = -ENOMEM; |
103 | ||
104 | size *= 2; | |
105 | } | |
106 | ||
c19c06f3 | 107 | kmem_set_warning(1); |
108 | ||
f1ca4da6 | 109 | return rc; |
110 | } | |
111 | ||
112 | static int | |
7c50328b | 113 | splat_kmem_test2(struct file *file, void *arg) |
f1ca4da6 | 114 | { |
7c50328b | 115 | void *ptr[SPLAT_KMEM_ALLOC_COUNT]; |
f1ca4da6 | 116 | int size = PAGE_SIZE; |
117 | int i, j, count, rc = 0; | |
118 | ||
c19c06f3 | 119 | /* We are intentionally going to push kmem_alloc to its max |
120 | * allocation size, so suppress the console warnings for now */ | |
121 | kmem_set_warning(0); | |
122 | ||
79b31f36 | 123 | while ((!rc) && (size <= (PAGE_SIZE * 32))) { |
f1ca4da6 | 124 | count = 0; |
125 | ||
7c50328b | 126 | for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) { |
f1ca4da6 | 127 | ptr[i] = kmem_zalloc(size, KM_SLEEP); |
128 | if (ptr[i]) | |
129 | count++; | |
130 | } | |
131 | ||
132 | /* Ensure buffer has been zero filled */ | |
7c50328b | 133 | for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) { |
f1ca4da6 | 134 | for (j = 0; j < size; j++) { |
135 | if (((char *)ptr[i])[j] != '\0') { | |
7c50328b | 136 | splat_vprint(file, SPLAT_KMEM_TEST2_NAME, |
f1ca4da6 | 137 | "%d-byte allocation was " |
138 | "not zeroed\n", size); | |
139 | rc = -EFAULT; | |
140 | } | |
141 | } | |
142 | } | |
143 | ||
7c50328b | 144 | for (i = 0; i < SPLAT_KMEM_ALLOC_COUNT; i++) |
f1ca4da6 | 145 | if (ptr[i]) |
146 | kmem_free(ptr[i], size); | |
147 | ||
7c50328b | 148 | splat_vprint(file, SPLAT_KMEM_TEST2_NAME, |
f1ca4da6 | 149 | "%d byte allocations, %d/%d successful\n", |
7c50328b | 150 | size, count, SPLAT_KMEM_ALLOC_COUNT); |
151 | if (count != SPLAT_KMEM_ALLOC_COUNT) | |
f1ca4da6 | 152 | rc = -ENOMEM; |
153 | ||
154 | size *= 2; | |
155 | } | |
156 | ||
c19c06f3 | 157 | kmem_set_warning(1); |
158 | ||
f1ca4da6 | 159 | return rc; |
160 | } | |
161 | ||
2fb9b26a | 162 | static int |
163 | splat_kmem_test3(struct file *file, void *arg) | |
164 | { | |
165 | void *ptr[SPLAT_VMEM_ALLOC_COUNT]; | |
166 | int size = PAGE_SIZE; | |
167 | int i, count, rc = 0; | |
168 | ||
169 | while ((!rc) && (size <= (PAGE_SIZE * 1024))) { | |
170 | count = 0; | |
171 | ||
172 | for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) { | |
173 | ptr[i] = vmem_alloc(size, KM_SLEEP); | |
174 | if (ptr[i]) | |
175 | count++; | |
176 | } | |
177 | ||
178 | for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) | |
179 | if (ptr[i]) | |
180 | vmem_free(ptr[i], size); | |
181 | ||
182 | splat_vprint(file, SPLAT_KMEM_TEST3_NAME, | |
183 | "%d byte allocations, %d/%d successful\n", | |
184 | size, count, SPLAT_VMEM_ALLOC_COUNT); | |
185 | if (count != SPLAT_VMEM_ALLOC_COUNT) | |
186 | rc = -ENOMEM; | |
187 | ||
188 | size *= 2; | |
189 | } | |
190 | ||
191 | return rc; | |
192 | } | |
193 | ||
194 | static int | |
195 | splat_kmem_test4(struct file *file, void *arg) | |
196 | { | |
197 | void *ptr[SPLAT_VMEM_ALLOC_COUNT]; | |
198 | int size = PAGE_SIZE; | |
199 | int i, j, count, rc = 0; | |
200 | ||
201 | while ((!rc) && (size <= (PAGE_SIZE * 1024))) { | |
202 | count = 0; | |
203 | ||
204 | for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) { | |
205 | ptr[i] = vmem_zalloc(size, KM_SLEEP); | |
206 | if (ptr[i]) | |
207 | count++; | |
208 | } | |
209 | ||
210 | /* Ensure buffer has been zero filled */ | |
211 | for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) { | |
212 | for (j = 0; j < size; j++) { | |
213 | if (((char *)ptr[i])[j] != '\0') { | |
214 | splat_vprint(file, SPLAT_KMEM_TEST4_NAME, | |
215 | "%d-byte allocation was " | |
216 | "not zeroed\n", size); | |
217 | rc = -EFAULT; | |
218 | } | |
219 | } | |
220 | } | |
221 | ||
222 | for (i = 0; i < SPLAT_VMEM_ALLOC_COUNT; i++) | |
223 | if (ptr[i]) | |
224 | vmem_free(ptr[i], size); | |
225 | ||
226 | splat_vprint(file, SPLAT_KMEM_TEST4_NAME, | |
227 | "%d byte allocations, %d/%d successful\n", | |
228 | size, count, SPLAT_VMEM_ALLOC_COUNT); | |
229 | if (count != SPLAT_VMEM_ALLOC_COUNT) | |
230 | rc = -ENOMEM; | |
231 | ||
232 | size *= 2; | |
233 | } | |
234 | ||
235 | return rc; | |
236 | } | |
237 | ||
7c50328b | 238 | #define SPLAT_KMEM_TEST_MAGIC 0x004488CCUL |
239 | #define SPLAT_KMEM_CACHE_NAME "kmem_test" | |
7c50328b | 240 | #define SPLAT_KMEM_OBJ_COUNT 128 |
2fb9b26a | 241 | #define SPLAT_KMEM_OBJ_RECLAIM 16 |
f1ca4da6 | 242 | |
243 | typedef struct kmem_cache_data { | |
f1ca4da6 | 244 | unsigned long kcd_magic; |
245 | int kcd_flag; | |
2fb9b26a | 246 | char kcd_buf[0]; |
f1ca4da6 | 247 | } kmem_cache_data_t; |
248 | ||
249 | typedef struct kmem_cache_priv { | |
250 | unsigned long kcp_magic; | |
251 | struct file *kcp_file; | |
252 | kmem_cache_t *kcp_cache; | |
7c50328b | 253 | kmem_cache_data_t *kcp_kcd[SPLAT_KMEM_OBJ_COUNT]; |
44b8f176 | 254 | spinlock_t kcp_lock; |
255 | wait_queue_head_t kcp_waitq; | |
2fb9b26a | 256 | int kcp_size; |
48e0606a | 257 | int kcp_align; |
f1ca4da6 | 258 | int kcp_count; |
44b8f176 | 259 | int kcp_threads; |
260 | int kcp_alloc; | |
f1ca4da6 | 261 | int kcp_rc; |
262 | } kmem_cache_priv_t; | |
263 | ||
264 | static int | |
2fb9b26a | 265 | splat_kmem_cache_test_constructor(void *ptr, void *priv, int flags) |
f1ca4da6 | 266 | { |
f1ca4da6 | 267 | kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv; |
2fb9b26a | 268 | kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr; |
f1ca4da6 | 269 | |
0498e6c5 | 270 | if (kcd && kcp) { |
271 | kcd->kcd_magic = kcp->kcp_magic; | |
2fb9b26a | 272 | kcd->kcd_flag = 1; |
0498e6c5 | 273 | memset(kcd->kcd_buf, 0xaa, kcp->kcp_size - (sizeof *kcd)); |
274 | kcp->kcp_count++; | |
f1ca4da6 | 275 | } |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static void | |
2fb9b26a | 281 | splat_kmem_cache_test_destructor(void *ptr, void *priv) |
f1ca4da6 | 282 | { |
f1ca4da6 | 283 | kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv; |
2fb9b26a | 284 | kmem_cache_data_t *kcd = (kmem_cache_data_t *)ptr; |
f1ca4da6 | 285 | |
0498e6c5 | 286 | if (kcd && kcp) { |
287 | kcd->kcd_magic = 0; | |
2fb9b26a | 288 | kcd->kcd_flag = 0; |
0498e6c5 | 289 | memset(kcd->kcd_buf, 0xbb, kcp->kcp_size - (sizeof *kcd)); |
290 | kcp->kcp_count--; | |
f1ca4da6 | 291 | } |
292 | ||
293 | return; | |
294 | } | |
295 | ||
296 | static int | |
48e0606a BB |
297 | splat_kmem_cache_test(struct file *file, void *arg, char *name, |
298 | int size, int align, int flags) | |
f1ca4da6 | 299 | { |
300 | kmem_cache_t *cache = NULL; | |
301 | kmem_cache_data_t *kcd = NULL; | |
302 | kmem_cache_priv_t kcp; | |
303 | int rc = 0, max; | |
304 | ||
7c50328b | 305 | kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC; |
f1ca4da6 | 306 | kcp.kcp_file = file; |
2fb9b26a | 307 | kcp.kcp_size = size; |
48e0606a | 308 | kcp.kcp_align = align; |
f1ca4da6 | 309 | kcp.kcp_count = 0; |
310 | kcp.kcp_rc = 0; | |
311 | ||
48e0606a BB |
312 | cache = kmem_cache_create(SPLAT_KMEM_CACHE_NAME, |
313 | kcp.kcp_size, kcp.kcp_align, | |
2fb9b26a | 314 | splat_kmem_cache_test_constructor, |
315 | splat_kmem_cache_test_destructor, | |
316 | NULL, &kcp, NULL, flags); | |
f1ca4da6 | 317 | if (!cache) { |
2fb9b26a | 318 | splat_vprint(file, name, |
3f412673 BB |
319 | "Unable to create '%s'\n", |
320 | SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 321 | return -ENOMEM; |
322 | } | |
323 | ||
2fb9b26a | 324 | kcd = kmem_cache_alloc(cache, KM_SLEEP); |
f1ca4da6 | 325 | if (!kcd) { |
2fb9b26a | 326 | splat_vprint(file, name, |
3f412673 BB |
327 | "Unable to allocate from '%s'\n", |
328 | SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 329 | rc = -EINVAL; |
330 | goto out_free; | |
331 | } | |
332 | ||
333 | if (!kcd->kcd_flag) { | |
2fb9b26a | 334 | splat_vprint(file, name, |
3f412673 BB |
335 | "Failed to run contructor for '%s'\n", |
336 | SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 337 | rc = -EINVAL; |
338 | goto out_free; | |
339 | } | |
340 | ||
341 | if (kcd->kcd_magic != kcp.kcp_magic) { | |
2fb9b26a | 342 | splat_vprint(file, name, |
3f412673 BB |
343 | "Failed to pass private data to constructor " |
344 | "for '%s'\n", SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 345 | rc = -EINVAL; |
346 | goto out_free; | |
347 | } | |
348 | ||
349 | max = kcp.kcp_count; | |
f1ca4da6 | 350 | kmem_cache_free(cache, kcd); |
351 | ||
352 | /* Destroy the entire cache which will force destructors to | |
353 | * run and we can verify one was called for every object */ | |
354 | kmem_cache_destroy(cache); | |
355 | if (kcp.kcp_count) { | |
2fb9b26a | 356 | splat_vprint(file, name, |
3f412673 BB |
357 | "Failed to run destructor on all slab objects " |
358 | "for '%s'\n", SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 359 | rc = -EINVAL; |
360 | } | |
361 | ||
2fb9b26a | 362 | splat_vprint(file, name, |
3f412673 BB |
363 | "Successfully ran ctors/dtors for %d elements in '%s'\n", |
364 | max, SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 365 | |
366 | return rc; | |
367 | ||
368 | out_free: | |
369 | if (kcd) | |
370 | kmem_cache_free(cache, kcd); | |
f1b59d26 | 371 | |
f1ca4da6 | 372 | kmem_cache_destroy(cache); |
373 | return rc; | |
374 | } | |
375 | ||
a1502d76 | 376 | /* Validate small object cache behavior for dynamic/kmem/vmem caches */ |
2fb9b26a | 377 | static int |
378 | splat_kmem_test5(struct file *file, void *arg) | |
379 | { | |
a1502d76 | 380 | char *name = SPLAT_KMEM_TEST5_NAME; |
381 | int rc; | |
382 | ||
48e0606a | 383 | rc = splat_kmem_cache_test(file, arg, name, 128, 0, 0); |
a1502d76 | 384 | if (rc) |
385 | return rc; | |
386 | ||
48e0606a | 387 | rc = splat_kmem_cache_test(file, arg, name, 128, 0, KMC_KMEM); |
a1502d76 | 388 | if (rc) |
389 | return rc; | |
390 | ||
48e0606a | 391 | return splat_kmem_cache_test(file, arg, name, 128, 0, KMC_VMEM); |
2fb9b26a | 392 | } |
393 | ||
a1502d76 | 394 | /* Validate large object cache behavior for dynamic/kmem/vmem caches */ |
2fb9b26a | 395 | static int |
396 | splat_kmem_test6(struct file *file, void *arg) | |
397 | { | |
a1502d76 | 398 | char *name = SPLAT_KMEM_TEST6_NAME; |
399 | int rc; | |
400 | ||
48e0606a | 401 | rc = splat_kmem_cache_test(file, arg, name, 128*1024, 0, 0); |
a1502d76 | 402 | if (rc) |
403 | return rc; | |
404 | ||
48e0606a | 405 | rc = splat_kmem_cache_test(file, arg, name, 128*1024, 0, KMC_KMEM); |
a1502d76 | 406 | if (rc) |
407 | return rc; | |
408 | ||
48e0606a | 409 | return splat_kmem_cache_test(file, arg, name, 128*1028, 0, KMC_VMEM); |
2fb9b26a | 410 | } |
411 | ||
f1ca4da6 | 412 | static void |
2fb9b26a | 413 | splat_kmem_cache_test_reclaim(void *priv) |
f1ca4da6 | 414 | { |
415 | kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv; | |
2fb9b26a | 416 | int i, count; |
417 | ||
418 | count = min(SPLAT_KMEM_OBJ_RECLAIM, kcp->kcp_count); | |
419 | splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST7_NAME, | |
420 | "Reaping %d objects from '%s'\n", count, | |
421 | SPLAT_KMEM_CACHE_NAME); | |
f1ca4da6 | 422 | |
2fb9b26a | 423 | for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) { |
f1ca4da6 | 424 | if (kcp->kcp_kcd[i]) { |
425 | kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]); | |
426 | kcp->kcp_kcd[i] = NULL; | |
2fb9b26a | 427 | |
428 | if (--count == 0) | |
429 | break; | |
f1ca4da6 | 430 | } |
431 | } | |
432 | ||
433 | return; | |
434 | } | |
435 | ||
436 | static int | |
2fb9b26a | 437 | splat_kmem_test7(struct file *file, void *arg) |
f1ca4da6 | 438 | { |
439 | kmem_cache_t *cache; | |
440 | kmem_cache_priv_t kcp; | |
2fb9b26a | 441 | int i, rc = 0; |
f1ca4da6 | 442 | |
7c50328b | 443 | kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC; |
f1ca4da6 | 444 | kcp.kcp_file = file; |
2fb9b26a | 445 | kcp.kcp_size = 256; |
f1ca4da6 | 446 | kcp.kcp_count = 0; |
447 | kcp.kcp_rc = 0; | |
448 | ||
2fb9b26a | 449 | cache = kmem_cache_create(SPLAT_KMEM_CACHE_NAME, kcp.kcp_size, 0, |
450 | splat_kmem_cache_test_constructor, | |
451 | splat_kmem_cache_test_destructor, | |
452 | splat_kmem_cache_test_reclaim, | |
453 | &kcp, NULL, 0); | |
f1ca4da6 | 454 | if (!cache) { |
2fb9b26a | 455 | splat_vprint(file, SPLAT_KMEM_TEST7_NAME, |
7c50328b | 456 | "Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME); |
f1ca4da6 | 457 | return -ENOMEM; |
458 | } | |
459 | ||
460 | kcp.kcp_cache = cache; | |
461 | ||
7c50328b | 462 | for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) { |
f1b59d26 | 463 | /* All allocations need not succeed */ |
2fb9b26a | 464 | kcp.kcp_kcd[i] = kmem_cache_alloc(cache, KM_SLEEP); |
f1ca4da6 | 465 | if (!kcp.kcp_kcd[i]) { |
2fb9b26a | 466 | splat_vprint(file, SPLAT_KMEM_TEST7_NAME, |
f1ca4da6 | 467 | "Unable to allocate from '%s'\n", |
7c50328b | 468 | SPLAT_KMEM_CACHE_NAME); |
f1ca4da6 | 469 | } |
470 | } | |
471 | ||
2fb9b26a | 472 | ASSERT(kcp.kcp_count > 0); |
473 | ||
474 | /* Request the slab cache free any objects it can. For a few reasons | |
475 | * this may not immediately result in more free memory even if objects | |
476 | * are freed. First off, due to fragmentation we may not be able to | |
477 | * reclaim any slabs. Secondly, even if we do we fully clear some | |
478 | * slabs we will not want to immedately reclaim all of them because | |
479 | * we may contend with cache allocs and thrash. What we want to see | |
480 | * is slab size decrease more gradually as it becomes clear they | |
481 | * will not be needed. This should be acheivable in less than minute | |
482 | * if it takes longer than this something has gone wrong. | |
483 | */ | |
484 | for (i = 0; i < 60; i++) { | |
485 | kmem_cache_reap_now(cache); | |
486 | splat_vprint(file, SPLAT_KMEM_TEST7_NAME, | |
487 | "%s cache objects %d, slabs %u/%u objs %u/%u\n", | |
488 | SPLAT_KMEM_CACHE_NAME, kcp.kcp_count, | |
489 | (unsigned)cache->skc_slab_alloc, | |
490 | (unsigned)cache->skc_slab_total, | |
491 | (unsigned)cache->skc_obj_alloc, | |
492 | (unsigned)cache->skc_obj_total); | |
493 | ||
494 | if (cache->skc_obj_total == 0) | |
495 | break; | |
496 | ||
497 | set_current_state(TASK_INTERRUPTIBLE); | |
498 | schedule_timeout(HZ); | |
499 | } | |
500 | ||
501 | if (cache->skc_obj_total == 0) { | |
502 | splat_vprint(file, SPLAT_KMEM_TEST7_NAME, | |
503 | "Successfully created %d objects " | |
504 | "in cache %s and reclaimed them\n", | |
505 | SPLAT_KMEM_OBJ_COUNT, SPLAT_KMEM_CACHE_NAME); | |
506 | } else { | |
507 | splat_vprint(file, SPLAT_KMEM_TEST7_NAME, | |
508 | "Failed to reclaim %u/%d objects from cache %s\n", | |
509 | (unsigned)cache->skc_obj_total, SPLAT_KMEM_OBJ_COUNT, | |
510 | SPLAT_KMEM_CACHE_NAME); | |
511 | rc = -ENOMEM; | |
512 | } | |
f1ca4da6 | 513 | |
2fb9b26a | 514 | /* Cleanup our mess (for failure case of time expiring) */ |
7c50328b | 515 | for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) |
f1ca4da6 | 516 | if (kcp.kcp_kcd[i]) |
517 | kmem_cache_free(cache, kcp.kcp_kcd[i]); | |
518 | ||
519 | kmem_cache_destroy(cache); | |
520 | ||
521 | return rc; | |
522 | } | |
523 | ||
44b8f176 | 524 | static void |
525 | splat_kmem_test8_thread(void *arg) | |
526 | { | |
527 | kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)arg; | |
528 | int count = kcp->kcp_alloc, rc = 0, i; | |
529 | void **objs; | |
530 | ||
531 | ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC); | |
532 | ||
533 | objs = vmem_zalloc(count * sizeof(void *), KM_SLEEP); | |
534 | if (!objs) { | |
4afaaefa | 535 | splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST8_NAME, |
536 | "Unable to alloc objp array for cache '%s'\n", | |
537 | kcp->kcp_cache->skc_name); | |
44b8f176 | 538 | rc = -ENOMEM; |
539 | goto out; | |
540 | } | |
541 | ||
542 | for (i = 0; i < count; i++) { | |
543 | objs[i] = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP); | |
544 | if (!objs[i]) { | |
545 | splat_vprint(kcp->kcp_file, SPLAT_KMEM_TEST8_NAME, | |
4afaaefa | 546 | "Unable to allocate from cache '%s'\n", |
547 | kcp->kcp_cache->skc_name); | |
44b8f176 | 548 | rc = -ENOMEM; |
4afaaefa | 549 | break; |
44b8f176 | 550 | } |
551 | } | |
552 | ||
44b8f176 | 553 | for (i = 0; i < count; i++) |
554 | if (objs[i]) | |
555 | kmem_cache_free(kcp->kcp_cache, objs[i]); | |
556 | ||
557 | vmem_free(objs, count * sizeof(void *)); | |
558 | out: | |
559 | spin_lock(&kcp->kcp_lock); | |
44b8f176 | 560 | if (!kcp->kcp_rc) |
561 | kcp->kcp_rc = rc; | |
44b8f176 | 562 | |
a1502d76 | 563 | if (--kcp->kcp_threads == 0) |
564 | wake_up(&kcp->kcp_waitq); | |
565 | ||
e9d7a2be | 566 | spin_unlock(&kcp->kcp_lock); |
567 | ||
44b8f176 | 568 | thread_exit(); |
569 | } | |
570 | ||
571 | static int | |
572 | splat_kmem_test8_count(kmem_cache_priv_t *kcp, int threads) | |
573 | { | |
574 | int ret; | |
575 | ||
576 | spin_lock(&kcp->kcp_lock); | |
97f274d4 | 577 | ret = (kcp->kcp_threads == threads); |
578 | spin_unlock(&kcp->kcp_lock); | |
44b8f176 | 579 | |
97f274d4 | 580 | return ret; |
44b8f176 | 581 | } |
582 | ||
583 | /* This test will always pass and is simply here so I can easily | |
584 | * eyeball the slab cache locking overhead to ensure it is reasonable. | |
585 | */ | |
586 | static int | |
fece7c99 | 587 | splat_kmem_test8_sc(struct file *file, void *arg, int size, int count) |
44b8f176 | 588 | { |
589 | kmem_cache_priv_t kcp; | |
590 | kthread_t *thr; | |
591 | struct timespec start, stop, delta; | |
fece7c99 | 592 | char cache_name[32]; |
7ea1cbf5 | 593 | int i, j, rc = 0, threads = 32; |
44b8f176 | 594 | |
595 | kcp.kcp_magic = SPLAT_KMEM_TEST_MAGIC; | |
596 | kcp.kcp_file = file; | |
597 | ||
fece7c99 | 598 | splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %s", "name", |
d46630e0 | 599 | "time (sec)\tslabs \tobjs \thash\n"); |
fece7c99 | 600 | splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %s", "", |
a1502d76 | 601 | " \ttot/max/calc\ttot/max/calc\n"); |
44b8f176 | 602 | |
fece7c99 | 603 | for (i = 1; i <= count; i *= 2) { |
604 | kcp.kcp_size = size; | |
44b8f176 | 605 | kcp.kcp_count = 0; |
606 | kcp.kcp_threads = 0; | |
fece7c99 | 607 | kcp.kcp_alloc = i; |
44b8f176 | 608 | kcp.kcp_rc = 0; |
609 | spin_lock_init(&kcp.kcp_lock); | |
610 | init_waitqueue_head(&kcp.kcp_waitq); | |
611 | ||
12018327 | 612 | (void)snprintf(cache_name, 32, "%s-%d-%d", |
613 | SPLAT_KMEM_CACHE_NAME, size, i); | |
4afaaefa | 614 | kcp.kcp_cache = kmem_cache_create(cache_name, kcp.kcp_size, 0, |
fece7c99 | 615 | splat_kmem_cache_test_constructor, |
616 | splat_kmem_cache_test_destructor, | |
617 | NULL, &kcp, NULL, 0); | |
44b8f176 | 618 | if (!kcp.kcp_cache) { |
619 | splat_vprint(file, SPLAT_KMEM_TEST8_NAME, | |
620 | "Unable to create '%s' cache\n", | |
621 | SPLAT_KMEM_CACHE_NAME); | |
7ea1cbf5 | 622 | rc = -ENOMEM; |
623 | break; | |
44b8f176 | 624 | } |
625 | ||
626 | start = current_kernel_time(); | |
627 | ||
fece7c99 | 628 | for (j = 0; j < threads; j++) { |
44b8f176 | 629 | thr = thread_create(NULL, 0, splat_kmem_test8_thread, |
630 | &kcp, 0, &p0, TS_RUN, minclsyspri); | |
7ea1cbf5 | 631 | if (thr == NULL) { |
632 | rc = -ESRCH; | |
633 | break; | |
634 | } | |
97f274d4 | 635 | spin_lock(&kcp.kcp_lock); |
44b8f176 | 636 | kcp.kcp_threads++; |
97f274d4 | 637 | spin_unlock(&kcp.kcp_lock); |
44b8f176 | 638 | } |
639 | ||
640 | /* Sleep until the thread sets kcp.kcp_threads == 0 */ | |
641 | wait_event(kcp.kcp_waitq, splat_kmem_test8_count(&kcp, 0)); | |
642 | stop = current_kernel_time(); | |
643 | delta = timespec_sub(stop, start); | |
644 | ||
fece7c99 | 645 | splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "%-22s %2ld.%09ld\t" |
a1502d76 | 646 | "%lu/%lu/%lu\t%lu/%lu/%lu\n", |
fece7c99 | 647 | kcp.kcp_cache->skc_name, |
44b8f176 | 648 | delta.tv_sec, delta.tv_nsec, |
649 | (unsigned long)kcp.kcp_cache->skc_slab_total, | |
650 | (unsigned long)kcp.kcp_cache->skc_slab_max, | |
fece7c99 | 651 | (unsigned long)(kcp.kcp_alloc * threads / |
652 | SPL_KMEM_CACHE_OBJ_PER_SLAB), | |
44b8f176 | 653 | (unsigned long)kcp.kcp_cache->skc_obj_total, |
654 | (unsigned long)kcp.kcp_cache->skc_obj_max, | |
a1502d76 | 655 | (unsigned long)(kcp.kcp_alloc * threads)); |
44b8f176 | 656 | |
657 | kmem_cache_destroy(kcp.kcp_cache); | |
658 | ||
7ea1cbf5 | 659 | if (!rc && kcp.kcp_rc) |
660 | rc = kcp.kcp_rc; | |
661 | ||
662 | if (rc) | |
44b8f176 | 663 | break; |
664 | } | |
665 | ||
7ea1cbf5 | 666 | return rc; |
44b8f176 | 667 | } |
668 | ||
fece7c99 | 669 | static int |
670 | splat_kmem_test8(struct file *file, void *arg) | |
671 | { | |
672 | int i, rc = 0; | |
673 | ||
674 | /* Run through slab cache with objects size from | |
675 | * 16-1Mb in 4x multiples with 1024 objects each */ | |
676 | for (i = 16; i <= 1024*1024; i *= 4) { | |
97f274d4 | 677 | rc = splat_kmem_test8_sc(file, arg, i, 256); |
fece7c99 | 678 | if (rc) |
679 | break; | |
680 | } | |
681 | ||
682 | return rc; | |
683 | } | |
684 | ||
48e0606a BB |
685 | /* Validate object alignment cache behavior for caches */ |
686 | static int | |
687 | splat_kmem_test9(struct file *file, void *arg) | |
688 | { | |
689 | char *name = SPLAT_KMEM_TEST9_NAME; | |
690 | int i, rc; | |
691 | ||
692 | for (i = 8; i <= PAGE_SIZE; i *= 2) { | |
693 | rc = splat_kmem_cache_test(file, arg, name, 157, i, 0); | |
694 | if (rc) | |
695 | return rc; | |
696 | } | |
697 | ||
698 | return rc; | |
699 | } | |
700 | ||
7c50328b | 701 | splat_subsystem_t * |
702 | splat_kmem_init(void) | |
f1ca4da6 | 703 | { |
7c50328b | 704 | splat_subsystem_t *sub; |
f1ca4da6 | 705 | |
706 | sub = kmalloc(sizeof(*sub), GFP_KERNEL); | |
707 | if (sub == NULL) | |
708 | return NULL; | |
709 | ||
710 | memset(sub, 0, sizeof(*sub)); | |
7c50328b | 711 | strncpy(sub->desc.name, SPLAT_KMEM_NAME, SPLAT_NAME_SIZE); |
712 | strncpy(sub->desc.desc, SPLAT_KMEM_DESC, SPLAT_DESC_SIZE); | |
f1ca4da6 | 713 | INIT_LIST_HEAD(&sub->subsystem_list); |
714 | INIT_LIST_HEAD(&sub->test_list); | |
715 | spin_lock_init(&sub->test_lock); | |
7c50328b | 716 | sub->desc.id = SPLAT_SUBSYSTEM_KMEM; |
f1ca4da6 | 717 | |
7c50328b | 718 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST1_NAME, SPLAT_KMEM_TEST1_DESC, |
719 | SPLAT_KMEM_TEST1_ID, splat_kmem_test1); | |
720 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST2_NAME, SPLAT_KMEM_TEST2_DESC, | |
721 | SPLAT_KMEM_TEST2_ID, splat_kmem_test2); | |
722 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST3_NAME, SPLAT_KMEM_TEST3_DESC, | |
723 | SPLAT_KMEM_TEST3_ID, splat_kmem_test3); | |
724 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST4_NAME, SPLAT_KMEM_TEST4_DESC, | |
725 | SPLAT_KMEM_TEST4_ID, splat_kmem_test4); | |
79b31f36 | 726 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST5_NAME, SPLAT_KMEM_TEST5_DESC, |
727 | SPLAT_KMEM_TEST5_ID, splat_kmem_test5); | |
2fb9b26a | 728 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST6_NAME, SPLAT_KMEM_TEST6_DESC, |
729 | SPLAT_KMEM_TEST6_ID, splat_kmem_test6); | |
730 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST7_NAME, SPLAT_KMEM_TEST7_DESC, | |
731 | SPLAT_KMEM_TEST7_ID, splat_kmem_test7); | |
44b8f176 | 732 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST8_NAME, SPLAT_KMEM_TEST8_DESC, |
733 | SPLAT_KMEM_TEST8_ID, splat_kmem_test8); | |
48e0606a BB |
734 | SPLAT_TEST_INIT(sub, SPLAT_KMEM_TEST9_NAME, SPLAT_KMEM_TEST9_DESC, |
735 | SPLAT_KMEM_TEST9_ID, splat_kmem_test9); | |
f1ca4da6 | 736 | |
737 | return sub; | |
738 | } | |
739 | ||
740 | void | |
7c50328b | 741 | splat_kmem_fini(splat_subsystem_t *sub) |
f1ca4da6 | 742 | { |
743 | ASSERT(sub); | |
48e0606a | 744 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST9_ID); |
44b8f176 | 745 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST8_ID); |
2fb9b26a | 746 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST7_ID); |
747 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST6_ID); | |
79b31f36 | 748 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST5_ID); |
7c50328b | 749 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST4_ID); |
750 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST3_ID); | |
751 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST2_ID); | |
752 | SPLAT_TEST_FINI(sub, SPLAT_KMEM_TEST1_ID); | |
f1ca4da6 | 753 | |
754 | kfree(sub); | |
755 | } | |
756 | ||
757 | int | |
7c50328b | 758 | splat_kmem_id(void) { |
759 | return SPLAT_SUBSYSTEM_KMEM; | |
f1ca4da6 | 760 | } |