]> git.saurik.com Git - apple/system_cmds.git/blame - dynamic_pager.tproj/dynamic_pager.c
system_cmds-300.tar.gz
[apple/system_cmds.git] / dynamic_pager.tproj / dynamic_pager.c
CommitLineData
1815bff5
A
1/* File created by Chris Youngworth, Apple Computer 2/11/99 */
2
3
4#define mig_external
5
6#include <mach/port.h>
7#include <mach/mach_error.h>
8#include <mach/mach_traps.h>
9#include <mach/mach.h>
10#ifndef MACH_BSD
11#define MACH_BSD
12#endif
13#include <mach/bootstrap.h>
14#include <mach/mach_syscalls.h>
15#include <mach/mig_errors.h>
16#include <sys/param.h>
c3a08f59 17#include <sys/mount.h>
1815bff5
A
18#include <sys/file.h>
19#include <sys/mman.h>
20#include <sys/stat.h>
21#include <sys/sysctl.h>
22#include <sys/gmon.h>
23#include <errno.h>
24#include <kvm.h>
25#include <limits.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <nlist.h>
30#include <ctype.h>
31#include <unistd.h>
32#include <paths.h>
33
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>
38
c3a08f59
A
39
40/*
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
44 * above this value.
45 */
46#define HI_WATER_DEFAULT 40000000
47#define MINIMUM_SIZE (1024 * 1024 * 64)
48#define MAXIMUM_SIZE (1024 * 1024 * 1024)
49
50#define MAX_LIMITS 8
51
52
53struct limit {
54 unsigned int size;
55 unsigned int low_water;
56} limits[MAX_LIMITS];
57
58
1815bff5 59int debug = 0;
c3a08f59 60int max_valid = 0;
1815bff5 61int file_count = 0;
c3a08f59
A
62unsigned int hi_water;
63unsigned int local_hi_water;
1815bff5 64int priority = 0;
c3a08f59 65int options = 0;
1815bff5
A
66char fileroot[512];
67
68
69/* global parameters for application notification option */
756446ec
A
70mach_port_t trigger_port = MACH_PORT_NULL;
71mach_port_t notify_port = MACH_PORT_NULL;
c3a08f59
A
72unsigned int notify_high = 0;
73unsigned int bs_recovery;
1815bff5
A
74
75/*
76void setprof __P((struct kvmvars *kvp, int state));
77void dumpstate __P((struct kvmvars *kvp));
78void reset __P((struct kvmvars *kvp));
79*/
80
81
82
83mach_msg_return_t
84server_alert_loop(
85 mach_msg_size_t max_size,
86 mach_port_t rcv_name,
87 mach_msg_options_t options)
88{
89 mig_reply_error_t *bufRequest = 0, *bufReply = 0;
90 register mach_msg_return_t mr;
91 register kern_return_t kr;
92
93 if ((kr = vm_allocate(mach_task_self(),
94 (vm_address_t *)&bufRequest,
95 max_size + MAX_TRAILER_SIZE,
96 TRUE)) != KERN_SUCCESS)
97 return kr;
1c51fdde
A
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)
102 return kr;
1815bff5
A
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)
108 return kr;
1c51fdde
A
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)
113 return kr;
1815bff5
A
114 mlock(bufReply, max_size + MAX_TRAILER_SIZE);
115 while(TRUE) {
83f6dbe8
A
116 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
117 0, max_size, rcv_name,
118 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
1815bff5
A
119 if (mr == MACH_MSG_SUCCESS) {
120 /* we have a request message */
121
122 if(!(default_pager_alerts_server(
123 &bufRequest->Head, &bufReply->Head)))
124 backing_store_triggers_server(
125 &bufRequest->Head, &bufReply->Head);
126
127 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
128 bufReply->RetCode != KERN_SUCCESS) {
129 if (bufReply->RetCode == MIG_NO_REPLY)
130 /*
131 * This return code is a little tricky--
132 * it appears that the demux routine found an
133 * error of some sort, but since that error
134 * would not normally get returned either to
135 * the local user or the remote one, we pretend it's
136 * ok.
137 */
138
139 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
140 mach_msg_destroy(&bufRequest->Head);
141 continue;
142 }
143
144 if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
145 /* no reply port, so destroy the reply */
146 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
147 mach_msg_destroy(&bufReply->Head);
148 } else {
149 break;
150 }
151 } else {
152 break;
153 }
154 }
155
156 (void)vm_deallocate(mach_task_self(),
157 (vm_address_t) bufRequest,
158 max_size + MAX_TRAILER_SIZE);
159 (void)vm_deallocate(mach_task_self(),
160 (vm_address_t) bufReply,
161 max_size + MAX_TRAILER_SIZE);
162 return KERN_FAILURE;
163
164}
165
166
167kern_return_t
168backing_store_triggers(dynamic_pager, hi_wat, flags, port)
169 mach_port_t dynamic_pager;
170 int hi_wat;
171 int flags;
172 mach_port_t port;
173{
c3a08f59
A
174 int cur_limits;
175
176 if (file_count > max_valid)
177 cur_limits = max_valid;
178 else
179 cur_limits = file_count;
180
181 if((hi_wat + limits[cur_limits].size) > limits[cur_limits].low_water)
1815bff5 182 return KERN_FAILURE; /* let ipc system clean up port */
756446ec
A
183
184 /* If there was a previous registration, throw it away */
185 if (notify_port != MACH_PORT_NULL) {
186 mach_port_deallocate(mach_task_self(), notify_port);
187 notify_port = MACH_PORT_NULL;
188 }
189
1815bff5
A
190 notify_port = port;
191 notify_high = hi_wat;
192 if(hi_water < notify_high) {
193 local_hi_water = notify_high;
194 } else {
195 local_hi_water = hi_water;
196 }
197 if(notify_high > hi_water) {
198 default_pager_space_alert(trigger_port, HI_WAT_ALERT);
199 }
200 return KERN_SUCCESS;
201}
202
203
204kern_return_t
205default_pager_space_alert(alert_port, flags)
206 mach_port_t alert_port;
207 int flags;
208{
209 char subfile[512];
210 FILE *file_ptr;
c3a08f59 211 off_t filesize;
1815bff5 212 int error;
756446ec 213 kern_return_t ret;
c3a08f59
A
214 int cur_limits;
215 unsigned int cur_size;
216 unsigned int notifications;
1815bff5 217
c3a08f59 218
1815bff5 219 if(flags & HI_WAT_ALERT) {
c3a08f59 220
1815bff5 221 file_count++;
c3a08f59
A
222
223 if (file_count > max_valid)
224 cur_limits = max_valid;
225 else
226 cur_limits = file_count;
227
228 cur_size = limits[cur_limits].size;
229 filesize = cur_size;
230
231 /*
232 * because the LO_WAT threshold changes relative to
233 * the size of the swap file we're creating
234 * we need to reset the LO_WAT_ALERT threshold each
235 * time we create a new swap file
236 */
237 if (limits[cur_limits].low_water)
238 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
239 else
240 notifications = HI_WAT_ALERT;
241
1815bff5
A
242 sprintf(subfile, "%s%d", fileroot, file_count);
243 file_ptr = fopen(subfile, "w+");
83f6dbe8
A
244 if (file_ptr == NULL) {
245 /* force error recovery below */
246 error = -1;
247 } else {
248 fchmod(fileno(file_ptr), (mode_t)01600);
249 error = fcntl(fileno(file_ptr), F_SETSIZE, &filesize);
250 if(error) {
251 error = ftruncate(fileno(file_ptr), filesize);
252 }
253 if(error)
254 unlink(subfile);
255 fclose(file_ptr);
756446ec 256 }
c3a08f59 257
1815bff5 258 if(error == -1) {
1815bff5 259 file_count--;
c3a08f59
A
260
261 if (file_count > max_valid)
262 cur_limits = max_valid;
263 else
264 cur_limits = file_count;
265
266 if (limits[cur_limits].low_water)
267 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
268 else
269 notifications = HI_WAT_ALERT;
270
1815bff5
A
271 local_hi_water = local_hi_water>>2;
272 if(notify_high >= (local_hi_water)) {
756446ec 273 if(notify_port != MACH_PORT_NULL) {
1815bff5
A
274 /* notify monitoring app of */
275 /* backing store shortage */
276 backing_store_alert(notify_port,
277 HI_WAT_ALERT);
278 mach_port_deallocate(mach_task_self(),
279 notify_port);
756446ec 280 notify_port = MACH_PORT_NULL;
1815bff5 281 notify_high = 0;
1815bff5
A
282 }
283 }
c3a08f59 284 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
1815bff5
A
285 } else {
286 if(hi_water < notify_high) {
287 if(local_hi_water < notify_high) {
288 bs_recovery = notify_high - local_hi_water;
289 }
290 local_hi_water = notify_high;
291 } else {
292 if(local_hi_water < hi_water) {
293 bs_recovery = hi_water - local_hi_water;
294 }
295 local_hi_water = hi_water;
296 }
c3a08f59
A
297 ret = macx_swapon(subfile, flags, cur_size, priority);
298
756446ec
A
299 if(ret) {
300 unlink(subfile);
301 file_count--;
c3a08f59
A
302
303 if (file_count > max_valid)
304 cur_limits = max_valid;
305 else
306 cur_limits = file_count;
307
308 if (limits[cur_limits].low_water)
309 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
310 else
311 notifications = HI_WAT_ALERT;
312
756446ec
A
313 local_hi_water = local_hi_water>>2;
314 if(notify_high >= (local_hi_water)) {
315 if(notify_port != MACH_PORT_NULL) {
316 /* notify monitoring app of */
317 /* backing store shortage */
318 backing_store_alert(
319 notify_port,
320 HI_WAT_ALERT);
321 mach_port_deallocate(
322 mach_task_self(),
323 notify_port);
324 notify_port = MACH_PORT_NULL;
325 notify_high = 0;
326 }
327 }
c3a08f59
A
328 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
329 } else if(bs_recovery <= cur_size) {
1815bff5
A
330 if((bs_recovery != 0) && (notify_port)) {
331 backing_store_alert(notify_port,
332 LO_WAT_ALERT);
333 mach_port_deallocate(mach_task_self(),
334 notify_port);
756446ec 335 notify_port = MACH_PORT_NULL;
1815bff5 336 notify_high = 0;
1815bff5
A
337 bs_recovery = 0;
338 }
339 } else
c3a08f59 340 bs_recovery = bs_recovery-cur_size;
1815bff5 341 }
c3a08f59 342 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
1815bff5
A
343 }
344 if(flags & LO_WAT_ALERT) {
1815bff5
A
345 sprintf(subfile, "%s%d", fileroot, file_count);
346 if(hi_water < notify_high) {
347 local_hi_water = notify_high;
348 } else {
349 local_hi_water = hi_water;
350 }
756446ec 351 if((bs_recovery != 0) && (notify_port != MACH_PORT_NULL)) {
1815bff5
A
352 backing_store_alert(notify_port, LO_WAT_ALERT);
353 mach_port_deallocate(mach_task_self(), notify_port);
756446ec 354 notify_port = MACH_PORT_NULL;
1815bff5 355 notify_high = 0;
1815bff5
A
356 bs_recovery = 0;
357 }
c3a08f59
A
358 if((error = macx_swapoff(subfile, flags)) == 0) {
359
1815bff5
A
360 unlink(subfile);
361 file_count--;
c3a08f59
A
362
363 if (file_count > max_valid)
364 cur_limits = max_valid;
365 else
366 cur_limits = file_count;
367 } else {
368 if (file_count > max_valid)
369 cur_limits = max_valid;
370 else
371 cur_limits = file_count;
1815bff5 372 }
c3a08f59
A
373 /*
374 * only need to reset the LO_WAT_ALERT... the HI_WAT size is fixed,
375 * it doesn't change even if the swap file size shrinks or grows
376 */
377 macx_triggers(local_hi_water, limits[cur_limits].low_water, LO_WAT_ALERT, alert_port);
1815bff5
A
378 }
379 return KERN_SUCCESS;
380}
381
382void
383wait_on_paging_trigger(trigger_port)
384 mach_port_t trigger_port;
385{
386 kern_return_t result;
387 result = server_alert_loop(4096, trigger_port, MACH_MSG_OPTION_NONE);
388 if (result != KERN_SUCCESS) {
389 fprintf(stderr, "dynamic_pager: default pager alert failed\n");
83f6dbe8 390 exit(EXIT_FAILURE);
1815bff5 391 }
83f6dbe8 392 exit(EXIT_SUCCESS);
1815bff5
A
393}
394
395void
396paging_setup(flags, size, priority, low, high)
397 int flags;
398 int size;
399 int priority;
400 int low;
401 int high;
402{
403 off_t filesize = size;
404 char subfile[512];
405 FILE *file_ptr;
756446ec 406 int error;
1815bff5
A
407
408 file_count = 0;
409 sprintf(subfile, "%s%d", fileroot, file_count);
410 file_ptr = fopen(subfile, "w+");
83f6dbe8
A
411 if (file_ptr == NULL) {
412 fprintf(stderr, "dynamic_pager: cannot create paging file %s!\n",
413 subfile);
414 exit(EXIT_FAILURE);
415 }
1815bff5 416 fchmod(fileno(file_ptr), (mode_t)01600);
83f6dbe8 417
756446ec
A
418 error = fcntl(fileno(file_ptr), F_SETSIZE, &filesize);
419 if(error) {
83f6dbe8 420 error = ftruncate(fileno(file_ptr), filesize);
756446ec 421 }
1815bff5 422 fclose(file_ptr);
83f6dbe8
A
423
424 if (error == -1) {
425 fprintf(stderr, "dynamic_pager: cannot extend paging file size %s to %llu!\n",
426 subfile, filesize);
427 exit(EXIT_FAILURE);
428 }
1815bff5
A
429
430 macx_swapon(subfile, flags, size, priority);
c3a08f59 431
1815bff5 432 if(hi_water) {
1c51fdde
A
433 mach_msg_type_name_t poly;
434
1815bff5
A
435 daemon(0,0);
436
437 if (mach_port_allocate(mach_task_self(),
438 MACH_PORT_RIGHT_RECEIVE,
439 &trigger_port) != KERN_SUCCESS) {
83f6dbe8
A
440 fprintf(stderr,"dynamic_pager: allocation of trigger port failed\n");
441 exit(EXIT_FAILURE);
1815bff5 442 }
1c51fdde
A
443 /* create a send right on our local port */
444 mach_port_extract_right(mach_task_self(), trigger_port,
445 MACH_MSG_TYPE_MAKE_SEND, &trigger_port, &poly);
1815bff5 446 macx_triggers(high, low, HI_WAT_ALERT, trigger_port);
c3a08f59 447
1815bff5 448 if(low) {
c3a08f59 449 macx_triggers(high, low, LO_WAT_ALERT, trigger_port);
1815bff5
A
450 }
451 /* register control port for applications wishing to */
452 /* get backing store notifications or change dynamic */
453 /* pager settings. */
454 set_dp_control_port(mach_host_self(), trigger_port);
455 wait_on_paging_trigger(trigger_port);
456 }
83f6dbe8 457 exit(EXIT_SUCCESS);
1815bff5
A
458}
459int
460main(int argc, char **argv)
461{
462 extern char *optarg;
463 extern int optind;
464 char default_filename[] = "/private/var/vm/swapfile";
465 int ch;
c3a08f59 466 int variable_sized = 1;
1815bff5
A
467
468 seteuid(getuid());
469 strcpy(fileroot, default_filename);
470
c3a08f59
A
471 limits[0].size = 20000000;
472 limits[0].low_water = 0;
473
474 hi_water = 0;
475 local_hi_water = 0;
476
477
1815bff5
A
478 while ((ch = getopt(argc, argv, "F:L:H:S:P:O:")) != EOF) {
479 switch((char)ch) {
480
481 case 'F':
482 strncpy(fileroot, optarg, 500);
483 break;
484
485 case 'L':
c3a08f59
A
486 variable_sized = 0;
487 limits[0].low_water = atoi(optarg);
1815bff5
A
488 break;
489 case 'H':
c3a08f59 490 variable_sized = 0;
1815bff5
A
491 hi_water = atoi(optarg);
492 break;
493 case 'S':
c3a08f59
A
494 variable_sized = 0;
495 limits[0].size = atoi(optarg);
1815bff5
A
496 break;
497 case 'P':
498 priority = atoi(optarg);
499 break;
500
501 default:
502 (void)fprintf(stderr,
503 "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n");
83f6dbe8 504 exit(EXIT_FAILURE);
1815bff5
A
505 }
506 }
c3a08f59
A
507
508 if (variable_sized) {
509 static char tmp[1024];
510 struct statfs sfs;
511 char *q;
512 int i;
513 int mib[4];
514 size_t len;
515 unsigned int size;
516 u_int64_t memsize;
517 u_int64_t fs_limit;
518
519 /*
520 * if we get here, then none of the following options were specified... -L, H, or -S
521 * drop into a new mode that scales the size of the swap file based on how much free
522 * space is left on the volume being used for swap and the amount of physical ram
523 * installed on the system...
524 * basically, we'll pick a maximum size that doesn't exceed the following limits...
83f6dbe8 525 * 1/8 the remaining free space of the swap volume
c3a08f59
A
526 * the size of phsyical ram
527 * MAXIMUM_SIZE - currently set to 1 Gbyte...
528 * once we have the maximum, we'll create a list of sizes and low_water limits
529 * we'll start with 2 files of MINIMUM_SIZE - currently 64 Mbytes...
530 * subsequent entries will double in size up to the calculated maximum... the low_water
531 * limit will be the sum of the current file size and the previous file size for each entry...
532 * as we add or delete files, we'll use the current file_count as an index into this
533 * table... if it's beyond the table size, we'll use the last entry
534 * the table entry will determine the size of the file to be created and the new low_water mark...
535 * the high_water mark is set to HI_WATER_DEFAULT which must be smaller than MINIMUM_SIZE...
536 * currently it is set to 40,000,000 to match the size being requested by the application
537 * monitoring low space conditions... having it set to the same size keeps us from creating
538 * an additional swap file when it really isn't necessary
539 */
540
541 /*
542 * get rid of the filename at the end of the swap file specification
543 * we only want the portion of the pathname that should already exist
544 */
545 strcpy(tmp, fileroot);
546 if (q = strrchr(tmp, '/'))
547 *q = 0;
548
549 if (statfs(tmp, &sfs) != -1) {
550 /*
83f6dbe8 551 * Limit the maximum size of a swap file to 1/8 the free
c3a08f59 552 * space available on the filesystem where the swap files
83f6dbe8
A
553 * are to reside. This will allow us to allocate and
554 * deallocate in finer increments on systems without much
555 * free space.
c3a08f59 556 */
83f6dbe8 557 fs_limit = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize) / 8;
c3a08f59
A
558
559 } else {
83f6dbe8
A
560 (void)fprintf(stderr, "dynamic_pager: swap directory must exist\n");
561 exit(EXIT_FAILURE);
c3a08f59
A
562 }
563 mib[0] = CTL_HW;
564 mib[1] = HW_MEMSIZE;
565 len = sizeof(u_int64_t);
566
567 if (sysctl(mib, 2, &memsize, &len, NULL, 0) < 0) {
568 /*
569 * if the sysctl fails for some reason
570 * use the starting size as the default
571 */
572 memsize = MINIMUM_SIZE;
573 }
574 if (memsize > fs_limit)
575 /*
576 * clip based on filesystem space available
577 */
578 memsize = fs_limit;
579
580 /*
581 * further limit the maximum size of a swap file
582 */
583 if (memsize > MAXIMUM_SIZE)
584 memsize = MAXIMUM_SIZE;
585
586 size = MINIMUM_SIZE;
587
588 /*
589 * start small and work our way up to the maximum
590 * sized allowed... this way, we don't tie up too
591 * much disk space if we never do any real paging
592 */
593 for (max_valid = 0, i = 0; i < MAX_LIMITS; i++) {
594 limits[i].size = size;
595
596 if (i == 0)
597 limits[i].low_water = size * 2;
598 else {
599 if ((limits[i - 1].size / 2) > HI_WATER_DEFAULT)
600 limits[i].low_water = size + (limits[i - 1].size / 2);
601 else
602 limits[i].low_water = size + limits[i - 1].size;
603 }
c3a08f59
A
604
605 if (i) {
606 /*
607 * make the first 2 files the same size
608 */
609 size = size * 2;
83f6dbe8
A
610 if (size > memsize)
611 break;
c3a08f59
A
612 }
613 max_valid++;
614 }
615 if (max_valid >= MAX_LIMITS)
616 max_valid = MAX_LIMITS - 1;
617
618 hi_water = HI_WATER_DEFAULT;
619 }
1815bff5 620 local_hi_water = hi_water;
c3a08f59
A
621
622 if((limits[0].low_water != 0) && (limits[0].low_water <= (limits[0].size + hi_water))) {
83f6dbe8
A
623 (void)fprintf(stderr, "dynamic_pager: low water trigger must be larger than size + hi_water\n");
624 exit(EXIT_FAILURE);
1815bff5
A
625 }
626 argc -= optind;
627 argv += optind;
c3a08f59
A
628
629 paging_setup(0, limits[0].size, priority, limits[0].low_water, hi_water);
630
1815bff5
A
631 return (0);
632}