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/mig_errors.h>
15 #include <sys/param.h>
16 #include <sys/mount.h>
20 #include <sys/sysctl.h>
33 #include <default_pager/default_pager_types.h>
34 #include <default_pager_alerts_server.h>
35 #include <backing_store_alerts.h>
36 #include <backing_store_triggers_server.h>
40 * HI_WATER_DEFAULT set to this funny value to
41 * match the size that the low space application
42 * is asking for... need to keep MINIMUM_SIZE
45 #define HI_WATER_DEFAULT 40000000
46 #define MINIMUM_SIZE (1024 * 1024 * 64)
47 #define MAXIMUM_SIZE (1024 * 1024 * 1024)
54 unsigned int low_water
;
61 unsigned int hi_water
;
62 unsigned int local_hi_water
;
68 /* global parameters for application notification option */
69 mach_port_t trigger_port
= MACH_PORT_NULL
;
70 mach_port_t notify_port
= MACH_PORT_NULL
;
71 unsigned int notify_high
= 0;
72 unsigned int bs_recovery
;
75 void setprof __P((struct kvmvars *kvp, int state));
76 void dumpstate __P((struct kvmvars *kvp));
77 void reset __P((struct kvmvars *kvp));
84 mach_msg_size_t max_size
,
86 mach_msg_options_t options
)
88 mig_reply_error_t
*bufRequest
= 0, *bufReply
= 0;
89 register mach_msg_return_t mr
;
90 register kern_return_t kr
;
92 if ((kr
= vm_allocate(mach_task_self(),
93 (vm_address_t
*)&bufRequest
,
94 max_size
+ MAX_TRAILER_SIZE
,
95 TRUE
)) != KERN_SUCCESS
)
97 if ((kr
= vm_protect(mach_task_self(),
98 (vm_address_t
)bufRequest
,
99 max_size
+ MAX_TRAILER_SIZE
,
100 FALSE
, VM_PROT_ALL
)) != KERN_SUCCESS
)
102 mlock(bufRequest
, max_size
+ MAX_TRAILER_SIZE
);
103 if ((kr
= vm_allocate(mach_task_self(),
104 (vm_address_t
*)&bufReply
,
105 max_size
+ MAX_TRAILER_SIZE
,
106 TRUE
)) != KERN_SUCCESS
)
108 if ((kr
= vm_protect(mach_task_self(),
109 (vm_address_t
)bufReply
,
110 max_size
+ MAX_TRAILER_SIZE
,
111 FALSE
, VM_PROT_ALL
)) != KERN_SUCCESS
)
113 mlock(bufReply
, max_size
+ MAX_TRAILER_SIZE
);
115 mr
= mach_msg(&bufRequest
->Head
, MACH_RCV_MSG
|options
,
116 0, max_size
, rcv_name
,
117 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
);
118 if (mr
== MACH_MSG_SUCCESS
) {
119 /* we have a request message */
121 if(!(default_pager_alerts_server(
122 &bufRequest
->Head
, &bufReply
->Head
)))
123 backing_store_triggers_server(
124 &bufRequest
->Head
, &bufReply
->Head
);
126 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) &&
127 bufReply
->RetCode
!= KERN_SUCCESS
) {
128 if (bufReply
->RetCode
== MIG_NO_REPLY
)
130 * This return code is a little tricky--
131 * it appears that the demux routine found an
132 * error of some sort, but since that error
133 * would not normally get returned either to
134 * the local user or the remote one, we pretend it's
138 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
139 mach_msg_destroy(&bufRequest
->Head
);
143 if (bufReply
->Head
.msgh_remote_port
== MACH_PORT_NULL
) {
144 /* no reply port, so destroy the reply */
145 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
146 mach_msg_destroy(&bufReply
->Head
);
155 (void)vm_deallocate(mach_task_self(),
156 (vm_address_t
) bufRequest
,
157 max_size
+ MAX_TRAILER_SIZE
);
158 (void)vm_deallocate(mach_task_self(),
159 (vm_address_t
) bufReply
,
160 max_size
+ MAX_TRAILER_SIZE
);
167 backing_store_triggers(dynamic_pager
, hi_wat
, flags
, port
)
168 mach_port_t dynamic_pager
;
175 if (file_count
> max_valid
)
176 cur_limits
= max_valid
;
178 cur_limits
= file_count
;
180 if((hi_wat
+ limits
[cur_limits
].size
) > limits
[cur_limits
].low_water
)
181 return KERN_FAILURE
; /* let ipc system clean up port */
183 /* If there was a previous registration, throw it away */
184 if (notify_port
!= MACH_PORT_NULL
) {
185 mach_port_deallocate(mach_task_self(), notify_port
);
186 notify_port
= MACH_PORT_NULL
;
190 notify_high
= hi_wat
;
191 if(hi_water
< notify_high
) {
192 local_hi_water
= notify_high
;
194 local_hi_water
= hi_water
;
196 if(notify_high
> hi_water
) {
197 default_pager_space_alert(trigger_port
, HI_WAT_ALERT
);
204 default_pager_space_alert(alert_port
, flags
)
205 mach_port_t alert_port
;
214 unsigned int cur_size
;
215 unsigned int notifications
;
218 if(flags
& HI_WAT_ALERT
) {
222 if (file_count
> max_valid
)
223 cur_limits
= max_valid
;
225 cur_limits
= file_count
;
227 cur_size
= limits
[cur_limits
].size
;
231 * because the LO_WAT threshold changes relative to
232 * the size of the swap file we're creating
233 * we need to reset the LO_WAT_ALERT threshold each
234 * time we create a new swap file
236 if (limits
[cur_limits
].low_water
)
237 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
239 notifications
= HI_WAT_ALERT
;
241 sprintf(subfile
, "%s%d", fileroot
, file_count
);
242 file_ptr
= fopen(subfile
, "w+");
243 if (file_ptr
== NULL
) {
244 /* force error recovery below */
247 fchmod(fileno(file_ptr
), (mode_t
)01600);
248 error
= fcntl(fileno(file_ptr
), F_SETSIZE
, &filesize
);
250 error
= ftruncate(fileno(file_ptr
), filesize
);
260 if (file_count
> max_valid
)
261 cur_limits
= max_valid
;
263 cur_limits
= file_count
;
265 if (limits
[cur_limits
].low_water
)
266 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
268 notifications
= HI_WAT_ALERT
;
270 local_hi_water
= local_hi_water
>>2;
271 if(notify_high
>= (local_hi_water
)) {
272 if(notify_port
!= MACH_PORT_NULL
) {
273 /* notify monitoring app of */
274 /* backing store shortage */
275 backing_store_alert(notify_port
,
277 mach_port_deallocate(mach_task_self(),
279 notify_port
= MACH_PORT_NULL
;
283 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
285 if(hi_water
< notify_high
) {
286 if(local_hi_water
< notify_high
) {
287 bs_recovery
= notify_high
- local_hi_water
;
289 local_hi_water
= notify_high
;
291 if(local_hi_water
< hi_water
) {
292 bs_recovery
= hi_water
- local_hi_water
;
294 local_hi_water
= hi_water
;
296 ret
= macx_swapon(subfile
, flags
, cur_size
, priority
);
302 if (file_count
> max_valid
)
303 cur_limits
= max_valid
;
305 cur_limits
= file_count
;
307 if (limits
[cur_limits
].low_water
)
308 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
310 notifications
= HI_WAT_ALERT
;
312 local_hi_water
= local_hi_water
>>2;
313 if(notify_high
>= (local_hi_water
)) {
314 if(notify_port
!= MACH_PORT_NULL
) {
315 /* notify monitoring app of */
316 /* backing store shortage */
320 mach_port_deallocate(
323 notify_port
= MACH_PORT_NULL
;
327 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
328 } else if(bs_recovery
<= cur_size
) {
329 if((bs_recovery
!= 0) && (notify_port
)) {
330 backing_store_alert(notify_port
,
332 mach_port_deallocate(mach_task_self(),
334 notify_port
= MACH_PORT_NULL
;
339 bs_recovery
= bs_recovery
-cur_size
;
341 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
343 if(flags
& LO_WAT_ALERT
) {
344 sprintf(subfile
, "%s%d", fileroot
, file_count
);
345 if(hi_water
< notify_high
) {
346 local_hi_water
= notify_high
;
348 local_hi_water
= hi_water
;
350 if((bs_recovery
!= 0) && (notify_port
!= MACH_PORT_NULL
)) {
351 backing_store_alert(notify_port
, LO_WAT_ALERT
);
352 mach_port_deallocate(mach_task_self(), notify_port
);
353 notify_port
= MACH_PORT_NULL
;
357 if((error
= macx_swapoff(subfile
, flags
)) == 0) {
362 if (file_count
> max_valid
)
363 cur_limits
= max_valid
;
365 cur_limits
= file_count
;
367 if (file_count
> max_valid
)
368 cur_limits
= max_valid
;
370 cur_limits
= file_count
;
373 * only need to reset the LO_WAT_ALERT... the HI_WAT size is fixed,
374 * it doesn't change even if the swap file size shrinks or grows
376 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, LO_WAT_ALERT
, alert_port
);
382 wait_on_paging_trigger(trigger_port
)
383 mach_port_t trigger_port
;
385 kern_return_t result
;
386 result
= server_alert_loop(4096, trigger_port
, MACH_MSG_OPTION_NONE
);
387 if (result
!= KERN_SUCCESS
) {
388 fprintf(stderr
, "dynamic_pager: default pager alert failed\n");
395 paging_setup(flags
, size
, priority
, low
, high
, encrypted
)
403 off_t filesize
= size
;
409 sprintf(subfile
, "%s%d", fileroot
, file_count
);
410 file_ptr
= fopen(subfile
, "w+");
411 if (file_ptr
== NULL
) {
412 fprintf(stderr
, "dynamic_pager: cannot create paging file %s!\n",
416 fchmod(fileno(file_ptr
), (mode_t
)01600);
418 error
= fcntl(fileno(file_ptr
), F_SETSIZE
, &filesize
);
420 error
= ftruncate(fileno(file_ptr
), filesize
);
425 fprintf(stderr
, "dynamic_pager: cannot extend paging file size %s to %llu!\n",
430 if (macx_triggers(0, 0,
434 MACH_PORT_NULL
) != 0) {
436 "dynamic_pager: warning: "
437 "could not turn encrypted swap %s\n",
438 (encrypted
? "on" : "off"));
441 macx_swapon(subfile
, flags
, size
, priority
);
444 mach_msg_type_name_t poly
;
448 if (mach_port_allocate(mach_task_self(),
449 MACH_PORT_RIGHT_RECEIVE
,
450 &trigger_port
) != KERN_SUCCESS
) {
451 fprintf(stderr
,"dynamic_pager: allocation of trigger port failed\n");
454 /* create a send right on our local port */
455 mach_port_extract_right(mach_task_self(), trigger_port
,
456 MACH_MSG_TYPE_MAKE_SEND
, &trigger_port
, &poly
);
457 macx_triggers(high
, low
, HI_WAT_ALERT
, trigger_port
);
460 macx_triggers(high
, low
, LO_WAT_ALERT
, trigger_port
);
462 /* register control port for applications wishing to */
463 /* get backing store notifications or change dynamic */
464 /* pager settings. */
465 set_dp_control_port(mach_host_self(), trigger_port
);
466 wait_on_paging_trigger(trigger_port
);
471 main(int argc
, char **argv
)
475 char default_filename
[] = "/private/var/vm/swapfile";
477 int variable_sized
= 1;
478 boolean_t encrypted_swap
= FALSE
;
481 strcpy(fileroot
, default_filename
);
483 limits
[0].size
= 20000000;
484 limits
[0].low_water
= 0;
490 while ((ch
= getopt(argc
, argv
, "EF:L:H:S:P:O:")) != EOF
) {
494 encrypted_swap
= TRUE
;
498 strncpy(fileroot
, optarg
, 500);
503 limits
[0].low_water
= atoi(optarg
);
507 hi_water
= atoi(optarg
);
511 limits
[0].size
= atoi(optarg
);
514 priority
= atoi(optarg
);
518 (void)fprintf(stderr
,
519 "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n");
524 if (variable_sized
) {
525 static char tmp
[1024];
536 * if we get here, then none of the following options were specified... -L, H, or -S
537 * drop into a new mode that scales the size of the swap file based on how much free
538 * space is left on the volume being used for swap and the amount of physical ram
539 * installed on the system...
540 * basically, we'll pick a maximum size that doesn't exceed the following limits...
541 * 1/8 the remaining free space of the swap volume
542 * the size of phsyical ram
543 * MAXIMUM_SIZE - currently set to 1 Gbyte...
544 * once we have the maximum, we'll create a list of sizes and low_water limits
545 * we'll start with 2 files of MINIMUM_SIZE - currently 64 Mbytes...
546 * subsequent entries will double in size up to the calculated maximum... the low_water
547 * limit will be the sum of the current file size and the previous file size for each entry...
548 * as we add or delete files, we'll use the current file_count as an index into this
549 * table... if it's beyond the table size, we'll use the last entry
550 * the table entry will determine the size of the file to be created and the new low_water mark...
551 * the high_water mark is set to HI_WATER_DEFAULT which must be smaller than MINIMUM_SIZE...
552 * currently it is set to 40,000,000 to match the size being requested by the application
553 * monitoring low space conditions... having it set to the same size keeps us from creating
554 * an additional swap file when it really isn't necessary
558 * get rid of the filename at the end of the swap file specification
559 * we only want the portion of the pathname that should already exist
561 strcpy(tmp
, fileroot
);
562 if (q
= strrchr(tmp
, '/'))
565 if (statfs(tmp
, &sfs
) != -1) {
567 * Limit the maximum size of a swap file to 1/8 the free
568 * space available on the filesystem where the swap files
569 * are to reside. This will allow us to allocate and
570 * deallocate in finer increments on systems without much
573 fs_limit
= ((u_int64_t
)sfs
.f_bfree
* (u_int64_t
)sfs
.f_bsize
) / 8;
576 (void)fprintf(stderr
, "dynamic_pager: swap directory must exist\n");
581 len
= sizeof(u_int64_t
);
583 if (sysctl(mib
, 2, &memsize
, &len
, NULL
, 0) < 0) {
585 * if the sysctl fails for some reason
586 * use the starting size as the default
588 memsize
= MINIMUM_SIZE
;
590 if (memsize
> fs_limit
)
592 * clip based on filesystem space available
597 * further limit the maximum size of a swap file
599 if (memsize
> MAXIMUM_SIZE
)
600 memsize
= MAXIMUM_SIZE
;
605 * start small and work our way up to the maximum
606 * sized allowed... this way, we don't tie up too
607 * much disk space if we never do any real paging
609 for (max_valid
= 0, i
= 0; i
< MAX_LIMITS
; i
++) {
610 limits
[i
].size
= size
;
613 limits
[i
].low_water
= size
* 2;
615 if ((limits
[i
- 1].size
/ 2) > HI_WATER_DEFAULT
)
616 limits
[i
].low_water
= size
+ (limits
[i
- 1].size
/ 2);
618 limits
[i
].low_water
= size
+ limits
[i
- 1].size
;
623 * make the first 2 files the same size
631 if (max_valid
>= MAX_LIMITS
)
632 max_valid
= MAX_LIMITS
- 1;
634 hi_water
= HI_WATER_DEFAULT
;
636 local_hi_water
= hi_water
;
638 if((limits
[0].low_water
!= 0) && (limits
[0].low_water
<= (limits
[0].size
+ hi_water
))) {
639 (void)fprintf(stderr
, "dynamic_pager: low water trigger must be larger than size + hi_water\n");
645 paging_setup(0, limits
[0].size
, priority
, limits
[0].low_water
, hi_water
,