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/bootstrap.h>
14 #include <mach/mach_syscalls.h>
15 #include <mach/mig_errors.h>
16 #include <sys/param.h>
17 #include <sys/mount.h>
21 #include <sys/sysctl.h>
34 #include <default_pager/default_pager_types.h>
35 #include <default_pager_alerts_server.h>
36 #include <backing_store_alerts.h>
37 #include <backing_store_triggers_server.h>
41 * HI_WATER_DEFAULT set to this funny value to
42 * match the size that the low space application
43 * is asking for... need to keep MINIMUM_SIZE
46 #define HI_WATER_DEFAULT 40000000
47 #define MINIMUM_SIZE (1024 * 1024 * 64)
48 #define MAXIMUM_SIZE (1024 * 1024 * 1024)
55 unsigned int low_water
;
62 unsigned int hi_water
;
63 unsigned int local_hi_water
;
69 /* global parameters for application notification option */
70 mach_port_t trigger_port
= MACH_PORT_NULL
;
71 mach_port_t notify_port
= MACH_PORT_NULL
;
72 unsigned int notify_high
= 0;
73 unsigned int bs_recovery
;
76 void setprof __P((struct kvmvars *kvp, int state));
77 void dumpstate __P((struct kvmvars *kvp));
78 void reset __P((struct kvmvars *kvp));
85 mach_msg_size_t max_size
,
87 mach_msg_options_t options
)
89 mig_reply_error_t
*bufRequest
= 0, *bufReply
= 0;
90 register mach_msg_return_t mr
;
91 register kern_return_t kr
;
93 if ((kr
= vm_allocate(mach_task_self(),
94 (vm_address_t
*)&bufRequest
,
95 max_size
+ MAX_TRAILER_SIZE
,
96 TRUE
)) != KERN_SUCCESS
)
98 if ((kr
= vm_protect(mach_task_self(),
99 (vm_address_t
)bufRequest
,
100 max_size
+ MAX_TRAILER_SIZE
,
101 FALSE
, VM_PROT_ALL
)) != 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 if ((kr
= vm_protect(mach_task_self(),
110 (vm_address_t
)bufReply
,
111 max_size
+ MAX_TRAILER_SIZE
,
112 FALSE
, VM_PROT_ALL
)) != KERN_SUCCESS
)
114 mlock(bufReply
, max_size
+ MAX_TRAILER_SIZE
);
116 mr
= mach_msg_overwrite_trap(&bufRequest
->Head
, MACH_RCV_MSG
|options
,
117 0, max_size
, rcv_name
,
118 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
,
119 (mach_msg_header_t
*) 0, 0);
120 if (mr
== MACH_MSG_SUCCESS
) {
121 /* we have a request message */
123 if(!(default_pager_alerts_server(
124 &bufRequest
->Head
, &bufReply
->Head
)))
125 backing_store_triggers_server(
126 &bufRequest
->Head
, &bufReply
->Head
);
128 if (!(bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) &&
129 bufReply
->RetCode
!= KERN_SUCCESS
) {
130 if (bufReply
->RetCode
== MIG_NO_REPLY
)
132 * This return code is a little tricky--
133 * it appears that the demux routine found an
134 * error of some sort, but since that error
135 * would not normally get returned either to
136 * the local user or the remote one, we pretend it's
140 bufRequest
->Head
.msgh_remote_port
= MACH_PORT_NULL
;
141 mach_msg_destroy(&bufRequest
->Head
);
145 if (bufReply
->Head
.msgh_remote_port
== MACH_PORT_NULL
) {
146 /* no reply port, so destroy the reply */
147 if (bufReply
->Head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
148 mach_msg_destroy(&bufReply
->Head
);
157 (void)vm_deallocate(mach_task_self(),
158 (vm_address_t
) bufRequest
,
159 max_size
+ MAX_TRAILER_SIZE
);
160 (void)vm_deallocate(mach_task_self(),
161 (vm_address_t
) bufReply
,
162 max_size
+ MAX_TRAILER_SIZE
);
169 backing_store_triggers(dynamic_pager
, hi_wat
, flags
, port
)
170 mach_port_t dynamic_pager
;
177 if (file_count
> max_valid
)
178 cur_limits
= max_valid
;
180 cur_limits
= file_count
;
182 if((hi_wat
+ limits
[cur_limits
].size
) > limits
[cur_limits
].low_water
)
183 return KERN_FAILURE
; /* let ipc system clean up port */
185 /* If there was a previous registration, throw it away */
186 if (notify_port
!= MACH_PORT_NULL
) {
187 mach_port_deallocate(mach_task_self(), notify_port
);
188 notify_port
= MACH_PORT_NULL
;
192 notify_high
= hi_wat
;
193 if(hi_water
< notify_high
) {
194 local_hi_water
= notify_high
;
196 local_hi_water
= hi_water
;
198 if(notify_high
> hi_water
) {
199 default_pager_space_alert(trigger_port
, HI_WAT_ALERT
);
206 default_pager_space_alert(alert_port
, flags
)
207 mach_port_t alert_port
;
216 unsigned int cur_size
;
217 unsigned int notifications
;
220 if(flags
& HI_WAT_ALERT
) {
224 if (file_count
> max_valid
)
225 cur_limits
= max_valid
;
227 cur_limits
= file_count
;
229 cur_size
= limits
[cur_limits
].size
;
233 * because the LO_WAT threshold changes relative to
234 * the size of the swap file we're creating
235 * we need to reset the LO_WAT_ALERT threshold each
236 * time we create a new swap file
238 if (limits
[cur_limits
].low_water
)
239 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
241 notifications
= HI_WAT_ALERT
;
243 sprintf(subfile
, "%s%d", fileroot
, file_count
);
244 file_ptr
= fopen(subfile
, "w+");
245 fchmod(fileno(file_ptr
), (mode_t
)01600);
246 error
= fcntl(fileno(file_ptr
), F_SETSIZE
, &filesize
);
248 error
= ftruncate(fileno(file_ptr
), filesize
);
256 if (file_count
> max_valid
)
257 cur_limits
= max_valid
;
259 cur_limits
= file_count
;
261 if (limits
[cur_limits
].low_water
)
262 notifications
= HI_WAT_ALERT
| LO_WAT_ALERT
;
264 notifications
= HI_WAT_ALERT
;
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
;
279 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
281 if(hi_water
< notify_high
) {
282 if(local_hi_water
< notify_high
) {
283 bs_recovery
= notify_high
- local_hi_water
;
285 local_hi_water
= notify_high
;
287 if(local_hi_water
< hi_water
) {
288 bs_recovery
= hi_water
- local_hi_water
;
290 local_hi_water
= hi_water
;
292 ret
= macx_swapon(subfile
, 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 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
324 } else if(bs_recovery
<= cur_size
) {
325 if((bs_recovery
!= 0) && (notify_port
)) {
326 backing_store_alert(notify_port
,
328 mach_port_deallocate(mach_task_self(),
330 notify_port
= MACH_PORT_NULL
;
335 bs_recovery
= bs_recovery
-cur_size
;
337 macx_triggers(local_hi_water
, limits
[cur_limits
].low_water
, notifications
, alert_port
);
339 if(flags
& LO_WAT_ALERT
) {
340 sprintf(subfile
, "%s%d", fileroot
, file_count
);
341 if(hi_water
< notify_high
) {
342 local_hi_water
= notify_high
;
344 local_hi_water
= hi_water
;
346 if((bs_recovery
!= 0) && (notify_port
!= MACH_PORT_NULL
)) {
347 backing_store_alert(notify_port
, LO_WAT_ALERT
);
348 mach_port_deallocate(mach_task_self(), notify_port
);
349 notify_port
= MACH_PORT_NULL
;
353 if((error
= macx_swapoff(subfile
, flags
)) == 0) {
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
)
398 off_t filesize
= size
;
404 sprintf(subfile
, "%s%d", fileroot
, file_count
);
405 file_ptr
= fopen(subfile
, "w+");
406 fchmod(fileno(file_ptr
), (mode_t
)01600);
407 error
= fcntl(fileno(file_ptr
), F_SETSIZE
, &filesize
);
409 error
= ftruncate(fileno(file_ptr
), filesize
);
413 macx_swapon(subfile
, flags
, size
, priority
);
416 mach_msg_type_name_t poly
;
420 if (mach_port_allocate(mach_task_self(),
421 MACH_PORT_RIGHT_RECEIVE
,
422 &trigger_port
) != KERN_SUCCESS
) {
423 fprintf(stderr
,"allocation of trigger port failed\n");
426 /* create a send right on our local port */
427 mach_port_extract_right(mach_task_self(), trigger_port
,
428 MACH_MSG_TYPE_MAKE_SEND
, &trigger_port
, &poly
);
429 macx_triggers(high
, low
, HI_WAT_ALERT
, trigger_port
);
432 macx_triggers(high
, low
, LO_WAT_ALERT
, trigger_port
);
434 /* register control port for applications wishing to */
435 /* get backing store notifications or change dynamic */
436 /* pager settings. */
437 set_dp_control_port(mach_host_self(), trigger_port
);
438 wait_on_paging_trigger(trigger_port
);
443 main(int argc
, char **argv
)
447 char default_filename
[] = "/private/var/vm/swapfile";
449 int variable_sized
= 1;
452 strcpy(fileroot
, default_filename
);
454 limits
[0].size
= 20000000;
455 limits
[0].low_water
= 0;
461 while ((ch
= getopt(argc
, argv
, "F:L:H:S:P:O:")) != EOF
) {
465 strncpy(fileroot
, optarg
, 500);
470 limits
[0].low_water
= atoi(optarg
);
474 hi_water
= atoi(optarg
);
478 limits
[0].size
= atoi(optarg
);
481 priority
= atoi(optarg
);
485 (void)fprintf(stderr
,
486 "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n");
491 if (variable_sized
) {
492 static char tmp
[1024];
503 * if we get here, then none of the following options were specified... -L, H, or -S
504 * drop into a new mode that scales the size of the swap file based on how much free
505 * space is left on the volume being used for swap and the amount of physical ram
506 * installed on the system...
507 * basically, we'll pick a maximum size that doesn't exceed the following limits...
508 * 1/4 the remaining free space of the swap volume
509 * the size of phsyical ram
510 * MAXIMUM_SIZE - currently set to 1 Gbyte...
511 * once we have the maximum, we'll create a list of sizes and low_water limits
512 * we'll start with 2 files of MINIMUM_SIZE - currently 64 Mbytes...
513 * subsequent entries will double in size up to the calculated maximum... the low_water
514 * limit will be the sum of the current file size and the previous file size for each entry...
515 * as we add or delete files, we'll use the current file_count as an index into this
516 * table... if it's beyond the table size, we'll use the last entry
517 * the table entry will determine the size of the file to be created and the new low_water mark...
518 * the high_water mark is set to HI_WATER_DEFAULT which must be smaller than MINIMUM_SIZE...
519 * currently it is set to 40,000,000 to match the size being requested by the application
520 * monitoring low space conditions... having it set to the same size keeps us from creating
521 * an additional swap file when it really isn't necessary
525 * get rid of the filename at the end of the swap file specification
526 * we only want the portion of the pathname that should already exist
528 strcpy(tmp
, fileroot
);
529 if (q
= strrchr(tmp
, '/'))
532 if (statfs(tmp
, &sfs
) != -1) {
534 * limit the maximum size of a swap file to 1/4 the free
535 * space available on the filesystem where the swap files
538 fs_limit
= ((u_int64_t
)sfs
.f_bfree
* (u_int64_t
)sfs
.f_bsize
) / 4;
541 (void)fprintf(stderr
, "usage: swap directory must exist\n");
546 len
= sizeof(u_int64_t
);
548 if (sysctl(mib
, 2, &memsize
, &len
, NULL
, 0) < 0) {
550 * if the sysctl fails for some reason
551 * use the starting size as the default
553 memsize
= MINIMUM_SIZE
;
555 if (memsize
> fs_limit
)
557 * clip based on filesystem space available
562 * further limit the maximum size of a swap file
564 if (memsize
> MAXIMUM_SIZE
)
565 memsize
= MAXIMUM_SIZE
;
570 * start small and work our way up to the maximum
571 * sized allowed... this way, we don't tie up too
572 * much disk space if we never do any real paging
574 for (max_valid
= 0, i
= 0; i
< MAX_LIMITS
; i
++) {
575 limits
[i
].size
= size
;
578 limits
[i
].low_water
= size
* 2;
580 if ((limits
[i
- 1].size
/ 2) > HI_WATER_DEFAULT
)
581 limits
[i
].low_water
= size
+ (limits
[i
- 1].size
/ 2);
583 limits
[i
].low_water
= size
+ limits
[i
- 1].size
;
590 * make the first 2 files the same size
596 if (max_valid
>= MAX_LIMITS
)
597 max_valid
= MAX_LIMITS
- 1;
599 hi_water
= HI_WATER_DEFAULT
;
601 local_hi_water
= hi_water
;
603 if((limits
[0].low_water
!= 0) && (limits
[0].low_water
<= (limits
[0].size
+ hi_water
))) {
604 (void)fprintf(stderr
, "usage: low water trigger must be larger than size + hi_water\n");
610 paging_setup(0, limits
[0].size
, priority
, limits
[0].low_water
, hi_water
);