1 /* File created by Chris Youngworth, Apple Computer 2/11/99 */
7 #include <mach/mach_error.h>
8 #include <mach/mach_traps.h>
13 #include <mach/mach_syscalls.h>
14 #include <mach/mach_traps.h>
15 #include <mach/mig_errors.h>
16 #include <sys/param.h>
17 #include <sys/mount.h>
21 #include <sys/sysctl.h>
23 #include <sys/types.h>
35 #include <IOKit/ps/IOPowerSourcesPrivate.h>
36 #include <IOKit/pwr_mgt/IOPMLibPrivate.h>
37 #include <CoreFoundation/CoreFoundation.h>
39 #include <default_pager/default_pager_types.h>
40 #include <default_pager_alerts_server.h>
41 #include <backing_store_alerts.h>
42 #include <backing_store_triggers_server.h>
46 * HI_WATER_DEFAULT set to this funny value to
47 * match the size that the low space application
48 * is asking for... need to keep MINIMUM_SIZE
51 #define HI_WATER_DEFAULT 40000000
52 #define MINIMUM_SIZE (1024 * 1024 * 64)
53 #define MAXIMUM_SIZE (1024 * 1024 * 1024)
60 unsigned int low_water
;
67 unsigned int hi_water
;
68 unsigned int local_hi_water
;
74 /* global parameters for application notification option */
75 mach_port_t trigger_port
= MACH_PORT_NULL
;
76 mach_port_t notify_port
= MACH_PORT_NULL
;
77 unsigned int notify_high
= 0;
78 unsigned int bs_recovery
;
81 void setprof __P((struct kvmvars *kvp, int state));
82 void dumpstate __P((struct kvmvars *kvp));
83 void reset __P((struct kvmvars *kvp));
90 mach_msg_size_t max_size
,
92 mach_msg_options_t options
)
94 mig_reply_error_t
*bufRequest
= 0, *bufReply
= 0;
95 register mach_msg_return_t mr
;
96 register kern_return_t kr
;
98 if ((kr
= vm_allocate(mach_task_self(),
99 (vm_address_t
*)&bufRequest
,
100 max_size
+ MAX_TRAILER_SIZE
,
101 TRUE
)) != KERN_SUCCESS
)
103 mlock(bufRequest
, max_size
+ MAX_TRAILER_SIZE
);
104 if ((kr
= vm_allocate(mach_task_self(),
105 (vm_address_t
*)&bufReply
,
106 max_size
+ MAX_TRAILER_SIZE
,
107 TRUE
)) != KERN_SUCCESS
)
109 mlock(bufReply
, max_size
+ MAX_TRAILER_SIZE
);
111 mr
= mach_msg(&bufRequest
->Head
, MACH_RCV_MSG
|options
,
112 0, max_size
, rcv_name
,
113 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
114 if (mr
== MACH_MSG_SUCCESS
) {
115 /* we have a request message */
117 if(!(default_pager_alerts_server(
118 &bufRequest
->Head
, &bufReply
->Head
)))
119 backing_store_triggers_server(
120 &bufRequest
->Head
, &bufReply
->Head
);
122 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) &&
123 bufReply
->RetCode
!= KERN_SUCCESS
) {
124 if (bufReply
->RetCode
== MIG_NO_REPLY
)
126 * This return code is a little tricky--
127 * it appears that the demux routine found an
128 * error of some sort, but since that error
129 * would not normally get returned either to
130 * the local user or the remote one, we pretend it's
134 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
135 mach_msg_destroy(&bufRequest
->Head
);
139 if (bufReply
->Head
.msgh_remote_port
== MACH_PORT_NULL
) {
140 /* no reply port, so destroy the reply */
141 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
142 mach_msg_destroy(&bufReply
->Head
);
151 (void)vm_deallocate(mach_task_self(),
152 (vm_address_t
) bufRequest
,
153 max_size
+ MAX_TRAILER_SIZE
);
154 (void)vm_deallocate(mach_task_self(),
155 (vm_address_t
) bufReply
,
156 max_size
+ MAX_TRAILER_SIZE
);
163 backing_store_triggers(dynamic_pager
, hi_wat
, flags
, port
)
164 mach_port_t dynamic_pager
;
171 if (file_count
> max_valid
)
172 cur_limits
= max_valid
;
174 cur_limits
= file_count
;
176 if((hi_wat
+ limits
[cur_limits
].size
) > limits
[cur_limits
].low_water
)
177 return KERN_FAILURE
; /* let ipc system clean up port */
179 /* If there was a previous registration, throw it away */
180 if (notify_port
!= MACH_PORT_NULL
) {
181 mach_port_deallocate(mach_task_self(), notify_port
);
182 notify_port
= MACH_PORT_NULL
;
186 notify_high
= hi_wat
;
187 if(hi_water
< notify_high
) {
188 local_hi_water
= notify_high
;
190 local_hi_water
= hi_water
;
192 if(notify_high
> hi_water
) {
193 default_pager_space_alert(trigger_port
, HI_WAT_ALERT
);
200 default_pager_space_alert(alert_port
, flags
)
201 mach_port_t alert_port
;
209 unsigned int cur_size
;
210 unsigned int notifications
;
213 if(flags
& HI_WAT_ALERT
) {
217 if (file_count
> max_valid
)
218 cur_limits
= max_valid
;
220 cur_limits
= file_count
;
222 cur_size
= limits
[cur_limits
].size
;
226 * because the LO_WAT threshold changes relative to
227 * the size of the swap file we're creating
228 * we need to reset the LO_WAT_ALERT threshold each
229 * time we create a new swap file
231 if (limits
[cur_limits
].low_water
)
232 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
234 notifications
= HI_WAT_ALERT
;
236 sprintf(subfile
, "%s%d", fileroot
, file_count
);
237 fd
= open(subfile
, O_CREAT
|O_EXCL
|O_RDWR
,(mode_t
)(S_IRUSR
|S_IWUSR
));
239 /* force error recovery below */
242 error
= fcntl(fd
, F_SETSIZE
, &filesize
);
244 error
= ftruncate(fd
, filesize
);
254 if (file_count
> max_valid
)
255 cur_limits
= max_valid
;
257 cur_limits
= file_count
;
259 if (limits
[cur_limits
].low_water
)
260 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
262 notifications
= HI_WAT_ALERT
;
264 notifications
|= SWAP_FILE_CREATION_ERROR
;
266 local_hi_water
= local_hi_water
>>2;
267 if(notify_high
>= (local_hi_water
)) {
268 if(notify_port
!= MACH_PORT_NULL
) {
269 /* notify monitoring app of */
270 /* backing store shortage */
271 backing_store_alert(notify_port
,
273 mach_port_deallocate(mach_task_self(),
275 notify_port
= MACH_PORT_NULL
;
280 if(hi_water
< notify_high
) {
281 if(local_hi_water
< notify_high
) {
282 bs_recovery
= notify_high
- local_hi_water
;
284 local_hi_water
= notify_high
;
286 if(local_hi_water
< hi_water
) {
287 bs_recovery
= hi_water
- local_hi_water
;
289 local_hi_water
= hi_water
;
291 ret
= macx_swapon((uint64_t)(uintptr_t)subfile
,
292 flags
, cur_size
, priority
);
298 if (file_count
> max_valid
)
299 cur_limits
= max_valid
;
301 cur_limits
= file_count
;
303 if (limits
[cur_limits
].low_water
)
304 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
306 notifications
= HI_WAT_ALERT
;
308 local_hi_water
= local_hi_water
>>2;
309 if(notify_high
>= (local_hi_water
)) {
310 if(notify_port
!= MACH_PORT_NULL
) {
311 /* notify monitoring app of */
312 /* backing store shortage */
316 mach_port_deallocate(
319 notify_port
= MACH_PORT_NULL
;
323 } else if(bs_recovery
<= cur_size
) {
324 if((bs_recovery
!= 0) && (notify_port
)) {
325 backing_store_alert(notify_port
,
327 mach_port_deallocate(mach_task_self(),
329 notify_port
= MACH_PORT_NULL
;
334 bs_recovery
= bs_recovery
-cur_size
;
336 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
338 if(flags
& LO_WAT_ALERT
) {
339 sprintf(subfile
, "%s%d", fileroot
, file_count
);
340 if(hi_water
< notify_high
) {
341 local_hi_water
= notify_high
;
343 local_hi_water
= hi_water
;
345 if((bs_recovery
!= 0) && (notify_port
!= MACH_PORT_NULL
)) {
346 backing_store_alert(notify_port
, LO_WAT_ALERT
);
347 mach_port_deallocate(mach_task_self(), notify_port
);
348 notify_port
= MACH_PORT_NULL
;
352 if((error
= macx_swapoff((uint64_t)(uintptr_t)subfile
,
358 if (file_count
> max_valid
)
359 cur_limits
= max_valid
;
361 cur_limits
= file_count
;
363 if (file_count
> max_valid
)
364 cur_limits
= max_valid
;
366 cur_limits
= file_count
;
369 * only need to reset the LO_WAT_ALERT... the HI_WAT size is fixed,
370 * it doesn't change even if the swap file size shrinks or grows
372 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, LO_WAT_ALERT
, alert_port
);
378 wait_on_paging_trigger(trigger_port
)
379 mach_port_t trigger_port
;
381 kern_return_t result
;
382 result
= server_alert_loop(4096, trigger_port
, MACH_MSG_OPTION_NONE
);
383 if (result
!= KERN_SUCCESS
) {
384 fprintf(stderr
, "dynamic_pager: default pager alert failed\n");
391 paging_setup(flags
, size
, priority
, low
, high
, encrypted
)
399 off_t filesize
= size
;
404 sprintf(subfile
, "%s%d", fileroot
, file_count
);
405 fd
= open(subfile
, O_CREAT
|O_EXCL
|O_RDWR
, ((mode_t
)(S_IRUSR
|S_IWUSR
)));
407 fprintf(stderr
, "dynamic_pager: cannot create paging file %s!\n",
412 error
= fcntl(fd
, F_SETSIZE
, &filesize
);
414 error
= ftruncate(fd
, filesize
);
419 fprintf(stderr
, "dynamic_pager: cannot extend paging file size %s to %llu!\n",
424 if (macx_triggers(0, 0,
428 MACH_PORT_NULL
) != 0) {
430 "dynamic_pager: warning: "
431 "could not turn encrypted swap %s\n",
432 (encrypted
? "on" : "off"));
435 macx_swapon((uint64_t)(uintptr_t)subfile
, flags
, size
, priority
);
438 mach_msg_type_name_t poly
;
440 if (mach_port_allocate(mach_task_self(),
441 MACH_PORT_RIGHT_RECEIVE
,
442 &trigger_port
) != KERN_SUCCESS
) {
443 fprintf(stderr
,"dynamic_pager: allocation of trigger port failed\n");
446 /* create a send right on our local port */
447 mach_port_extract_right(mach_task_self(), trigger_port
,
448 MACH_MSG_TYPE_MAKE_SEND
, &trigger_port
, &poly
);
449 macx_triggers(high
, low
, HI_WAT_ALERT
, trigger_port
);
452 macx_triggers(high
, low
, LO_WAT_ALERT
, trigger_port
);
454 /* register control port for applications wishing to */
455 /* get backing store notifications or change dynamic */
456 /* pager settings. */
457 set_dp_control_port(mach_host_self(), trigger_port
);
458 wait_on_paging_trigger(trigger_port
);
464 clean_swap_directory(const char *path
)
467 struct dirent
*entry
;
472 fprintf(stderr
,"dynamic_pager: cannot open swap directory %s\n", path
);
476 while ((entry
= readdir(dir
)) != NULL
) {
477 if (entry
->d_namlen
>= 4 && strncmp(entry
->d_name
, "swap", 4) == 0) {
478 snprintf(buf
, sizeof buf
, "%s/%s", path
, entry
->d_name
);
486 #define VM_PREFS_PLIST "/Library/Preferences/com.apple.virtualMemory.plist"
487 #define VM_PREFS_ENCRYPT_SWAP_KEY "UseEncryptedSwap"
490 should_encrypt_swap(void)
492 CFPropertyListRef propertyList
;
493 CFTypeID propertyListType
;
494 CFStringRef errorString
;
495 CFDataRef resourceData
;
499 boolean_t should_encrypt
;
500 boolean_t explicit_value
;
502 explicit_value
= false;
503 should_encrypt
= true;
505 fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
, (const UInt8
*)VM_PREFS_PLIST
, strlen(VM_PREFS_PLIST
), false);
506 if (fileURL
== NULL
) {
507 /*fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), VM_PREFS_PLIST);*/
511 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault
, fileURL
, &resourceData
, NULL
, NULL
, &errorCode
)) {
512 /*fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), VM_PREFS_PLIST, (int)errorCode);*/
518 propertyList
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, resourceData
, kCFPropertyListMutableContainers
, &errorString
);
519 if (propertyList
== NULL
) {
520 /*fprintf(stderr, "%s: cannot get XML propertyList %s\n", getprogname(), VM_PREFS_PLIST);*/
521 CFRelease(resourceData
);
525 propertyListType
= CFGetTypeID(propertyList
);
527 if (propertyListType
== CFDictionaryGetTypeID()) {
528 value
= (CFTypeRef
) CFDictionaryGetValue((CFDictionaryRef
) propertyList
, CFSTR(VM_PREFS_ENCRYPT_SWAP_KEY
));
530 /* no value: use the default value */
531 } else if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
532 fprintf(stderr
, "%s: wrong type for key \"%s\"\n",
533 getprogname(), VM_PREFS_ENCRYPT_SWAP_KEY
);
534 /* bogus value, assume it's "true" for safety's sake */
535 should_encrypt
= true;
536 explicit_value
= true;
538 should_encrypt
= CFBooleanGetValue((CFBooleanRef
)value
);
539 explicit_value
= true;
543 /*fprintf(stderr, "%s: invalid propertyList type %d (not a dictionary)\n", getprogname(), propertyListType);*/
545 CFRelease(resourceData
);
546 CFRelease(propertyList
);
549 if (! explicit_value
) {
550 #if TARGET_OS_EMBEDDED
551 should_encrypt
= FALSE
;
553 /* by default, encrypt swap on laptops only */
555 * Look for battery power source.
557 should_encrypt
= (kCFBooleanTrue
== IOPSPowerSourceSupported(NULL
, CFSTR(kIOPMBatteryPowerKey
)));
558 /*fprintf(stderr, "dynamic_pager: battery power source: %d\n", should_encrypt);*/
562 return should_encrypt
;
566 main(int argc
, char **argv
)
570 char default_filename
[] = "/private/var/vm/swapfile";
572 int variable_sized
= 1,flags
=0;
573 boolean_t encrypted_swap
;
581 strcpy(fileroot
, default_filename
);
584 limits
[0].size
= 20000000;
585 limits
[0].low_water
= 0;
590 encrypted_swap
= should_encrypt_swap();
592 while ((ch
= getopt(argc
, argv
, "EF:L:H:S:P:QO:")) != EOF
) {
596 encrypted_swap
= TRUE
;
600 strncpy(fileroot
, optarg
, 500);
605 limits
[0].low_water
= atoi(optarg
);
609 hi_water
= atoi(optarg
);
613 limits
[0].size
= atoi(optarg
);
616 priority
= atoi(optarg
);
620 /* just query for "encrypted swap" default value */
622 "dynamic_pager: encrypted swap will be %s\n",
623 encrypted_swap
? "ON": "OFF");
627 (void)fprintf(stderr
,
628 "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n");
633 if (variable_sized
) {
634 static char tmp
[1024];
642 u_int64_t fs_limit
= 0;
645 * if we get here, then none of the following options were specified... -L, H, or -S
646 * drop into a new mode that scales the size of the swap file based on how much free
647 * space is left on the volume being used for swap and the amount of physical ram
648 * installed on the system...
649 * basically, we'll pick a maximum size that doesn't exceed the following limits...
650 * 1/8 the remaining free space of the swap volume
651 * the size of phsyical ram
652 * MAXIMUM_SIZE - currently set to 1 Gbyte...
653 * once we have the maximum, we'll create a list of sizes and low_water limits
654 * we'll start with 2 files of MINIMUM_SIZE - currently 64 Mbytes...
655 * subsequent entries will double in size up to the calculated maximum... the low_water
656 * limit will be the sum of the current file size and the previous file size for each entry...
657 * as we add or delete files, we'll use the current file_count as an index into this
658 * table... if it's beyond the table size, we'll use the last entry
659 * the table entry will determine the size of the file to be created and the new low_water mark...
660 * the high_water mark is set to HI_WATER_DEFAULT which must be smaller than MINIMUM_SIZE...
661 * currently it is set to 40,000,000 to match the size being requested by the application
662 * monitoring low space conditions... having it set to the same size keeps us from creating
663 * an additional swap file when it really isn't necessary
667 * get rid of the filename at the end of the swap file specification
668 * we only want the portion of the pathname that should already exist
670 strcpy(tmp
, fileroot
);
671 if ((q
= strrchr(tmp
, '/')))
675 * Remove all files in the swap directory.
677 clean_swap_directory(tmp
);
679 if (statfs(tmp
, &sfs
) == -1) {
681 * Setup the swap directory.
683 if (mkdir(tmp
, 0755) == -1) {
684 (void)fprintf(stderr
, "dynamic_pager: cannot create swap directory %s\n", tmp
);
689 if (statfs(tmp
, &sfs
) == -1) {
691 * We really can't get filesystem status,
692 * so let's not limit the swap files...
694 fs_limit
= (u_int64_t
) -1;
698 if (fs_limit
!= (u_int64_t
) -1) {
700 * Limit the maximum size of a swap file to 1/8 the free
701 * space available on the filesystem where the swap files
702 * are to reside. This will allow us to allocate and
703 * deallocate in finer increments on systems without much
706 fs_limit
= ((u_int64_t
)sfs
.f_bfree
* (u_int64_t
)sfs
.f_bsize
) / 8;
711 len
= sizeof(u_int64_t
);
713 if (sysctl(mib
, 2, &memsize
, &len
, NULL
, 0) < 0) {
715 * if the sysctl fails for some reason
716 * use the starting size as the default
718 memsize
= MINIMUM_SIZE
;
720 if (memsize
> fs_limit
)
722 * clip based on filesystem space available
727 * further limit the maximum size of a swap file
729 if (memsize
<= MINIMUM_SIZE
) {
730 (void)fprintf(stderr
, "dynamic_pager: Need more space on the disk to enable swapping.\n");
733 } else if (memsize
<= (MINIMUM_SIZE
*2)) {
734 (void)fprintf(stderr
, "dynamic_pager: Activating emergency swap file immediately.\n");
735 flags
|= USE_EMERGENCY_SWAP_FILE_FIRST
;
736 } else if (memsize
> MAXIMUM_SIZE
) {
737 memsize
= MAXIMUM_SIZE
;
743 * start small and work our way up to the maximum
744 * sized allowed... this way, we don't tie up too
745 * much disk space if we never do any real paging
747 for (max_valid
= 0, i
= 0; i
< MAX_LIMITS
; i
++) {
748 limits
[i
].size
= size
;
751 limits
[i
].low_water
= size
* 2;
753 if ((limits
[i
- 1].size
/ 2) > HI_WATER_DEFAULT
)
754 limits
[i
].low_water
= size
+ (limits
[i
- 1].size
/ 2);
756 limits
[i
].low_water
= size
+ limits
[i
- 1].size
;
761 * make the first 2 files the same size
769 if (max_valid
>= MAX_LIMITS
)
770 max_valid
= MAX_LIMITS
- 1;
772 hi_water
= HI_WATER_DEFAULT
;
774 local_hi_water
= hi_water
;
776 if((limits
[0].low_water
!= 0) && (limits
[0].low_water
<= (limits
[0].size
+ hi_water
))) {
777 (void)fprintf(stderr
, "dynamic_pager: low water trigger must be larger than size + hi_water\n");
783 paging_setup(flags
, limits
[0].size
, priority
, limits
[0].low_water
, hi_water
,