]> git.saurik.com Git - apple/xnu.git/blame - osfmk/vm/bsd_vm.c
xnu-124.1.tar.gz
[apple/xnu.git] / osfmk / vm / bsd_vm.c
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <sys/errno.h>
24#include <kern/host.h>
25#include <mach/mach_types.h>
26#include <vm/vm_map.h>
27#include <vm/vm_kern.h>
28#include <vm/vm_pageout.h>
29#include <mach/kern_return.h>
30#include <mach/memory_object_types.h>
31#include <mach/port.h>
32#include <mach/policy.h>
33#include <ipc/ipc_port.h>
34#include <ipc/ipc_space.h>
35#include <kern/thread.h>
36#include <vm/vm_pageout.h>
37
38
39/* BSD VM COMPONENT INTERFACES */
40int
41get_map_nentries(
42 vm_map_t);
43
44vm_offset_t
45get_map_start(
46 vm_map_t);
47
48vm_offset_t
49get_map_end(
50 vm_map_t);
51
52/*
53 *
54 */
55int
56get_map_nentries(
57 vm_map_t map)
58{
59 return(map->hdr.nentries);
60}
61
62/*
63 *
64 */
65vm_offset_t
66get_map_start(
67 vm_map_t map)
68{
69 return(vm_map_first_entry(map)->vme_start);
70}
71
72/*
73 *
74 */
75vm_offset_t
76get_map_end(
77 vm_map_t map)
78{
79 return(vm_map_last_entry(map)->vme_end);
80}
81
82/*
83 * BSD VNODE PAGER
84 */
85
86/* until component support available */
87int vnode_pager_workaround;
88
89typedef int vnode_port_t;
90
91typedef struct vnode_pager {
92 ipc_port_t pager; /* pager */
93 ipc_port_t pager_handle; /* pager handle */
94 ipc_port_t vm_obj_handle; /* memory object's control handle */
95 vnode_port_t vnode_handle; /* vnode handle */
96} *vnode_pager_t;
97
98typedef struct vnode_port_entry {
99 queue_chain_t links; /* queue links */
100 ipc_port_t name; /* port name */
101 vnode_pager_t pager_rec; /* pager record */
102} *vnode_port_entry_t;
103
104
105#define VNODE_PORT_HASH_COUNT 127
106#define vnode_port_hash(name_port) \
107 (((int)(name_port) & 0xffffff) % VNODE_PORT_HASH_COUNT)
108
109queue_head_t vnode_port_hashtable[VNODE_PORT_HASH_COUNT];
110zone_t vnode_port_hash_zone;
111decl_simple_lock_data(,vnode_port_hash_lock)
112
113
114ipc_port_t
115trigger_name_to_port(
116 mach_port_t);
117
118void
119vnode_pager_bootstrap(
120 void);
121
122void
123vnode_pager_alloc_map(
124 void);
125
126ipc_port_t
127vnode_pager_setup(
128 vnode_port_t,
129 ipc_port_t);
130
131ipc_port_t
132vnode_pager_lookup(
133 vnode_port_t,
134 ipc_port_t);
135
136kern_return_t
137vnode_pager_init(
138 ipc_port_t,
139 ipc_port_t,
140 vm_size_t);
141
142kern_return_t
143vnode_pager_data_request(
144 ipc_port_t,
145 ipc_port_t,
146 vm_object_offset_t,
147 vm_size_t,
148 vm_prot_t);
149
150kern_return_t
151vnode_pager_data_return(
152 ipc_port_t,
153 ipc_port_t,
154 vm_object_offset_t,
155 pointer_t,
156 vm_size_t,
157 boolean_t,
158 boolean_t);
159
160void
161vnode_pager_no_senders(
162 ipc_port_t,
163 mach_port_mscount_t);
164
165kern_return_t
166vnode_pager_terminate(
167 ipc_port_t,
168 ipc_port_t);
169
170kern_return_t
171vnode_pager_cluster_read(
172 vnode_pager_t,
173 vm_object_offset_t,
174 vm_size_t);
175
176void
177vnode_pager_cluster_write(
178 vnode_pager_t,
179 vm_object_offset_t,
180 vm_size_t);
181
182kern_return_t
183memory_object_change_attributes(
184 vm_object_t,
185 memory_object_flavor_t,
186 memory_object_info_t,
187 mach_msg_type_number_t,
188 ipc_port_t,
189 mach_msg_type_name_t);
190
191int
192vnode_pagein(
193 vnode_port_t,
194 upl_t,
195 vm_offset_t,
196 vm_object_offset_t,
197 int,
198 int,
199 int *);
200int
201vnode_pageout(
202 vnode_port_t,
203 upl_t,
204 vm_offset_t,
205 vm_object_offset_t,
206 int,
207 int,
208 int *);
209
210vnode_pager_t
211vnode_object_create(
212 vnode_port_t vp);
213
214void
215vnode_port_hash_init(void);
216
217void
218vnode_port_hash_insert(
219 ipc_port_t,
220 vnode_pager_t);
221
222vnode_pager_t
223vnode_port_hash_lookup(
224 ipc_port_t);
225
226void
227vnode_port_hash_delete(
228 ipc_port_t);
229
230void
231vnode_pager_release_from_cache(
232 int *cnt);
233
234zone_t vnode_pager_zone;
235
236
237#define VNODE_PAGER_NULL ((vnode_pager_t) 0)
238
239/* TODO: Should be set dynamically by vnode_pager_init() */
240#define CLUSTER_SHIFT 1
241
242/* TODO: Should be set dynamically by vnode_pager_bootstrap() */
243#define MAX_VNODE 10000
244
245
246#if DEBUG
247int pagerdebug=0;
248
249#define PAGER_ALL 0xffffffff
250#define PAGER_INIT 0x00000001
251#define PAGER_PAGEIN 0x00000002
252
253#define PAGER_DEBUG(LEVEL, A) {if ((pagerdebug & LEVEL)==LEVEL){printf A;}}
254#else
255#define PAGER_DEBUG(LEVEL, A)
256#endif
257
258/*
259 * Routine: macx_triggers
260 * Function:
261 * Syscall interface to set the call backs for low and
262 * high water marks.
263 */
264int
265macx_triggers(
266 int hi_water,
267 int low_water,
268 int flags,
269 mach_port_t trigger_name)
270{
271 kern_return_t kr;
272 ipc_port_t default_pager_port = MACH_PORT_NULL;
273 ipc_port_t trigger_port;
274
275 kr = host_default_memory_manager(host_priv_self(),
276 &default_pager_port, 0);
277 if(kr != KERN_SUCCESS) {
278 return EINVAL;
279 }
280 trigger_port = trigger_name_to_port(trigger_name);
281 if(trigger_port == NULL) {
282 return EINVAL;
283 }
284 /* trigger_port is locked and active */
285 ip_unlock(trigger_port);
286 default_pager_triggers(default_pager_port,
287 hi_water, low_water, flags, trigger_port);
288 ipc_port_make_send(trigger_port);
289
290 /*
291 * Set thread scheduling priority and policy for the current thread
292 * it is assumed for the time being that the thread setting the alert
293 * is the same one which will be servicing it.
294 */
295 {
296 struct policy_timeshare_base fifo_base;
297 struct policy_timeshare_limit fifo_limit;
298 policy_base_t base;
299 processor_set_t pset;
300 policy_limit_t limit;
301
302 pset = (current_thread())->processor_set;
303 base = (policy_base_t) &fifo_base;
304 limit = (policy_limit_t) &fifo_limit;
305 fifo_limit.max_priority = fifo_base.base_priority = MAXPRI_STANDARD;
306 thread_set_policy((current_thread())->top_act, pset, POLICY_FIFO, base, POLICY_TIMESHARE_BASE_COUNT, limit, POLICY_TIMESHARE_LIMIT_COUNT);
307 }
308
309 current_thread()->vm_privilege = TRUE;
310}
311
312/*
313 *
314 */
315ipc_port_t
316trigger_name_to_port(
317 mach_port_t trigger_name)
318{
319 ipc_port_t trigger_port;
320 ipc_space_t space;
321
322 if (trigger_name == 0)
323 return (NULL);
324
325 space = current_space();
326 if(ipc_port_translate_receive(space, (mach_port_name_t)trigger_name,
327 &trigger_port) != KERN_SUCCESS)
328 return (NULL);
329 return trigger_port;
330}
331
332/*
333 *
334 */
335void
336vnode_pager_bootstrap(void)
337{
338 register vm_size_t size;
339
340 size = (vm_size_t) sizeof(struct vnode_pager);
341 vnode_pager_zone = zinit(size, (vm_size_t) MAX_VNODE*size,
342 PAGE_SIZE, "vnode pager structures");
343 vnode_port_hash_init();
344
345 return;
346}
347
348/*
349 *
350 */
351ipc_port_t
352vnode_pager_setup(
353 vnode_port_t vp,
354 ipc_port_t pager)
355{
356 vnode_pager_t vnode_object;
357 kern_return_t kr;
358 ipc_port_t previous;
359
360 if (pager &&
361 (vnode_object = vnode_port_hash_lookup(pager))) {
362 if (vnode_object->vnode_handle == vp)
363 return(pager);
364 }
365
366 vnode_object = vnode_object_create(vp);
367 if (vnode_object == VNODE_PAGER_NULL)
368 panic("vnode_pager_setup: vnode_object_create() failed");
369
370 vnode_object->pager = ipc_port_alloc_kernel();
371 assert (vnode_object->pager != IP_NULL);
372 pager_mux_hash_insert(vnode_object->pager,
373 (rpc_subsystem_t)&vnode_pager_workaround);
374
375 vnode_object->pager_handle = ipc_port_make_send(vnode_object->pager);
376
377 vnode_port_hash_insert(vnode_object->pager_handle, vnode_object);
378
379 ipc_port_make_sonce(vnode_object->pager);
380 ip_lock(vnode_object->pager); /* unlocked in nsrequest below */
381 ipc_port_nsrequest(vnode_object->pager, 1, vnode_object->pager, &previous);
382
383 PAGER_DEBUG(PAGER_INIT, ("vnode_pager_setup: vp %x pager %x vnode_pager %x\n", vp, vnode_object->pager_handle, vnode_object));
384
385 ubc_setpager( vp, vnode_object->pager_handle);
386 return(vnode_object->pager_handle);
387}
388
389/*
390 *
391 */
392ipc_port_t
393vnode_pager_lookup(
394 vnode_port_t vp,
395 ipc_port_t pager)
396{
397 vnode_pager_t vnode_object;
398 kern_return_t kr;
399
400 if (pager &&
401 (vnode_object = vnode_port_hash_lookup(pager))) {
402 if (vnode_object->vnode_handle == vp)
403 return(vnode_object->vm_obj_handle);
404 else
405 return NULL;
406 }
407 else
408 return NULL;
409}
410
411/*
412 *
413 */
414kern_return_t
415vnode_pager_init(ipc_port_t pager,
416 ipc_port_t pager_request,
417 vm_size_t pg_size)
418{
419 vnode_pager_t vnode_object;
420 kern_return_t kr;
421 memory_object_attr_info_data_t attributes;
422 vm_object_t vm_object;
423
424
425 PAGER_DEBUG(PAGER_ALL, ("vnode_pager_init: %x, %x, %x\n", pager, pager_request, pg_size));
426
427 vnode_object = vnode_port_hash_lookup(pager);
428 if (vnode_object == VNODE_PAGER_NULL)
429 panic("vnode_pager_init: lookup failed");
430
431 vnode_object->vm_obj_handle = pager_request;
432
433 vm_object = vm_object_lookup(pager_request);
434
435 if (vm_object == VM_OBJECT_NULL)
436 panic("vnode_pager_init: vm_object_lookup() failed");
437
438 attributes.copy_strategy = MEMORY_OBJECT_COPY_DELAY;
439 /* attributes.cluster_size = (1 << (CLUSTER_SHIFT + PAGE_SHIFT));*/
440 attributes.cluster_size = (1 << (PAGE_SHIFT));
441 attributes.may_cache_object = TRUE;
442 attributes.temporary = TRUE;
443
444 kr = memory_object_change_attributes(
445 vm_object,
446 MEMORY_OBJECT_ATTRIBUTE_INFO,
447 (memory_object_info_t) &attributes,
448 MEMORY_OBJECT_ATTR_INFO_COUNT,
449 MACH_PORT_NULL, 0);
450 if (kr != KERN_SUCCESS)
451 panic("vnode_pager_init: memory_object_change_attributes() failed");
452
453 return(KERN_SUCCESS);
454}
455
456/*
457 *
458 */
459kern_return_t
460vnode_pager_data_return(
461 ipc_port_t mem_obj,
462 ipc_port_t control_port,
463 vm_object_offset_t offset,
464 pointer_t addr,
465 vm_size_t data_cnt,
466 boolean_t dirty,
467 boolean_t kernel_copy)
468{
469 register vnode_pager_t vnode_object;
470
471 vnode_object = vnode_port_hash_lookup(mem_obj);
472 if (vnode_object == VNODE_PAGER_NULL)
473 panic("vnode_pager_data_return: lookup failed");
474
475 vnode_pager_cluster_write(vnode_object, offset, data_cnt);
476
477 return KERN_SUCCESS;
478}
479
480/*
481 *
482 */
483kern_return_t
484vnode_pager_data_request(
485 ipc_port_t mem_obj,
486 ipc_port_t mem_obj_control,
487 vm_object_offset_t offset,
488 vm_size_t length,
489 vm_prot_t protection_required)
490{
491 register vnode_pager_t vnode_object;
492
493 PAGER_DEBUG(PAGER_ALL, ("vnode_pager_data_request: %x, %x, %x, %x, %x\n", mem_obj, mem_obj_control, offset, length, protection_required));
494
495 vnode_object = vnode_port_hash_lookup(mem_obj);
496
497 PAGER_DEBUG(PAGER_PAGEIN, ("vnode_pager_data_request: %x, %x, %x, %x, %x, vnode_object %x\n", mem_obj, mem_obj_control, offset, length, protection_required, vnode_object));
498
499 if (vnode_object == VNODE_PAGER_NULL)
500 panic("vnode_pager_data_request: lookup failed");
501
502 vnode_pager_cluster_read(vnode_object, offset, length);
503
504 return KERN_SUCCESS;
505}
506
507/*
508 *
509 */
510void
511vnode_pager_no_senders(
512 ipc_port_t mem_obj,
513 mach_port_mscount_t mscount)
514{
515 register vnode_pager_t vnode_object;
516
517 PAGER_DEBUG(PAGER_ALL, ("vnode_pager_nosenders: %x, %x\n", mem_obj, mscount));
518
519 vnode_object = vnode_port_hash_lookup(mem_obj);
520 if (vnode_object == VNODE_PAGER_NULL)
521 panic("vnode_pager_no_senders: lookup failed");
522
523 assert(vnode_object->pager_handle == mem_obj);
524
525 pager_mux_hash_delete((ipc_port_t) vnode_object->pager_handle);
526 ipc_port_dealloc_kernel(vnode_object->pager);
527 vnode_port_hash_delete(vnode_object->pager_handle);
528 if (vnode_object->vnode_handle != (vnode_port_t) NULL) {
529 vnode_pager_vrele(vnode_object->vnode_handle);
530 }
531 zfree(vnode_pager_zone, (vm_offset_t) vnode_object);
532
533 return;
534}
535
536/*
537 *
538 */
539kern_return_t
540vnode_pager_terminate(
541 ipc_port_t mem_obj,
542 ipc_port_t mem_obj_control)
543{
544 register vnode_pager_t vnode_object;
545
546 PAGER_DEBUG(PAGER_ALL, ("vnode_pager_terminate: %x, %x\n", mem_obj, mem_obj_control));
547
548 vnode_object = vnode_port_hash_lookup(mem_obj);
549 if (vnode_object == VNODE_PAGER_NULL)
550 panic("vnode_pager_terminate: lookup failed");
551
552 assert(vnode_object->pager_handle == mem_obj);
553
554 /* release extra send right created by the fact that the caller */
555 /* of vnode_pager_setup does not establish a mapping between a */
556 /* cache object and the mem_obj (AMO). When a subsequent vm_map */
557 /* is done, vm_map will bump the send right count */
558 ipc_port_release_send(mem_obj);
559
560 /* release a send right because terminate is called directly and */
561 /* not through IPC, the right won't disappear quietly */
562 ipc_port_release_send(mem_obj);
563
564 ipc_port_dealloc_kernel(mem_obj_control);
565
566 return(KERN_SUCCESS);
567}
568
569/*
570 *
571 */
572kern_return_t
573vnode_pager_synchronize(
574 ipc_port_t pager,
575 ipc_port_t pager_request,
576 vm_object_offset_t offset,
577 vm_offset_t length,
578 vm_sync_t sync_flags)
579{
580 memory_object_synchronize_completed(vm_object_lookup(pager_request), offset, length);
581
582 return (KERN_SUCCESS);
583}
584
585/*
586 *
587 */
588void
589vnode_pager_cluster_write(
590 vnode_pager_t vnode_object,
591 vm_object_offset_t offset,
592 vm_size_t cnt)
593{
594 int error = 0;
595 int local_error = 0;
596 int kret;
597 int size;
598
599 if (cnt & PAGE_MASK) {
600 panic("vs_cluster_write: cnt not a multiple of PAGE_SIZE");
601 }
602 size = (cnt < (PAGE_SIZE*32)) ? cnt : (PAGE_SIZE*32); /* effective min */
603
604 while (cnt) {
605
606 kret = vnode_pageout(vnode_object->vnode_handle, (upl_t )NULL, (vm_offset_t)NULL, offset, size, 0, &local_error);
607
608 if (local_error != 0) {
609 error = local_error;
610 local_error = 0;
611 }
612 cnt -= size;
613 offset += size;
614 }
615#if 0
616 if (error != 0)
617 return(KERN_FAILURE);
618
619 return(KERN_SUCCESS);
620#endif /* 0 */
621}
622
623
624/*
625 *
626 */
627kern_return_t
628vnode_pager_cluster_read(
629 vnode_pager_t vnode_object,
630 vm_object_offset_t offset,
631 vm_size_t cnt)
632{
633 int error = 0;
634 int local_error = 0;
635 int kret;
636 int size;
637
638 if(cnt & PAGE_MASK) {
639 panic("vs_cluster_read: cnt not a multiple of PAGE_SIZE");
640 }
641
642 size = PAGE_SIZE;
643
644 while (cnt) {
645
646 kret = vnode_pagein(vnode_object->vnode_handle, (upl_t)NULL, (vm_offset_t)NULL, offset, size, 0, &local_error);
647
648 if (local_error != 0) {
649 error = local_error;
650 local_error = 0;
651 }
652 cnt -= size;
653 offset += size;
654 }
655 if (error != 0)
656 return(KERN_FAILURE);
657
658 return(KERN_SUCCESS);
659
660}
661
662
663/*
664 *
665 */
666void
667vnode_pager_release_from_cache(
668 int *cnt)
669{
670 memory_object_free_from_cache(
671 &realhost, (int)&vnode_pager_workaround, cnt);
672}
673
674/*
675 *
676 */
677vnode_pager_t
678vnode_object_create(
679 vnode_port_t vp)
680{
681 register vnode_pager_t vnode_object;
682
683 vnode_object = (struct vnode_pager *) zalloc(vnode_pager_zone);
684 if (vnode_object == VNODE_PAGER_NULL)
685 return(VNODE_PAGER_NULL);
686 vnode_object->pager_handle = IP_NULL;
687 vnode_object->vm_obj_handle = IP_NULL;
688 vnode_object->vnode_handle = vp;
689
690 return(vnode_object);
691}
692
693/*
694 *
695 */
696void
697vnode_port_hash_init(void)
698{
699 register vm_size_t size;
700 register int i;
701
702
703 size = (vm_size_t) sizeof(struct vnode_port_entry);
704
705 vnode_port_hash_zone = zinit(size,
706 (vm_size_t) MAX_VNODE * size,
707 PAGE_SIZE, "vnode_pager port hash");
708
709 for (i = 0; i < VNODE_PORT_HASH_COUNT; i++)
710 queue_init(&vnode_port_hashtable[i]);
711
712 simple_lock_init(&vnode_port_hash_lock,ETAP_NO_TRACE);
713}
714
715/*
716 *
717 */
718void
719vnode_port_hash_insert(
720 ipc_port_t name_port,
721 vnode_pager_t rec)
722{
723 register vnode_port_entry_t new_entry;
724
725 new_entry = (vnode_port_entry_t) zalloc(vnode_port_hash_zone);
726 /*
727 * TODO: Delete the following check once MAX_VNODE is removed
728 */
729 if (!new_entry)
730 panic("vnode_port_hash_insert: no space");
731 new_entry->name = name_port;
732 new_entry->pager_rec = rec;
733
734 simple_lock(&vnode_port_hash_lock);
735 queue_enter(&vnode_port_hashtable[vnode_port_hash(name_port)],
736 new_entry, vnode_port_entry_t, links);
737 simple_unlock(&vnode_port_hash_lock);
738}
739
740/*
741 *
742 */
743vnode_pager_t
744vnode_port_hash_lookup(
745 ipc_port_t name_port)
746{
747 register queue_t bucket;
748 register vnode_port_entry_t entry;
749 vnode_pager_t rec;
750
751 bucket = (queue_t) &vnode_port_hashtable[vnode_port_hash(name_port)];
752
753 simple_lock(&vnode_port_hash_lock);
754 entry = (vnode_port_entry_t) queue_first(bucket);
755 while (!queue_end(bucket,&entry->links)) {
756 if (entry->name == name_port) {
757 rec = entry->pager_rec;
758 simple_unlock(&vnode_port_hash_lock);
759 return(rec);
760 }
761 entry = (vnode_port_entry_t)queue_next(&entry->links);
762 }
763 simple_unlock(&vnode_port_hash_lock);
764 return(VNODE_PAGER_NULL);
765}
766
767/*
768 *
769 */
770void
771vnode_port_hash_delete(
772 ipc_port_t name_port)
773{
774 register queue_t bucket;
775 register vnode_port_entry_t entry;
776
777 bucket = (queue_t) &vnode_port_hashtable[vnode_port_hash(name_port)];
778
779 simple_lock(&vnode_port_hash_lock);
780 entry = (vnode_port_entry_t) queue_first(bucket);
781 while (!queue_end(bucket,&entry->links)) {
782 if (entry->name == name_port) {
783 queue_remove(bucket, entry, vnode_port_entry_t,links);
784 simple_unlock(&vnode_port_hash_lock);
785 zfree(vnode_port_hash_zone, (vm_offset_t) entry);
786 return;
787 }
788 entry = (vnode_port_entry_t)queue_next(&entry->links);
789 }
790 simple_unlock(&vnode_port_hash_lock);
791}