| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: subr_vmem.c,v 1.100 2019/12/21 14:50:34 ad Exp $ */ | | 1 | /* $NetBSD: subr_vmem.c,v 1.101 2020/04/19 21:11:42 ad Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c)2006,2007,2008,2009 YAMAMOTO Takashi, | | 4 | * Copyright (c)2006,2007,2008,2009 YAMAMOTO Takashi, |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
| @@ -36,27 +36,27 @@ | | | @@ -36,27 +36,27 @@ |
36 | * - A pool(9) is used for vmem boundary tags | | 36 | * - A pool(9) is used for vmem boundary tags |
37 | * - During a pool get call the global vmem_btag_refill_lock is taken, | | 37 | * - During a pool get call the global vmem_btag_refill_lock is taken, |
38 | * to serialize access to the allocation reserve, but no other | | 38 | * to serialize access to the allocation reserve, but no other |
39 | * vmem arena locks. | | 39 | * vmem arena locks. |
40 | * - During pool_put calls no vmem mutexes are locked. | | 40 | * - During pool_put calls no vmem mutexes are locked. |
41 | * - pool_drain doesn't hold the pool's mutex while releasing memory to | | 41 | * - pool_drain doesn't hold the pool's mutex while releasing memory to |
42 | * its backing therefore no interferance with any vmem mutexes. | | 42 | * its backing therefore no interferance with any vmem mutexes. |
43 | * - The boundary tag pool is forced to put page headers into pool pages | | 43 | * - The boundary tag pool is forced to put page headers into pool pages |
44 | * (PR_PHINPAGE) and not off page to avoid pool recursion. | | 44 | * (PR_PHINPAGE) and not off page to avoid pool recursion. |
45 | * (due to sizeof(bt_t) it should be the case anyway) | | 45 | * (due to sizeof(bt_t) it should be the case anyway) |
46 | */ | | 46 | */ |
47 | | | 47 | |
48 | #include <sys/cdefs.h> | | 48 | #include <sys/cdefs.h> |
49 | __KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,v 1.100 2019/12/21 14:50:34 ad Exp $"); | | 49 | __KERNEL_RCSID(0, "$NetBSD: subr_vmem.c,v 1.101 2020/04/19 21:11:42 ad Exp $"); |
50 | | | 50 | |
51 | #if defined(_KERNEL) && defined(_KERNEL_OPT) | | 51 | #if defined(_KERNEL) && defined(_KERNEL_OPT) |
52 | #include "opt_ddb.h" | | 52 | #include "opt_ddb.h" |
53 | #endif /* defined(_KERNEL) && defined(_KERNEL_OPT) */ | | 53 | #endif /* defined(_KERNEL) && defined(_KERNEL_OPT) */ |
54 | | | 54 | |
55 | #include <sys/param.h> | | 55 | #include <sys/param.h> |
56 | #include <sys/hash.h> | | 56 | #include <sys/hash.h> |
57 | #include <sys/queue.h> | | 57 | #include <sys/queue.h> |
58 | #include <sys/bitops.h> | | 58 | #include <sys/bitops.h> |
59 | | | 59 | |
60 | #if defined(_KERNEL) | | 60 | #if defined(_KERNEL) |
61 | #include <sys/systm.h> | | 61 | #include <sys/systm.h> |
62 | #include <sys/kernel.h> /* hz */ | | 62 | #include <sys/kernel.h> /* hz */ |
| @@ -195,26 +195,27 @@ static size_t vmem_btag_freelist_count = | | | @@ -195,26 +195,27 @@ static size_t vmem_btag_freelist_count = |
195 | static struct pool vmem_btag_pool; | | 195 | static struct pool vmem_btag_pool; |
196 | | | 196 | |
197 | static void | | 197 | static void |
198 | vmem_kick_pdaemon(void) | | 198 | vmem_kick_pdaemon(void) |
199 | { | | 199 | { |
200 | #if defined(_KERNEL) | | 200 | #if defined(_KERNEL) |
201 | uvm_kick_pdaemon(); | | 201 | uvm_kick_pdaemon(); |
202 | #endif | | 202 | #endif |
203 | } | | 203 | } |
204 | | | 204 | |
205 | /* ---- boundary tag */ | | 205 | /* ---- boundary tag */ |
206 | | | 206 | |
207 | static int bt_refill(vmem_t *vm); | | 207 | static int bt_refill(vmem_t *vm); |
| | | 208 | static int bt_refill_locked(vmem_t *vm); |
208 | | | 209 | |
209 | static void * | | 210 | static void * |
210 | pool_page_alloc_vmem_meta(struct pool *pp, int flags) | | 211 | pool_page_alloc_vmem_meta(struct pool *pp, int flags) |
211 | { | | 212 | { |
212 | const vm_flag_t vflags = (flags & PR_WAITOK) ? VM_SLEEP: VM_NOSLEEP; | | 213 | const vm_flag_t vflags = (flags & PR_WAITOK) ? VM_SLEEP: VM_NOSLEEP; |
213 | vmem_addr_t va; | | 214 | vmem_addr_t va; |
214 | int ret; | | 215 | int ret; |
215 | | | 216 | |
216 | ret = vmem_alloc(kmem_meta_arena, pp->pr_alloc->pa_pagesz, | | 217 | ret = vmem_alloc(kmem_meta_arena, pp->pr_alloc->pa_pagesz, |
217 | (vflags & ~VM_FITMASK) | VM_INSTANTFIT | VM_POPULATING, &va); | | 218 | (vflags & ~VM_FITMASK) | VM_INSTANTFIT | VM_POPULATING, &va); |
218 | | | 219 | |
219 | return ret ? NULL : (void *)va; | | 220 | return ret ? NULL : (void *)va; |
220 | } | | 221 | } |
| @@ -224,33 +225,33 @@ pool_page_free_vmem_meta(struct pool *pp | | | @@ -224,33 +225,33 @@ pool_page_free_vmem_meta(struct pool *pp |
224 | { | | 225 | { |
225 | | | 226 | |
226 | vmem_free(kmem_meta_arena, (vmem_addr_t)v, pp->pr_alloc->pa_pagesz); | | 227 | vmem_free(kmem_meta_arena, (vmem_addr_t)v, pp->pr_alloc->pa_pagesz); |
227 | } | | 228 | } |
228 | | | 229 | |
229 | /* allocator for vmem-pool metadata */ | | 230 | /* allocator for vmem-pool metadata */ |
230 | struct pool_allocator pool_allocator_vmem_meta = { | | 231 | struct pool_allocator pool_allocator_vmem_meta = { |
231 | .pa_alloc = pool_page_alloc_vmem_meta, | | 232 | .pa_alloc = pool_page_alloc_vmem_meta, |
232 | .pa_free = pool_page_free_vmem_meta, | | 233 | .pa_free = pool_page_free_vmem_meta, |
233 | .pa_pagesz = 0 | | 234 | .pa_pagesz = 0 |
234 | }; | | 235 | }; |
235 | | | 236 | |
236 | static int | | 237 | static int |
237 | bt_refill(vmem_t *vm) | | 238 | bt_refill_locked(vmem_t *vm) |
238 | { | | 239 | { |
239 | bt_t *bt; | | 240 | bt_t *bt; |
240 | | | 241 | |
241 | VMEM_LOCK(vm); | | 242 | VMEM_ASSERT_LOCKED(vm); |
| | | 243 | |
242 | if (vm->vm_nfreetags > BT_MINRESERVE) { | | 244 | if (vm->vm_nfreetags > BT_MINRESERVE) { |
243 | VMEM_UNLOCK(vm); | | | |
244 | return 0; | | 245 | return 0; |
245 | } | | 246 | } |
246 | | | 247 | |
247 | mutex_enter(&vmem_btag_lock); | | 248 | mutex_enter(&vmem_btag_lock); |
248 | while (!LIST_EMPTY(&vmem_btag_freelist) && | | 249 | while (!LIST_EMPTY(&vmem_btag_freelist) && |
249 | vm->vm_nfreetags <= BT_MINRESERVE) { | | 250 | vm->vm_nfreetags <= BT_MINRESERVE) { |
250 | bt = LIST_FIRST(&vmem_btag_freelist); | | 251 | bt = LIST_FIRST(&vmem_btag_freelist); |
251 | LIST_REMOVE(bt, bt_freelist); | | 252 | LIST_REMOVE(bt, bt_freelist); |
252 | LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); | | 253 | LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); |
253 | vm->vm_nfreetags++; | | 254 | vm->vm_nfreetags++; |
254 | vmem_btag_freelist_count--; | | 255 | vmem_btag_freelist_count--; |
255 | VMEM_EVCNT_INCR(static_bt_inuse); | | 256 | VMEM_EVCNT_INCR(static_bt_inuse); |
256 | } | | 257 | } |
| @@ -259,94 +260,104 @@ bt_refill(vmem_t *vm) | | | @@ -259,94 +260,104 @@ bt_refill(vmem_t *vm) |
259 | while (vm->vm_nfreetags <= BT_MINRESERVE) { | | 260 | while (vm->vm_nfreetags <= BT_MINRESERVE) { |
260 | VMEM_UNLOCK(vm); | | 261 | VMEM_UNLOCK(vm); |
261 | mutex_enter(&vmem_btag_refill_lock); | | 262 | mutex_enter(&vmem_btag_refill_lock); |
262 | bt = pool_get(&vmem_btag_pool, PR_NOWAIT); | | 263 | bt = pool_get(&vmem_btag_pool, PR_NOWAIT); |
263 | mutex_exit(&vmem_btag_refill_lock); | | 264 | mutex_exit(&vmem_btag_refill_lock); |
264 | VMEM_LOCK(vm); | | 265 | VMEM_LOCK(vm); |
265 | if (bt == NULL) | | 266 | if (bt == NULL) |
266 | break; | | 267 | break; |
267 | LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); | | 268 | LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); |
268 | vm->vm_nfreetags++; | | 269 | vm->vm_nfreetags++; |
269 | } | | 270 | } |
270 | | | 271 | |
271 | if (vm->vm_nfreetags <= BT_MINRESERVE) { | | 272 | if (vm->vm_nfreetags <= BT_MINRESERVE) { |
272 | VMEM_UNLOCK(vm); | | | |
273 | return ENOMEM; | | 273 | return ENOMEM; |
274 | } | | 274 | } |
275 | | | 275 | |
276 | VMEM_UNLOCK(vm); | | | |
277 | | | | |
278 | if (kmem_meta_arena != NULL) { | | 276 | if (kmem_meta_arena != NULL) { |
| | | 277 | VMEM_UNLOCK(vm); |
279 | (void)bt_refill(kmem_arena); | | 278 | (void)bt_refill(kmem_arena); |
280 | (void)bt_refill(kmem_va_meta_arena); | | 279 | (void)bt_refill(kmem_va_meta_arena); |
281 | (void)bt_refill(kmem_meta_arena); | | 280 | (void)bt_refill(kmem_meta_arena); |
| | | 281 | VMEM_LOCK(vm); |
282 | } | | 282 | } |
283 | | | 283 | |
284 | return 0; | | 284 | return 0; |
285 | } | | 285 | } |
286 | | | 286 | |
| | | 287 | static int |
| | | 288 | bt_refill(vmem_t *vm) |
| | | 289 | { |
| | | 290 | int rv; |
| | | 291 | |
| | | 292 | VMEM_LOCK(vm); |
| | | 293 | rv = bt_refill_locked(vm); |
| | | 294 | VMEM_UNLOCK(vm); |
| | | 295 | return rv; |
| | | 296 | } |
| | | 297 | |
287 | static bt_t * | | 298 | static bt_t * |
288 | bt_alloc(vmem_t *vm, vm_flag_t flags) | | 299 | bt_alloc(vmem_t *vm, vm_flag_t flags) |
289 | { | | 300 | { |
290 | bt_t *bt; | | 301 | bt_t *bt; |
291 | VMEM_LOCK(vm); | | 302 | |
| | | 303 | VMEM_ASSERT_LOCKED(vm); |
| | | 304 | |
292 | while (vm->vm_nfreetags <= BT_MINRESERVE && (flags & VM_POPULATING) == 0) { | | 305 | while (vm->vm_nfreetags <= BT_MINRESERVE && (flags & VM_POPULATING) == 0) { |
293 | VMEM_UNLOCK(vm); | | 306 | if (bt_refill_locked(vm)) { |
294 | if (bt_refill(vm)) { | | | |
295 | if ((flags & VM_NOSLEEP) != 0) { | | 307 | if ((flags & VM_NOSLEEP) != 0) { |
296 | return NULL; | | 308 | return NULL; |
297 | } | | 309 | } |
298 | | | 310 | |
299 | /* | | 311 | /* |
300 | * It would be nice to wait for something specific here | | 312 | * It would be nice to wait for something specific here |
301 | * but there are multiple ways that a retry could | | 313 | * but there are multiple ways that a retry could |
302 | * succeed and we can't wait for multiple things | | 314 | * succeed and we can't wait for multiple things |
303 | * simultaneously. So we'll just sleep for an arbitrary | | 315 | * simultaneously. So we'll just sleep for an arbitrary |
304 | * short period of time and retry regardless. | | 316 | * short period of time and retry regardless. |
305 | * This should be a very rare case. | | 317 | * This should be a very rare case. |
306 | */ | | 318 | */ |
307 | | | 319 | |
308 | vmem_kick_pdaemon(); | | 320 | vmem_kick_pdaemon(); |
309 | kpause("btalloc", false, 1, NULL); | | 321 | kpause("btalloc", false, 1, &vm->vm_lock); |
310 | } | | 322 | } |
311 | VMEM_LOCK(vm); | | | |
312 | } | | 323 | } |
313 | bt = LIST_FIRST(&vm->vm_freetags); | | 324 | bt = LIST_FIRST(&vm->vm_freetags); |
314 | LIST_REMOVE(bt, bt_freelist); | | 325 | LIST_REMOVE(bt, bt_freelist); |
315 | vm->vm_nfreetags--; | | 326 | vm->vm_nfreetags--; |
316 | VMEM_UNLOCK(vm); | | | |
317 | | | 327 | |
318 | return bt; | | 328 | return bt; |
319 | } | | 329 | } |
320 | | | 330 | |
321 | static void | | 331 | static void |
322 | bt_free(vmem_t *vm, bt_t *bt) | | 332 | bt_free(vmem_t *vm, bt_t *bt) |
323 | { | | 333 | { |
324 | | | 334 | |
325 | VMEM_LOCK(vm); | | 335 | VMEM_ASSERT_LOCKED(vm); |
| | | 336 | |
326 | LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); | | 337 | LIST_INSERT_HEAD(&vm->vm_freetags, bt, bt_freelist); |
327 | vm->vm_nfreetags++; | | 338 | vm->vm_nfreetags++; |
328 | VMEM_UNLOCK(vm); | | | |
329 | } | | 339 | } |
330 | | | 340 | |
331 | static void | | 341 | static void |
332 | bt_freetrim(vmem_t *vm, int freelimit) | | 342 | bt_freetrim(vmem_t *vm, int freelimit) |
333 | { | | 343 | { |
334 | bt_t *t; | | 344 | bt_t *t; |
335 | LIST_HEAD(, vmem_btag) tofree; | | 345 | LIST_HEAD(, vmem_btag) tofree; |
336 | | | 346 | |
| | | 347 | VMEM_ASSERT_LOCKED(vm); |
| | | 348 | |
337 | LIST_INIT(&tofree); | | 349 | LIST_INIT(&tofree); |
338 | | | 350 | |
339 | VMEM_LOCK(vm); | | | |
340 | while (vm->vm_nfreetags > freelimit) { | | 351 | while (vm->vm_nfreetags > freelimit) { |
341 | bt_t *bt = LIST_FIRST(&vm->vm_freetags); | | 352 | bt_t *bt = LIST_FIRST(&vm->vm_freetags); |
342 | LIST_REMOVE(bt, bt_freelist); | | 353 | LIST_REMOVE(bt, bt_freelist); |
343 | vm->vm_nfreetags--; | | 354 | vm->vm_nfreetags--; |
344 | if (bt >= static_bts | | 355 | if (bt >= static_bts |
345 | && bt < &static_bts[STATIC_BT_COUNT]) { | | 356 | && bt < &static_bts[STATIC_BT_COUNT]) { |
346 | mutex_enter(&vmem_btag_lock); | | 357 | mutex_enter(&vmem_btag_lock); |
347 | LIST_INSERT_HEAD(&vmem_btag_freelist, bt, bt_freelist); | | 358 | LIST_INSERT_HEAD(&vmem_btag_freelist, bt, bt_freelist); |
348 | vmem_btag_freelist_count++; | | 359 | vmem_btag_freelist_count++; |
349 | mutex_exit(&vmem_btag_lock); | | 360 | mutex_exit(&vmem_btag_lock); |
350 | VMEM_EVCNT_DECR(static_bt_inuse); | | 361 | VMEM_EVCNT_DECR(static_bt_inuse); |
351 | } else { | | 362 | } else { |
352 | LIST_INSERT_HEAD(&tofree, bt, bt_freelist); | | 363 | LIST_INSERT_HEAD(&tofree, bt, bt_freelist); |
| @@ -413,27 +424,27 @@ bt_freehead_toalloc(vmem_t *vm, vmem_siz | | | @@ -413,27 +424,27 @@ bt_freehead_toalloc(vmem_t *vm, vmem_siz |
413 | | | 424 | |
414 | return &vm->vm_freelist[idx]; | | 425 | return &vm->vm_freelist[idx]; |
415 | } | | 426 | } |
416 | | | 427 | |
417 | /* ---- boundary tag hash */ | | 428 | /* ---- boundary tag hash */ |
418 | | | 429 | |
419 | static struct vmem_hashlist * | | 430 | static struct vmem_hashlist * |
420 | bt_hashhead(vmem_t *vm, vmem_addr_t addr) | | 431 | bt_hashhead(vmem_t *vm, vmem_addr_t addr) |
421 | { | | 432 | { |
422 | struct vmem_hashlist *list; | | 433 | struct vmem_hashlist *list; |
423 | unsigned int hash; | | 434 | unsigned int hash; |
424 | | | 435 | |
425 | hash = hash32_buf(&addr, sizeof(addr), HASH32_BUF_INIT); | | 436 | hash = hash32_buf(&addr, sizeof(addr), HASH32_BUF_INIT); |
426 | list = &vm->vm_hashlist[hash % vm->vm_hashsize]; | | 437 | list = &vm->vm_hashlist[hash & vm->vm_hashmask]; |
427 | | | 438 | |
428 | return list; | | 439 | return list; |
429 | } | | 440 | } |
430 | | | 441 | |
431 | static bt_t * | | 442 | static bt_t * |
432 | bt_lookupbusy(vmem_t *vm, vmem_addr_t addr) | | 443 | bt_lookupbusy(vmem_t *vm, vmem_addr_t addr) |
433 | { | | 444 | { |
434 | struct vmem_hashlist *list; | | 445 | struct vmem_hashlist *list; |
435 | bt_t *bt; | | 446 | bt_t *bt; |
436 | | | 447 | |
437 | list = bt_hashhead(vm, addr); | | 448 | list = bt_hashhead(vm, addr); |
438 | LIST_FOREACH(bt, list, bt_hashlist) { | | 449 | LIST_FOREACH(bt, list, bt_hashlist) { |
439 | if (bt->bt_start == addr) { | | 450 | if (bt->bt_start == addr) { |
| @@ -453,27 +464,29 @@ bt_rembusy(vmem_t *vm, bt_t *bt) | | | @@ -453,27 +464,29 @@ bt_rembusy(vmem_t *vm, bt_t *bt) |
453 | vm->vm_nbusytag--; | | 464 | vm->vm_nbusytag--; |
454 | LIST_REMOVE(bt, bt_hashlist); | | 465 | LIST_REMOVE(bt, bt_hashlist); |
455 | } | | 466 | } |
456 | | | 467 | |
457 | static void | | 468 | static void |
458 | bt_insbusy(vmem_t *vm, bt_t *bt) | | 469 | bt_insbusy(vmem_t *vm, bt_t *bt) |
459 | { | | 470 | { |
460 | struct vmem_hashlist *list; | | 471 | struct vmem_hashlist *list; |
461 | | | 472 | |
462 | KASSERT(bt->bt_type == BT_TYPE_BUSY); | | 473 | KASSERT(bt->bt_type == BT_TYPE_BUSY); |
463 | | | 474 | |
464 | list = bt_hashhead(vm, bt->bt_start); | | 475 | list = bt_hashhead(vm, bt->bt_start); |
465 | LIST_INSERT_HEAD(list, bt, bt_hashlist); | | 476 | LIST_INSERT_HEAD(list, bt, bt_hashlist); |
466 | vm->vm_nbusytag++; | | 477 | if (++vm->vm_nbusytag > vm->vm_maxbusytag) { |
| | | 478 | vm->vm_maxbusytag = vm->vm_nbusytag; |
| | | 479 | } |
467 | vm->vm_inuse += bt->bt_size; | | 480 | vm->vm_inuse += bt->bt_size; |
468 | } | | 481 | } |
469 | | | 482 | |
470 | /* ---- boundary tag list */ | | 483 | /* ---- boundary tag list */ |
471 | | | 484 | |
472 | static void | | 485 | static void |
473 | bt_remseg(vmem_t *vm, bt_t *bt) | | 486 | bt_remseg(vmem_t *vm, bt_t *bt) |
474 | { | | 487 | { |
475 | | | 488 | |
476 | TAILQ_REMOVE(&vm->vm_seglist, bt, bt_seglist); | | 489 | TAILQ_REMOVE(&vm->vm_seglist, bt, bt_seglist); |
477 | } | | 490 | } |
478 | | | 491 | |
479 | static void | | 492 | static void |
| @@ -666,178 +679,193 @@ void | | | @@ -666,178 +679,193 @@ void |
666 | vmem_subsystem_init(vmem_t *vm) | | 679 | vmem_subsystem_init(vmem_t *vm) |
667 | { | | 680 | { |
668 | | | 681 | |
669 | kmem_va_meta_arena = vmem_init(&kmem_va_meta_arena_store, "vmem-va", | | 682 | kmem_va_meta_arena = vmem_init(&kmem_va_meta_arena_store, "vmem-va", |
670 | 0, 0, PAGE_SIZE, vmem_alloc, vmem_free, vm, | | 683 | 0, 0, PAGE_SIZE, vmem_alloc, vmem_free, vm, |
671 | 0, VM_NOSLEEP | VM_BOOTSTRAP | VM_LARGEIMPORT, | | 684 | 0, VM_NOSLEEP | VM_BOOTSTRAP | VM_LARGEIMPORT, |
672 | IPL_VM); | | 685 | IPL_VM); |
673 | | | 686 | |
674 | kmem_meta_arena = vmem_init(&kmem_meta_arena_store, "vmem-meta", | | 687 | kmem_meta_arena = vmem_init(&kmem_meta_arena_store, "vmem-meta", |
675 | 0, 0, PAGE_SIZE, | | 688 | 0, 0, PAGE_SIZE, |
676 | uvm_km_kmem_alloc, uvm_km_kmem_free, kmem_va_meta_arena, | | 689 | uvm_km_kmem_alloc, uvm_km_kmem_free, kmem_va_meta_arena, |
677 | 0, VM_NOSLEEP | VM_BOOTSTRAP, IPL_VM); | | 690 | 0, VM_NOSLEEP | VM_BOOTSTRAP, IPL_VM); |
678 | | | 691 | |
679 | pool_init(&vmem_btag_pool, sizeof(bt_t), 0, 0, PR_PHINPAGE, | | 692 | pool_init(&vmem_btag_pool, sizeof(bt_t), coherency_unit, 0, |
680 | "vmembt", &pool_allocator_vmem_meta, IPL_VM); | | 693 | PR_PHINPAGE, "vmembt", &pool_allocator_vmem_meta, IPL_VM); |
681 | } | | 694 | } |
682 | #endif /* defined(_KERNEL) */ | | 695 | #endif /* defined(_KERNEL) */ |
683 | | | 696 | |
684 | static int | | 697 | static int |
685 | vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags, | | 698 | vmem_add1(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags, |
686 | int spanbttype) | | 699 | int spanbttype) |
687 | { | | 700 | { |
688 | bt_t *btspan; | | 701 | bt_t *btspan; |
689 | bt_t *btfree; | | 702 | bt_t *btfree; |
690 | | | 703 | |
| | | 704 | VMEM_ASSERT_LOCKED(vm); |
691 | KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0); | | 705 | KASSERT((flags & (VM_SLEEP|VM_NOSLEEP)) != 0); |
692 | KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0); | | 706 | KASSERT((~flags & (VM_SLEEP|VM_NOSLEEP)) != 0); |
693 | KASSERT(spanbttype == BT_TYPE_SPAN || | | 707 | KASSERT(spanbttype == BT_TYPE_SPAN || |
694 | spanbttype == BT_TYPE_SPAN_STATIC); | | 708 | spanbttype == BT_TYPE_SPAN_STATIC); |
695 | | | 709 | |
696 | btspan = bt_alloc(vm, flags); | | 710 | btspan = bt_alloc(vm, flags); |
697 | if (btspan == NULL) { | | 711 | if (btspan == NULL) { |
698 | return ENOMEM; | | 712 | return ENOMEM; |
699 | } | | 713 | } |
700 | btfree = bt_alloc(vm, flags); | | 714 | btfree = bt_alloc(vm, flags); |
701 | if (btfree == NULL) { | | 715 | if (btfree == NULL) { |
702 | bt_free(vm, btspan); | | 716 | bt_free(vm, btspan); |
703 | return ENOMEM; | | 717 | return ENOMEM; |
704 | } | | 718 | } |
705 | | | 719 | |
706 | btspan->bt_type = spanbttype; | | 720 | btspan->bt_type = spanbttype; |
707 | btspan->bt_start = addr; | | 721 | btspan->bt_start = addr; |
708 | btspan->bt_size = size; | | 722 | btspan->bt_size = size; |
709 | | | 723 | |
710 | btfree->bt_type = BT_TYPE_FREE; | | 724 | btfree->bt_type = BT_TYPE_FREE; |
711 | btfree->bt_start = addr; | | 725 | btfree->bt_start = addr; |
712 | btfree->bt_size = size; | | 726 | btfree->bt_size = size; |
713 | | | 727 | |
714 | VMEM_LOCK(vm); | | | |
715 | bt_insseg_tail(vm, btspan); | | 728 | bt_insseg_tail(vm, btspan); |
716 | bt_insseg(vm, btfree, btspan); | | 729 | bt_insseg(vm, btfree, btspan); |
717 | bt_insfree(vm, btfree); | | 730 | bt_insfree(vm, btfree); |
718 | vm->vm_size += size; | | 731 | vm->vm_size += size; |
719 | VMEM_UNLOCK(vm); | | | |
720 | | | 732 | |
721 | return 0; | | 733 | return 0; |
722 | } | | 734 | } |
723 | | | 735 | |
724 | static void | | 736 | static void |
725 | vmem_destroy1(vmem_t *vm) | | 737 | vmem_destroy1(vmem_t *vm) |
726 | { | | 738 | { |
727 | | | 739 | |
728 | #if defined(QCACHE) | | 740 | #if defined(QCACHE) |
729 | qc_destroy(vm); | | 741 | qc_destroy(vm); |
730 | #endif /* defined(QCACHE) */ | | 742 | #endif /* defined(QCACHE) */ |
731 | if (vm->vm_hashlist != NULL) { | | 743 | VMEM_LOCK(vm); |
732 | int i; | | | |
733 | | | 744 | |
734 | for (i = 0; i < vm->vm_hashsize; i++) { | | 745 | for (int i = 0; i < vm->vm_hashsize; i++) { |
735 | bt_t *bt; | | 746 | bt_t *bt; |
736 | | | 747 | |
737 | while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) { | | 748 | while ((bt = LIST_FIRST(&vm->vm_hashlist[i])) != NULL) { |
738 | KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC); | | 749 | KASSERT(bt->bt_type == BT_TYPE_SPAN_STATIC); |
739 | bt_free(vm, bt); | | 750 | LIST_REMOVE(bt, bt_hashlist); |
740 | } | | 751 | bt_free(vm, bt); |
741 | } | | | |
742 | if (vm->vm_hashlist != &vm->vm_hash0) { | | | |
743 | xfree(vm->vm_hashlist, | | | |
744 | sizeof(struct vmem_hashlist *) * vm->vm_hashsize); | | | |
745 | } | | 752 | } |
746 | } | | 753 | } |
747 | | | 754 | |
| | | 755 | /* bt_freetrim() drops the lock. */ |
748 | bt_freetrim(vm, 0); | | 756 | bt_freetrim(vm, 0); |
| | | 757 | if (vm->vm_hashlist != &vm->vm_hash0) { |
| | | 758 | xfree(vm->vm_hashlist, |
| | | 759 | sizeof(struct vmem_hashlist) * vm->vm_hashsize); |
| | | 760 | } |
749 | | | 761 | |
750 | VMEM_CONDVAR_DESTROY(vm); | | 762 | VMEM_CONDVAR_DESTROY(vm); |
751 | VMEM_LOCK_DESTROY(vm); | | 763 | VMEM_LOCK_DESTROY(vm); |
752 | xfree(vm, sizeof(*vm)); | | 764 | xfree(vm, sizeof(*vm)); |
753 | } | | 765 | } |
754 | | | 766 | |
755 | static int | | 767 | static int |
756 | vmem_import(vmem_t *vm, vmem_size_t size, vm_flag_t flags) | | 768 | vmem_import(vmem_t *vm, vmem_size_t size, vm_flag_t flags) |
757 | { | | 769 | { |
758 | vmem_addr_t addr; | | 770 | vmem_addr_t addr; |
759 | int rc; | | 771 | int rc; |
760 | | | 772 | |
| | | 773 | VMEM_ASSERT_LOCKED(vm); |
| | | 774 | |
761 | if (vm->vm_importfn == NULL) { | | 775 | if (vm->vm_importfn == NULL) { |
762 | return EINVAL; | | 776 | return EINVAL; |
763 | } | | 777 | } |
764 | | | 778 | |
765 | if (vm->vm_flags & VM_LARGEIMPORT) { | | 779 | if (vm->vm_flags & VM_LARGEIMPORT) { |
766 | size *= 16; | | 780 | size *= 16; |
767 | } | | 781 | } |
768 | | | 782 | |
| | | 783 | VMEM_UNLOCK(vm); |
769 | if (vm->vm_flags & VM_XIMPORT) { | | 784 | if (vm->vm_flags & VM_XIMPORT) { |
770 | rc = __FPTRCAST(vmem_ximport_t *, vm->vm_importfn)(vm->vm_arg, | | 785 | rc = __FPTRCAST(vmem_ximport_t *, vm->vm_importfn)(vm->vm_arg, |
771 | size, &size, flags, &addr); | | 786 | size, &size, flags, &addr); |
772 | } else { | | 787 | } else { |
773 | rc = (vm->vm_importfn)(vm->vm_arg, size, flags, &addr); | | 788 | rc = (vm->vm_importfn)(vm->vm_arg, size, flags, &addr); |
774 | } | | 789 | } |
| | | 790 | VMEM_LOCK(vm); |
| | | 791 | |
775 | if (rc) { | | 792 | if (rc) { |
776 | return ENOMEM; | | 793 | return ENOMEM; |
777 | } | | 794 | } |
778 | | | 795 | |
779 | if (vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN) != 0) { | | 796 | if (vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN) != 0) { |
| | | 797 | VMEM_UNLOCK(vm); |
780 | (*vm->vm_releasefn)(vm->vm_arg, addr, size); | | 798 | (*vm->vm_releasefn)(vm->vm_arg, addr, size); |
| | | 799 | VMEM_LOCK(vm); |
781 | return ENOMEM; | | 800 | return ENOMEM; |
782 | } | | 801 | } |
783 | | | 802 | |
784 | return 0; | | 803 | return 0; |
785 | } | | 804 | } |
786 | | | 805 | |
787 | static int | | 806 | static int |
788 | vmem_rehash(vmem_t *vm, size_t newhashsize, vm_flag_t flags) | | 807 | vmem_rehash(vmem_t *vm, size_t newhashsize, vm_flag_t flags) |
789 | { | | 808 | { |
790 | bt_t *bt; | | 809 | bt_t *bt; |
791 | int i; | | 810 | int i; |
792 | struct vmem_hashlist *newhashlist; | | 811 | struct vmem_hashlist *newhashlist; |
793 | struct vmem_hashlist *oldhashlist; | | 812 | struct vmem_hashlist *oldhashlist; |
794 | size_t oldhashsize; | | 813 | size_t oldhashsize; |
795 | | | 814 | |
796 | KASSERT(newhashsize > 0); | | 815 | KASSERT(newhashsize > 0); |
797 | | | 816 | |
| | | 817 | /* Round hash size up to a power of 2. */ |
| | | 818 | newhashsize = 1 << (ilog2(newhashsize) + 1); |
| | | 819 | |
798 | newhashlist = | | 820 | newhashlist = |
799 | xmalloc(sizeof(struct vmem_hashlist *) * newhashsize, flags); | | 821 | xmalloc(sizeof(struct vmem_hashlist) * newhashsize, flags); |
800 | if (newhashlist == NULL) { | | 822 | if (newhashlist == NULL) { |
801 | return ENOMEM; | | 823 | return ENOMEM; |
802 | } | | 824 | } |
803 | for (i = 0; i < newhashsize; i++) { | | 825 | for (i = 0; i < newhashsize; i++) { |
804 | LIST_INIT(&newhashlist[i]); | | 826 | LIST_INIT(&newhashlist[i]); |
805 | } | | 827 | } |
806 | | | 828 | |
807 | if (!VMEM_TRYLOCK(vm)) { | | 829 | VMEM_LOCK(vm); |
808 | xfree(newhashlist, | | 830 | /* Decay back to a small hash slowly. */ |
809 | sizeof(struct vmem_hashlist *) * newhashsize); | | 831 | if (vm->vm_maxbusytag >= 2) { |
810 | return EBUSY; | | 832 | vm->vm_maxbusytag = vm->vm_maxbusytag / 2 - 1; |
| | | 833 | if (vm->vm_nbusytag > vm->vm_maxbusytag) { |
| | | 834 | vm->vm_maxbusytag = vm->vm_nbusytag; |
| | | 835 | } |
| | | 836 | } else { |
| | | 837 | vm->vm_maxbusytag = vm->vm_nbusytag; |
811 | } | | 838 | } |
812 | oldhashlist = vm->vm_hashlist; | | 839 | oldhashlist = vm->vm_hashlist; |
813 | oldhashsize = vm->vm_hashsize; | | 840 | oldhashsize = vm->vm_hashsize; |
814 | vm->vm_hashlist = newhashlist; | | 841 | vm->vm_hashlist = newhashlist; |
815 | vm->vm_hashsize = newhashsize; | | 842 | vm->vm_hashsize = newhashsize; |
| | | 843 | vm->vm_hashmask = newhashsize - 1; |
816 | if (oldhashlist == NULL) { | | 844 | if (oldhashlist == NULL) { |
817 | VMEM_UNLOCK(vm); | | 845 | VMEM_UNLOCK(vm); |
818 | return 0; | | 846 | return 0; |
819 | } | | 847 | } |
820 | for (i = 0; i < oldhashsize; i++) { | | 848 | for (i = 0; i < oldhashsize; i++) { |
821 | while ((bt = LIST_FIRST(&oldhashlist[i])) != NULL) { | | 849 | while ((bt = LIST_FIRST(&oldhashlist[i])) != NULL) { |
822 | bt_rembusy(vm, bt); /* XXX */ | | 850 | bt_rembusy(vm, bt); /* XXX */ |
823 | bt_insbusy(vm, bt); | | 851 | bt_insbusy(vm, bt); |
824 | } | | 852 | } |
825 | } | | 853 | } |
826 | VMEM_UNLOCK(vm); | | 854 | VMEM_UNLOCK(vm); |
827 | | | 855 | |
828 | if (oldhashlist != &vm->vm_hash0) { | | 856 | if (oldhashlist != &vm->vm_hash0) { |
829 | xfree(oldhashlist, | | 857 | xfree(oldhashlist, |
830 | sizeof(struct vmem_hashlist *) * oldhashsize); | | 858 | sizeof(struct vmem_hashlist) * oldhashsize); |
831 | } | | 859 | } |
832 | | | 860 | |
833 | return 0; | | 861 | return 0; |
834 | } | | 862 | } |
835 | | | 863 | |
836 | /* | | 864 | /* |
837 | * vmem_fit: check if a bt can satisfy the given restrictions. | | 865 | * vmem_fit: check if a bt can satisfy the given restrictions. |
838 | * | | 866 | * |
839 | * it's a caller's responsibility to ensure the region is big enough | | 867 | * it's a caller's responsibility to ensure the region is big enough |
840 | * before calling us. | | 868 | * before calling us. |
841 | */ | | 869 | */ |
842 | | | 870 | |
843 | static int | | 871 | static int |
| @@ -924,38 +952,40 @@ vmem_init(vmem_t *vm, const char *name, | | | @@ -924,38 +952,40 @@ vmem_init(vmem_t *vm, const char *name, |
924 | VMEM_CONDVAR_INIT(vm, "vmem"); | | 952 | VMEM_CONDVAR_INIT(vm, "vmem"); |
925 | VMEM_LOCK_INIT(vm, ipl); | | 953 | VMEM_LOCK_INIT(vm, ipl); |
926 | vm->vm_flags = flags; | | 954 | vm->vm_flags = flags; |
927 | vm->vm_nfreetags = 0; | | 955 | vm->vm_nfreetags = 0; |
928 | LIST_INIT(&vm->vm_freetags); | | 956 | LIST_INIT(&vm->vm_freetags); |
929 | strlcpy(vm->vm_name, name, sizeof(vm->vm_name)); | | 957 | strlcpy(vm->vm_name, name, sizeof(vm->vm_name)); |
930 | vm->vm_quantum_mask = quantum - 1; | | 958 | vm->vm_quantum_mask = quantum - 1; |
931 | vm->vm_quantum_shift = SIZE2ORDER(quantum); | | 959 | vm->vm_quantum_shift = SIZE2ORDER(quantum); |
932 | KASSERT(ORDER2SIZE(vm->vm_quantum_shift) == quantum); | | 960 | KASSERT(ORDER2SIZE(vm->vm_quantum_shift) == quantum); |
933 | vm->vm_importfn = importfn; | | 961 | vm->vm_importfn = importfn; |
934 | vm->vm_releasefn = releasefn; | | 962 | vm->vm_releasefn = releasefn; |
935 | vm->vm_arg = arg; | | 963 | vm->vm_arg = arg; |
936 | vm->vm_nbusytag = 0; | | 964 | vm->vm_nbusytag = 0; |
| | | 965 | vm->vm_maxbusytag = 0; |
937 | vm->vm_size = 0; | | 966 | vm->vm_size = 0; |
938 | vm->vm_inuse = 0; | | 967 | vm->vm_inuse = 0; |
939 | #if defined(QCACHE) | | 968 | #if defined(QCACHE) |
940 | qc_init(vm, qcache_max, ipl); | | 969 | qc_init(vm, qcache_max, ipl); |
941 | #endif /* defined(QCACHE) */ | | 970 | #endif /* defined(QCACHE) */ |
942 | | | 971 | |
943 | TAILQ_INIT(&vm->vm_seglist); | | 972 | TAILQ_INIT(&vm->vm_seglist); |
944 | for (i = 0; i < VMEM_MAXORDER; i++) { | | 973 | for (i = 0; i < VMEM_MAXORDER; i++) { |
945 | LIST_INIT(&vm->vm_freelist[i]); | | 974 | LIST_INIT(&vm->vm_freelist[i]); |
946 | } | | 975 | } |
947 | memset(&vm->vm_hash0, 0, sizeof(struct vmem_hashlist)); | | 976 | memset(&vm->vm_hash0, 0, sizeof(vm->vm_hash0)); |
948 | vm->vm_hashsize = 1; | | 977 | vm->vm_hashsize = 1; |
| | | 978 | vm->vm_hashmask = vm->vm_hashsize - 1; |
949 | vm->vm_hashlist = &vm->vm_hash0; | | 979 | vm->vm_hashlist = &vm->vm_hash0; |
950 | | | 980 | |
951 | if (size != 0) { | | 981 | if (size != 0) { |
952 | if (vmem_add(vm, base, size, flags) != 0) { | | 982 | if (vmem_add(vm, base, size, flags) != 0) { |
953 | vmem_destroy1(vm); | | 983 | vmem_destroy1(vm); |
954 | return NULL; | | 984 | return NULL; |
955 | } | | 985 | } |
956 | } | | 986 | } |
957 | | | 987 | |
958 | #if defined(_KERNEL) | | 988 | #if defined(_KERNEL) |
959 | if (flags & VM_BOOTSTRAP) { | | 989 | if (flags & VM_BOOTSTRAP) { |
960 | bt_refill(vm); | | 990 | bt_refill(vm); |
961 | } | | 991 | } |
| @@ -1097,45 +1127,47 @@ vmem_xalloc(vmem_t *vm, const vmem_size_ | | | @@ -1097,45 +1127,47 @@ vmem_xalloc(vmem_t *vm, const vmem_size_ |
1097 | KASSERT((nocross & (nocross - 1)) == 0); | | 1127 | KASSERT((nocross & (nocross - 1)) == 0); |
1098 | KASSERT((align == 0 && phase == 0) || phase < align); | | 1128 | KASSERT((align == 0 && phase == 0) || phase < align); |
1099 | KASSERT(nocross == 0 || nocross >= size); | | 1129 | KASSERT(nocross == 0 || nocross >= size); |
1100 | KASSERT(minaddr <= maxaddr); | | 1130 | KASSERT(minaddr <= maxaddr); |
1101 | KASSERT(!VMEM_CROSS_P(phase, phase + size - 1, nocross)); | | 1131 | KASSERT(!VMEM_CROSS_P(phase, phase + size - 1, nocross)); |
1102 | | | 1132 | |
1103 | if (align == 0) { | | 1133 | if (align == 0) { |
1104 | align = vm->vm_quantum_mask + 1; | | 1134 | align = vm->vm_quantum_mask + 1; |
1105 | } | | 1135 | } |
1106 | | | 1136 | |
1107 | /* | | 1137 | /* |
1108 | * allocate boundary tags before acquiring the vmem lock. | | 1138 | * allocate boundary tags before acquiring the vmem lock. |
1109 | */ | | 1139 | */ |
| | | 1140 | VMEM_LOCK(vm); |
1110 | btnew = bt_alloc(vm, flags); | | 1141 | btnew = bt_alloc(vm, flags); |
1111 | if (btnew == NULL) { | | 1142 | if (btnew == NULL) { |
| | | 1143 | VMEM_UNLOCK(vm); |
1112 | return ENOMEM; | | 1144 | return ENOMEM; |
1113 | } | | 1145 | } |
1114 | btnew2 = bt_alloc(vm, flags); /* XXX not necessary if no restrictions */ | | 1146 | btnew2 = bt_alloc(vm, flags); /* XXX not necessary if no restrictions */ |
1115 | if (btnew2 == NULL) { | | 1147 | if (btnew2 == NULL) { |
1116 | bt_free(vm, btnew); | | 1148 | bt_free(vm, btnew); |
| | | 1149 | VMEM_UNLOCK(vm); |
1117 | return ENOMEM; | | 1150 | return ENOMEM; |
1118 | } | | 1151 | } |
1119 | | | 1152 | |
1120 | /* | | 1153 | /* |
1121 | * choose a free block from which we allocate. | | 1154 | * choose a free block from which we allocate. |
1122 | */ | | 1155 | */ |
1123 | retry_strat: | | 1156 | retry_strat: |
1124 | first = bt_freehead_toalloc(vm, size, strat); | | 1157 | first = bt_freehead_toalloc(vm, size, strat); |
1125 | end = &vm->vm_freelist[VMEM_MAXORDER]; | | 1158 | end = &vm->vm_freelist[VMEM_MAXORDER]; |
1126 | retry: | | 1159 | retry: |
1127 | bt = NULL; | | 1160 | bt = NULL; |
1128 | VMEM_LOCK(vm); | | | |
1129 | vmem_check(vm); | | 1161 | vmem_check(vm); |
1130 | if (strat == VM_INSTANTFIT) { | | 1162 | if (strat == VM_INSTANTFIT) { |
1131 | /* | | 1163 | /* |
1132 | * just choose the first block which satisfies our restrictions. | | 1164 | * just choose the first block which satisfies our restrictions. |
1133 | * | | 1165 | * |
1134 | * note that we don't need to check the size of the blocks | | 1166 | * note that we don't need to check the size of the blocks |
1135 | * because any blocks found on these list should be larger than | | 1167 | * because any blocks found on these list should be larger than |
1136 | * the given size. | | 1168 | * the given size. |
1137 | */ | | 1169 | */ |
1138 | for (list = first; list < end; list++) { | | 1170 | for (list = first; list < end; list++) { |
1139 | bt = LIST_FIRST(list); | | 1171 | bt = LIST_FIRST(list); |
1140 | if (bt != NULL) { | | 1172 | if (bt != NULL) { |
1141 | rc = vmem_fit(bt, size, align, phase, | | 1173 | rc = vmem_fit(bt, size, align, phase, |
| @@ -1166,58 +1198,56 @@ retry: | | | @@ -1166,58 +1198,56 @@ retry: |
1166 | */ | | 1198 | */ |
1167 | for (list = first; list < end; list++) { | | 1199 | for (list = first; list < end; list++) { |
1168 | LIST_FOREACH(bt, list, bt_freelist) { | | 1200 | LIST_FOREACH(bt, list, bt_freelist) { |
1169 | if (bt->bt_size >= size) { | | 1201 | if (bt->bt_size >= size) { |
1170 | rc = vmem_fit(bt, size, align, phase, | | 1202 | rc = vmem_fit(bt, size, align, phase, |
1171 | nocross, minaddr, maxaddr, &start); | | 1203 | nocross, minaddr, maxaddr, &start); |
1172 | if (rc == 0) { | | 1204 | if (rc == 0) { |
1173 | goto gotit; | | 1205 | goto gotit; |
1174 | } | | 1206 | } |
1175 | } | | 1207 | } |
1176 | } | | 1208 | } |
1177 | } | | 1209 | } |
1178 | } | | 1210 | } |
1179 | VMEM_UNLOCK(vm); | | | |
1180 | #if 1 | | 1211 | #if 1 |
1181 | if (strat == VM_INSTANTFIT) { | | 1212 | if (strat == VM_INSTANTFIT) { |
1182 | strat = VM_BESTFIT; | | 1213 | strat = VM_BESTFIT; |
1183 | goto retry_strat; | | 1214 | goto retry_strat; |
1184 | } | | 1215 | } |
1185 | #endif | | 1216 | #endif |
1186 | if (align != vm->vm_quantum_mask + 1 || phase != 0 || nocross != 0) { | | 1217 | if (align != vm->vm_quantum_mask + 1 || phase != 0 || nocross != 0) { |
1187 | | | 1218 | |
1188 | /* | | 1219 | /* |
1189 | * XXX should try to import a region large enough to | | 1220 | * XXX should try to import a region large enough to |
1190 | * satisfy restrictions? | | 1221 | * satisfy restrictions? |
1191 | */ | | 1222 | */ |
1192 | | | 1223 | |
1193 | goto fail; | | 1224 | goto fail; |
1194 | } | | 1225 | } |
1195 | /* XXX eeek, minaddr & maxaddr not respected */ | | 1226 | /* XXX eeek, minaddr & maxaddr not respected */ |
1196 | if (vmem_import(vm, size, flags) == 0) { | | 1227 | if (vmem_import(vm, size, flags) == 0) { |
1197 | goto retry; | | 1228 | goto retry; |
1198 | } | | 1229 | } |
1199 | /* XXX */ | | 1230 | /* XXX */ |
1200 | | | 1231 | |
1201 | if ((flags & VM_SLEEP) != 0) { | | 1232 | if ((flags & VM_SLEEP) != 0) { |
1202 | vmem_kick_pdaemon(); | | 1233 | vmem_kick_pdaemon(); |
1203 | VMEM_LOCK(vm); | | | |
1204 | VMEM_CONDVAR_WAIT(vm); | | 1234 | VMEM_CONDVAR_WAIT(vm); |
1205 | VMEM_UNLOCK(vm); | | | |
1206 | goto retry; | | 1235 | goto retry; |
1207 | } | | 1236 | } |
1208 | fail: | | 1237 | fail: |
1209 | bt_free(vm, btnew); | | 1238 | bt_free(vm, btnew); |
1210 | bt_free(vm, btnew2); | | 1239 | bt_free(vm, btnew2); |
| | | 1240 | VMEM_UNLOCK(vm); |
1211 | return ENOMEM; | | 1241 | return ENOMEM; |
1212 | | | 1242 | |
1213 | gotit: | | 1243 | gotit: |
1214 | KASSERT(bt->bt_type == BT_TYPE_FREE); | | 1244 | KASSERT(bt->bt_type == BT_TYPE_FREE); |
1215 | KASSERT(bt->bt_size >= size); | | 1245 | KASSERT(bt->bt_size >= size); |
1216 | bt_remfree(vm, bt); | | 1246 | bt_remfree(vm, bt); |
1217 | vmem_check(vm); | | 1247 | vmem_check(vm); |
1218 | if (bt->bt_start != start) { | | 1248 | if (bt->bt_start != start) { |
1219 | btnew2->bt_type = BT_TYPE_FREE; | | 1249 | btnew2->bt_type = BT_TYPE_FREE; |
1220 | btnew2->bt_start = bt->bt_start; | | 1250 | btnew2->bt_start = bt->bt_start; |
1221 | btnew2->bt_size = start - bt->bt_start; | | 1251 | btnew2->bt_size = start - bt->bt_start; |
1222 | bt->bt_start = start; | | 1252 | bt->bt_start = start; |
1223 | bt->bt_size -= btnew2->bt_size; | | 1253 | bt->bt_size -= btnew2->bt_size; |
| @@ -1228,43 +1258,41 @@ gotit: | | | @@ -1228,43 +1258,41 @@ gotit: |
1228 | } | | 1258 | } |
1229 | KASSERT(bt->bt_start == start); | | 1259 | KASSERT(bt->bt_start == start); |
1230 | if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) { | | 1260 | if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) { |
1231 | /* split */ | | 1261 | /* split */ |
1232 | btnew->bt_type = BT_TYPE_BUSY; | | 1262 | btnew->bt_type = BT_TYPE_BUSY; |
1233 | btnew->bt_start = bt->bt_start; | | 1263 | btnew->bt_start = bt->bt_start; |
1234 | btnew->bt_size = size; | | 1264 | btnew->bt_size = size; |
1235 | bt->bt_start = bt->bt_start + size; | | 1265 | bt->bt_start = bt->bt_start + size; |
1236 | bt->bt_size -= size; | | 1266 | bt->bt_size -= size; |
1237 | bt_insfree(vm, bt); | | 1267 | bt_insfree(vm, bt); |
1238 | bt_insseg(vm, btnew, TAILQ_PREV(bt, vmem_seglist, bt_seglist)); | | 1268 | bt_insseg(vm, btnew, TAILQ_PREV(bt, vmem_seglist, bt_seglist)); |
1239 | bt_insbusy(vm, btnew); | | 1269 | bt_insbusy(vm, btnew); |
1240 | vmem_check(vm); | | 1270 | vmem_check(vm); |
1241 | VMEM_UNLOCK(vm); | | | |
1242 | } else { | | 1271 | } else { |
1243 | bt->bt_type = BT_TYPE_BUSY; | | 1272 | bt->bt_type = BT_TYPE_BUSY; |
1244 | bt_insbusy(vm, bt); | | 1273 | bt_insbusy(vm, bt); |
1245 | vmem_check(vm); | | 1274 | vmem_check(vm); |
1246 | VMEM_UNLOCK(vm); | | | |
1247 | bt_free(vm, btnew); | | 1275 | bt_free(vm, btnew); |
1248 | btnew = bt; | | 1276 | btnew = bt; |
1249 | } | | 1277 | } |
1250 | if (btnew2 != NULL) { | | 1278 | if (btnew2 != NULL) { |
1251 | bt_free(vm, btnew2); | | 1279 | bt_free(vm, btnew2); |
1252 | } | | 1280 | } |
1253 | KASSERT(btnew->bt_size >= size); | | 1281 | KASSERT(btnew->bt_size >= size); |
1254 | btnew->bt_type = BT_TYPE_BUSY; | | 1282 | btnew->bt_type = BT_TYPE_BUSY; |
1255 | | | | |
1256 | if (addrp != NULL) | | 1283 | if (addrp != NULL) |
1257 | *addrp = btnew->bt_start; | | 1284 | *addrp = btnew->bt_start; |
| | | 1285 | VMEM_UNLOCK(vm); |
1258 | return 0; | | 1286 | return 0; |
1259 | } | | 1287 | } |
1260 | | | 1288 | |
1261 | /* | | 1289 | /* |
1262 | * vmem_free: free the resource to the arena. | | 1290 | * vmem_free: free the resource to the arena. |
1263 | */ | | 1291 | */ |
1264 | | | 1292 | |
1265 | void | | 1293 | void |
1266 | vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) | | 1294 | vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) |
1267 | { | | 1295 | { |
1268 | | | 1296 | |
1269 | KASSERT(size > 0); | | 1297 | KASSERT(size > 0); |
1270 | | | 1298 | |
| @@ -1276,108 +1304,104 @@ vmem_free(vmem_t *vm, vmem_addr_t addr, | | | @@ -1276,108 +1304,104 @@ vmem_free(vmem_t *vm, vmem_addr_t addr, |
1276 | pool_cache_put(qc->qc_cache, (void *)addr); | | 1304 | pool_cache_put(qc->qc_cache, (void *)addr); |
1277 | return; | | 1305 | return; |
1278 | } | | 1306 | } |
1279 | #endif /* defined(QCACHE) */ | | 1307 | #endif /* defined(QCACHE) */ |
1280 | | | 1308 | |
1281 | vmem_xfree(vm, addr, size); | | 1309 | vmem_xfree(vm, addr, size); |
1282 | } | | 1310 | } |
1283 | | | 1311 | |
1284 | void | | 1312 | void |
1285 | vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) | | 1313 | vmem_xfree(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) |
1286 | { | | 1314 | { |
1287 | bt_t *bt; | | 1315 | bt_t *bt; |
1288 | bt_t *t; | | 1316 | bt_t *t; |
1289 | LIST_HEAD(, vmem_btag) tofree; | | | |
1290 | | | | |
1291 | LIST_INIT(&tofree); | | | |
1292 | | | 1317 | |
1293 | KASSERT(size > 0); | | 1318 | KASSERT(size > 0); |
1294 | | | 1319 | |
1295 | VMEM_LOCK(vm); | | 1320 | VMEM_LOCK(vm); |
1296 | | | 1321 | |
1297 | bt = bt_lookupbusy(vm, addr); | | 1322 | bt = bt_lookupbusy(vm, addr); |
1298 | KASSERT(bt != NULL); | | 1323 | KASSERT(bt != NULL); |
1299 | KASSERT(bt->bt_start == addr); | | 1324 | KASSERT(bt->bt_start == addr); |
1300 | KASSERT(bt->bt_size == vmem_roundup_size(vm, size) || | | 1325 | KASSERT(bt->bt_size == vmem_roundup_size(vm, size) || |
1301 | bt->bt_size - vmem_roundup_size(vm, size) <= vm->vm_quantum_mask); | | 1326 | bt->bt_size - vmem_roundup_size(vm, size) <= vm->vm_quantum_mask); |
1302 | KASSERT(bt->bt_type == BT_TYPE_BUSY); | | 1327 | KASSERT(bt->bt_type == BT_TYPE_BUSY); |
1303 | bt_rembusy(vm, bt); | | 1328 | bt_rembusy(vm, bt); |
1304 | bt->bt_type = BT_TYPE_FREE; | | 1329 | bt->bt_type = BT_TYPE_FREE; |
1305 | | | 1330 | |
1306 | /* coalesce */ | | 1331 | /* coalesce */ |
1307 | t = TAILQ_NEXT(bt, bt_seglist); | | 1332 | t = TAILQ_NEXT(bt, bt_seglist); |
1308 | if (t != NULL && t->bt_type == BT_TYPE_FREE) { | | 1333 | if (t != NULL && t->bt_type == BT_TYPE_FREE) { |
1309 | KASSERT(BT_END(bt) < t->bt_start); /* YYY */ | | 1334 | KASSERT(BT_END(bt) < t->bt_start); /* YYY */ |
1310 | bt_remfree(vm, t); | | 1335 | bt_remfree(vm, t); |
1311 | bt_remseg(vm, t); | | 1336 | bt_remseg(vm, t); |
1312 | bt->bt_size += t->bt_size; | | 1337 | bt->bt_size += t->bt_size; |
1313 | LIST_INSERT_HEAD(&tofree, t, bt_freelist); | | 1338 | bt_free(vm, t); |
1314 | } | | 1339 | } |
1315 | t = TAILQ_PREV(bt, vmem_seglist, bt_seglist); | | 1340 | t = TAILQ_PREV(bt, vmem_seglist, bt_seglist); |
1316 | if (t != NULL && t->bt_type == BT_TYPE_FREE) { | | 1341 | if (t != NULL && t->bt_type == BT_TYPE_FREE) { |
1317 | KASSERT(BT_END(t) < bt->bt_start); /* YYY */ | | 1342 | KASSERT(BT_END(t) < bt->bt_start); /* YYY */ |
1318 | bt_remfree(vm, t); | | 1343 | bt_remfree(vm, t); |
1319 | bt_remseg(vm, t); | | 1344 | bt_remseg(vm, t); |
1320 | bt->bt_size += t->bt_size; | | 1345 | bt->bt_size += t->bt_size; |
1321 | bt->bt_start = t->bt_start; | | 1346 | bt->bt_start = t->bt_start; |
1322 | LIST_INSERT_HEAD(&tofree, t, bt_freelist); | | 1347 | bt_free(vm, t); |
1323 | } | | 1348 | } |
1324 | | | 1349 | |
1325 | t = TAILQ_PREV(bt, vmem_seglist, bt_seglist); | | 1350 | t = TAILQ_PREV(bt, vmem_seglist, bt_seglist); |
1326 | KASSERT(t != NULL); | | 1351 | KASSERT(t != NULL); |
1327 | KASSERT(BT_ISSPAN_P(t) || t->bt_type == BT_TYPE_BUSY); | | 1352 | KASSERT(BT_ISSPAN_P(t) || t->bt_type == BT_TYPE_BUSY); |
1328 | if (vm->vm_releasefn != NULL && t->bt_type == BT_TYPE_SPAN && | | 1353 | if (vm->vm_releasefn != NULL && t->bt_type == BT_TYPE_SPAN && |
1329 | t->bt_size == bt->bt_size) { | | 1354 | t->bt_size == bt->bt_size) { |
1330 | vmem_addr_t spanaddr; | | 1355 | vmem_addr_t spanaddr; |
1331 | vmem_size_t spansize; | | 1356 | vmem_size_t spansize; |
1332 | | | 1357 | |
1333 | KASSERT(t->bt_start == bt->bt_start); | | 1358 | KASSERT(t->bt_start == bt->bt_start); |
1334 | spanaddr = bt->bt_start; | | 1359 | spanaddr = bt->bt_start; |
1335 | spansize = bt->bt_size; | | 1360 | spansize = bt->bt_size; |
1336 | bt_remseg(vm, bt); | | 1361 | bt_remseg(vm, bt); |
1337 | LIST_INSERT_HEAD(&tofree, bt, bt_freelist); | | 1362 | bt_free(vm, bt); |
1338 | bt_remseg(vm, t); | | 1363 | bt_remseg(vm, t); |
1339 | LIST_INSERT_HEAD(&tofree, t, bt_freelist); | | 1364 | bt_free(vm, t); |
1340 | vm->vm_size -= spansize; | | 1365 | vm->vm_size -= spansize; |
1341 | VMEM_CONDVAR_BROADCAST(vm); | | 1366 | VMEM_CONDVAR_BROADCAST(vm); |
1342 | VMEM_UNLOCK(vm); | | 1367 | /* bt_freetrim() drops the lock. */ |
| | | 1368 | bt_freetrim(vm, BT_MAXFREE); |
1343 | (*vm->vm_releasefn)(vm->vm_arg, spanaddr, spansize); | | 1369 | (*vm->vm_releasefn)(vm->vm_arg, spanaddr, spansize); |
1344 | } else { | | 1370 | } else { |
1345 | bt_insfree(vm, bt); | | 1371 | bt_insfree(vm, bt); |
1346 | VMEM_CONDVAR_BROADCAST(vm); | | 1372 | VMEM_CONDVAR_BROADCAST(vm); |
1347 | VMEM_UNLOCK(vm); | | 1373 | /* bt_freetrim() drops the lock. */ |
| | | 1374 | bt_freetrim(vm, BT_MAXFREE); |
1348 | } | | 1375 | } |
1349 | | | | |
1350 | while (!LIST_EMPTY(&tofree)) { | | | |
1351 | t = LIST_FIRST(&tofree); | | | |
1352 | LIST_REMOVE(t, bt_freelist); | | | |
1353 | bt_free(vm, t); | | | |
1354 | } | | | |
1355 | | | | |
1356 | bt_freetrim(vm, BT_MAXFREE); | | | |
1357 | } | | 1376 | } |
1358 | | | 1377 | |
1359 | /* | | 1378 | /* |
1360 | * vmem_add: | | 1379 | * vmem_add: |
1361 | * | | 1380 | * |
1362 | * => caller must ensure appropriate spl, | | 1381 | * => caller must ensure appropriate spl, |
1363 | * if the arena can be accessed from interrupt context. | | 1382 | * if the arena can be accessed from interrupt context. |
1364 | */ | | 1383 | */ |
1365 | | | 1384 | |
1366 | int | | 1385 | int |
1367 | vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags) | | 1386 | vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size, vm_flag_t flags) |
1368 | { | | 1387 | { |
| | | 1388 | int rv; |
1369 | | | 1389 | |
1370 | return vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN_STATIC); | | 1390 | VMEM_LOCK(vm); |
| | | 1391 | rv = vmem_add1(vm, addr, size, flags, BT_TYPE_SPAN_STATIC); |
| | | 1392 | VMEM_UNLOCK(vm); |
| | | 1393 | |
| | | 1394 | return rv; |
1371 | } | | 1395 | } |
1372 | | | 1396 | |
1373 | /* | | 1397 | /* |
1374 | * vmem_size: information about arenas size | | 1398 | * vmem_size: information about arenas size |
1375 | * | | 1399 | * |
1376 | * => return free/allocated size in arena | | 1400 | * => return free/allocated size in arena |
1377 | */ | | 1401 | */ |
1378 | vmem_size_t | | 1402 | vmem_size_t |
1379 | vmem_size(vmem_t *vm, int typemask) | | 1403 | vmem_size(vmem_t *vm, int typemask) |
1380 | { | | 1404 | { |
1381 | | | 1405 | |
1382 | switch (typemask) { | | 1406 | switch (typemask) { |
1383 | case VMEM_ALLOC: | | 1407 | case VMEM_ALLOC: |
| @@ -1400,32 +1424,28 @@ static struct workqueue *vmem_rehash_wq; | | | @@ -1400,32 +1424,28 @@ static struct workqueue *vmem_rehash_wq; |
1400 | static struct work vmem_rehash_wk; | | 1424 | static struct work vmem_rehash_wk; |
1401 | | | 1425 | |
1402 | static void | | 1426 | static void |
1403 | vmem_rehash_all(struct work *wk, void *dummy) | | 1427 | vmem_rehash_all(struct work *wk, void *dummy) |
1404 | { | | 1428 | { |
1405 | vmem_t *vm; | | 1429 | vmem_t *vm; |
1406 | | | 1430 | |
1407 | KASSERT(wk == &vmem_rehash_wk); | | 1431 | KASSERT(wk == &vmem_rehash_wk); |
1408 | mutex_enter(&vmem_list_lock); | | 1432 | mutex_enter(&vmem_list_lock); |
1409 | LIST_FOREACH(vm, &vmem_list, vm_alllist) { | | 1433 | LIST_FOREACH(vm, &vmem_list, vm_alllist) { |
1410 | size_t desired; | | 1434 | size_t desired; |
1411 | size_t current; | | 1435 | size_t current; |
1412 | | | 1436 | |
1413 | if (!VMEM_TRYLOCK(vm)) { | | 1437 | desired = atomic_load_relaxed(&vm->vm_maxbusytag); |
1414 | continue; | | 1438 | current = atomic_load_relaxed(&vm->vm_hashsize); |
1415 | } | | | |
1416 | desired = vm->vm_nbusytag; | | | |
1417 | current = vm->vm_hashsize; | | | |
1418 | VMEM_UNLOCK(vm); | | | |
1419 | | | 1439 | |
1420 | if (desired > VMEM_HASHSIZE_MAX) { | | 1440 | if (desired > VMEM_HASHSIZE_MAX) { |
1421 | desired = VMEM_HASHSIZE_MAX; | | 1441 | desired = VMEM_HASHSIZE_MAX; |
1422 | } else if (desired < VMEM_HASHSIZE_MIN) { | | 1442 | } else if (desired < VMEM_HASHSIZE_MIN) { |
1423 | desired = VMEM_HASHSIZE_MIN; | | 1443 | desired = VMEM_HASHSIZE_MIN; |
1424 | } | | 1444 | } |
1425 | if (desired > current * 2 || desired * 2 < current) { | | 1445 | if (desired > current * 2 || desired * 2 < current) { |
1426 | vmem_rehash(vm, desired, VM_NOSLEEP); | | 1446 | vmem_rehash(vm, desired, VM_NOSLEEP); |
1427 | } | | 1447 | } |
1428 | } | | 1448 | } |
1429 | mutex_exit(&vmem_list_lock); | | 1449 | mutex_exit(&vmem_list_lock); |
1430 | | | 1450 | |
1431 | callout_schedule(&vmem_rehash_ch, vmem_rehash_interval); | | 1451 | callout_schedule(&vmem_rehash_ch, vmem_rehash_interval); |