]> git.saurik.com Git - apple/system_cmds.git/blame - dynamic_pager.tproj/dynamic_pager.c
system_cmds-498.2.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
1815bff5 13#include <mach/mach_syscalls.h>
ef8ad44b 14#include <mach/mach_traps.h>
1815bff5
A
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>
34d340d7 23#include <sys/types.h>
1815bff5 24#include <errno.h>
1815bff5
A
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>
34d340d7
A
33#include <dirent.h>
34
34d340d7 35#include <IOKit/ps/IOPowerSourcesPrivate.h>
ef8ad44b
A
36#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
37#include <CoreFoundation/CoreFoundation.h>
1815bff5
A
38
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>
43
c3a08f59
A
44
45/*
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
49 * above this value.
50 */
51#define HI_WATER_DEFAULT 40000000
52#define MINIMUM_SIZE (1024 * 1024 * 64)
53#define MAXIMUM_SIZE (1024 * 1024 * 1024)
54
55#define MAX_LIMITS 8
56
57
58struct limit {
59 unsigned int size;
60 unsigned int low_water;
61} limits[MAX_LIMITS];
62
63
1815bff5 64int debug = 0;
c3a08f59 65int max_valid = 0;
1815bff5 66int file_count = 0;
c3a08f59
A
67unsigned int hi_water;
68unsigned int local_hi_water;
1815bff5 69int priority = 0;
c3a08f59 70int options = 0;
1815bff5
A
71char fileroot[512];
72
73
74/* global parameters for application notification option */
756446ec
A
75mach_port_t trigger_port = MACH_PORT_NULL;
76mach_port_t notify_port = MACH_PORT_NULL;
c3a08f59
A
77unsigned int notify_high = 0;
78unsigned int bs_recovery;
1815bff5
A
79
80/*
81void setprof __P((struct kvmvars *kvp, int state));
82void dumpstate __P((struct kvmvars *kvp));
83void reset __P((struct kvmvars *kvp));
84*/
85
86
87
88mach_msg_return_t
89server_alert_loop(
90 mach_msg_size_t max_size,
91 mach_port_t rcv_name,
92 mach_msg_options_t options)
93{
94 mig_reply_error_t *bufRequest = 0, *bufReply = 0;
95 register mach_msg_return_t mr;
96 register kern_return_t kr;
97
98 if ((kr = vm_allocate(mach_task_self(),
99 (vm_address_t *)&bufRequest,
100 max_size + MAX_TRAILER_SIZE,
101 TRUE)) != KERN_SUCCESS)
102 return kr;
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;
109 mlock(bufReply, max_size + MAX_TRAILER_SIZE);
110 while(TRUE) {
83f6dbe8
A
111 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
112 0, max_size, rcv_name,
113 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
1815bff5
A
114 if (mr == MACH_MSG_SUCCESS) {
115 /* we have a request message */
116
117 if(!(default_pager_alerts_server(
118 &bufRequest->Head, &bufReply->Head)))
119 backing_store_triggers_server(
120 &bufRequest->Head, &bufReply->Head);
121
122 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
123 bufReply->RetCode != KERN_SUCCESS) {
124 if (bufReply->RetCode == MIG_NO_REPLY)
125 /*
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
131 * ok.
132 */
133
134 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
135 mach_msg_destroy(&bufRequest->Head);
136 continue;
137 }
138
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);
143 } else {
144 break;
145 }
146 } else {
147 break;
148 }
149 }
150
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);
157 return KERN_FAILURE;
158
159}
160
161
162kern_return_t
163backing_store_triggers(dynamic_pager, hi_wat, flags, port)
164 mach_port_t dynamic_pager;
165 int hi_wat;
166 int flags;
167 mach_port_t port;
168{
c3a08f59
A
169 int cur_limits;
170
171 if (file_count > max_valid)
172 cur_limits = max_valid;
173 else
174 cur_limits = file_count;
175
176 if((hi_wat + limits[cur_limits].size) > limits[cur_limits].low_water)
1815bff5 177 return KERN_FAILURE; /* let ipc system clean up port */
756446ec
A
178
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;
183 }
184
1815bff5
A
185 notify_port = port;
186 notify_high = hi_wat;
187 if(hi_water < notify_high) {
188 local_hi_water = notify_high;
189 } else {
190 local_hi_water = hi_water;
191 }
192 if(notify_high > hi_water) {
193 default_pager_space_alert(trigger_port, HI_WAT_ALERT);
194 }
195 return KERN_SUCCESS;
196}
197
198
199kern_return_t
200default_pager_space_alert(alert_port, flags)
201 mach_port_t alert_port;
202 int flags;
203{
204 char subfile[512];
c3a08f59 205 off_t filesize;
ef8ad44b 206 int error=0, fd=0;
756446ec 207 kern_return_t ret;
c3a08f59
A
208 int cur_limits;
209 unsigned int cur_size;
210 unsigned int notifications;
1815bff5 211
c3a08f59 212
1815bff5 213 if(flags & HI_WAT_ALERT) {
c3a08f59 214
1815bff5 215 file_count++;
c3a08f59
A
216
217 if (file_count > max_valid)
218 cur_limits = max_valid;
219 else
220 cur_limits = file_count;
221
222 cur_size = limits[cur_limits].size;
223 filesize = cur_size;
224
225 /*
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
230 */
231 if (limits[cur_limits].low_water)
232 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
233 else
234 notifications = HI_WAT_ALERT;
235
1815bff5 236 sprintf(subfile, "%s%d", fileroot, file_count);
ef8ad44b
A
237 fd = open(subfile, O_CREAT|O_EXCL|O_RDWR,(mode_t)(S_IRUSR|S_IWUSR));
238 if (fd == -1) {
83f6dbe8
A
239 /* force error recovery below */
240 error = -1;
241 } else {
ef8ad44b 242 error = fcntl(fd, F_SETSIZE, &filesize);
83f6dbe8 243 if(error) {
ef8ad44b 244 error = ftruncate(fd, filesize);
83f6dbe8
A
245 }
246 if(error)
247 unlink(subfile);
ef8ad44b 248 close(fd);
756446ec 249 }
c3a08f59 250
1815bff5 251 if(error == -1) {
1815bff5 252 file_count--;
c3a08f59
A
253
254 if (file_count > max_valid)
255 cur_limits = max_valid;
256 else
257 cur_limits = file_count;
258
259 if (limits[cur_limits].low_water)
260 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
261 else
262 notifications = HI_WAT_ALERT;
ef8ad44b
A
263
264 notifications |= SWAP_FILE_CREATION_ERROR;
c3a08f59 265
1815bff5
A
266 local_hi_water = local_hi_water>>2;
267 if(notify_high >= (local_hi_water)) {
756446ec 268 if(notify_port != MACH_PORT_NULL) {
1815bff5
A
269 /* notify monitoring app of */
270 /* backing store shortage */
271 backing_store_alert(notify_port,
272 HI_WAT_ALERT);
273 mach_port_deallocate(mach_task_self(),
274 notify_port);
756446ec 275 notify_port = MACH_PORT_NULL;
1815bff5 276 notify_high = 0;
1815bff5
A
277 }
278 }
1815bff5
A
279 } else {
280 if(hi_water < notify_high) {
281 if(local_hi_water < notify_high) {
282 bs_recovery = notify_high - local_hi_water;
283 }
284 local_hi_water = notify_high;
285 } else {
286 if(local_hi_water < hi_water) {
287 bs_recovery = hi_water - local_hi_water;
288 }
289 local_hi_water = hi_water;
290 }
ef8ad44b
A
291 ret = macx_swapon((uint64_t)(uintptr_t)subfile,
292 flags, cur_size, priority);
c3a08f59 293
756446ec
A
294 if(ret) {
295 unlink(subfile);
296 file_count--;
c3a08f59
A
297
298 if (file_count > max_valid)
299 cur_limits = max_valid;
300 else
301 cur_limits = file_count;
302
303 if (limits[cur_limits].low_water)
304 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
305 else
306 notifications = HI_WAT_ALERT;
307
756446ec
A
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 */
313 backing_store_alert(
314 notify_port,
315 HI_WAT_ALERT);
316 mach_port_deallocate(
317 mach_task_self(),
318 notify_port);
319 notify_port = MACH_PORT_NULL;
320 notify_high = 0;
321 }
322 }
c3a08f59 323 } else if(bs_recovery <= cur_size) {
1815bff5
A
324 if((bs_recovery != 0) && (notify_port)) {
325 backing_store_alert(notify_port,
326 LO_WAT_ALERT);
327 mach_port_deallocate(mach_task_self(),
328 notify_port);
756446ec 329 notify_port = MACH_PORT_NULL;
1815bff5 330 notify_high = 0;
1815bff5
A
331 bs_recovery = 0;
332 }
333 } else
c3a08f59 334 bs_recovery = bs_recovery-cur_size;
1815bff5 335 }
c3a08f59 336 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
1815bff5
A
337 }
338 if(flags & LO_WAT_ALERT) {
1815bff5
A
339 sprintf(subfile, "%s%d", fileroot, file_count);
340 if(hi_water < notify_high) {
341 local_hi_water = notify_high;
342 } else {
343 local_hi_water = hi_water;
344 }
756446ec 345 if((bs_recovery != 0) && (notify_port != MACH_PORT_NULL)) {
1815bff5
A
346 backing_store_alert(notify_port, LO_WAT_ALERT);
347 mach_port_deallocate(mach_task_self(), notify_port);
756446ec 348 notify_port = MACH_PORT_NULL;
1815bff5 349 notify_high = 0;
1815bff5
A
350 bs_recovery = 0;
351 }
ef8ad44b
A
352 if((error = macx_swapoff((uint64_t)(uintptr_t)subfile,
353 flags)) == 0) {
c3a08f59 354
1815bff5
A
355 unlink(subfile);
356 file_count--;
c3a08f59
A
357
358 if (file_count > max_valid)
359 cur_limits = max_valid;
360 else
361 cur_limits = file_count;
362 } else {
363 if (file_count > max_valid)
364 cur_limits = max_valid;
365 else
366 cur_limits = file_count;
1815bff5 367 }
c3a08f59
A
368 /*
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
371 */
372 macx_triggers(local_hi_water, limits[cur_limits].low_water, LO_WAT_ALERT, alert_port);
1815bff5
A
373 }
374 return KERN_SUCCESS;
375}
376
377void
378wait_on_paging_trigger(trigger_port)
379 mach_port_t trigger_port;
380{
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");
83f6dbe8 385 exit(EXIT_FAILURE);
1815bff5 386 }
83f6dbe8 387 exit(EXIT_SUCCESS);
1815bff5
A
388}
389
390void
2fc1e207 391paging_setup(flags, size, priority, low, high, encrypted)
1815bff5
A
392 int flags;
393 int size;
394 int priority;
395 int low;
396 int high;
2fc1e207 397 boolean_t encrypted;
1815bff5
A
398{
399 off_t filesize = size;
400 char subfile[512];
ef8ad44b 401 int error, fd = 0;
1815bff5
A
402
403 file_count = 0;
404 sprintf(subfile, "%s%d", fileroot, file_count);
ef8ad44b
A
405 fd = open(subfile, O_CREAT|O_EXCL|O_RDWR, ((mode_t)(S_IRUSR|S_IWUSR)));
406 if (fd == -1) {
83f6dbe8
A
407 fprintf(stderr, "dynamic_pager: cannot create paging file %s!\n",
408 subfile);
409 exit(EXIT_FAILURE);
410 }
83f6dbe8 411
ef8ad44b 412 error = fcntl(fd, F_SETSIZE, &filesize);
756446ec 413 if(error) {
ef8ad44b 414 error = ftruncate(fd, filesize);
756446ec 415 }
ef8ad44b 416 close(fd);
83f6dbe8
A
417
418 if (error == -1) {
419 fprintf(stderr, "dynamic_pager: cannot extend paging file size %s to %llu!\n",
420 subfile, filesize);
421 exit(EXIT_FAILURE);
422 }
1815bff5 423
2fc1e207
A
424 if (macx_triggers(0, 0,
425 (encrypted
426 ? SWAP_ENCRYPT_ON
427 : SWAP_ENCRYPT_OFF),
428 MACH_PORT_NULL) != 0) {
429 fprintf(stderr,
430 "dynamic_pager: warning: "
431 "could not turn encrypted swap %s\n",
432 (encrypted ? "on" : "off"));
433 }
434
ef8ad44b 435 macx_swapon((uint64_t)(uintptr_t)subfile, flags, size, priority);
c3a08f59 436
1815bff5 437 if(hi_water) {
1c51fdde
A
438 mach_msg_type_name_t poly;
439
1815bff5
A
440 if (mach_port_allocate(mach_task_self(),
441 MACH_PORT_RIGHT_RECEIVE,
442 &trigger_port) != KERN_SUCCESS) {
83f6dbe8
A
443 fprintf(stderr,"dynamic_pager: allocation of trigger port failed\n");
444 exit(EXIT_FAILURE);
1815bff5 445 }
1c51fdde
A
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);
1815bff5 449 macx_triggers(high, low, HI_WAT_ALERT, trigger_port);
c3a08f59 450
1815bff5 451 if(low) {
c3a08f59 452 macx_triggers(high, low, LO_WAT_ALERT, trigger_port);
1815bff5
A
453 }
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);
459 }
83f6dbe8 460 exit(EXIT_SUCCESS);
1815bff5 461}
34d340d7
A
462
463static void
464clean_swap_directory(const char *path)
465{
466 DIR *dir;
467 struct dirent *entry;
468 char buf[1024];
469
470 dir = opendir(path);
471 if (dir == NULL) {
472 fprintf(stderr,"dynamic_pager: cannot open swap directory %s\n", path);
473 exit(EXIT_FAILURE);
474 }
475
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);
479 unlink(buf);
480 }
481 }
482
483 closedir(dir);
484}
485
486#define VM_PREFS_PLIST "/Library/Preferences/com.apple.virtualMemory.plist"
487#define VM_PREFS_ENCRYPT_SWAP_KEY "UseEncryptedSwap"
488
489static boolean_t
490should_encrypt_swap(void)
491{
492 CFPropertyListRef propertyList;
493 CFTypeID propertyListType;
494 CFStringRef errorString;
495 CFDataRef resourceData;
496 SInt32 errorCode;
497 CFURLRef fileURL;
498 CFTypeRef value;
499 boolean_t should_encrypt;
500 boolean_t explicit_value;
34d340d7
A
501
502 explicit_value = false;
ef8ad44b 503 should_encrypt = true;
34d340d7
A
504
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);*/
508 goto done;
509 }
510
511 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
512 /*fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), VM_PREFS_PLIST, (int)errorCode);*/
513 CFRelease(fileURL);
514 goto done;
515 }
516
517 CFRelease(fileURL);
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);
522 goto done;
523 }
524
525 propertyListType = CFGetTypeID(propertyList);
526
527 if (propertyListType == CFDictionaryGetTypeID()) {
528 value = (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef) propertyList, CFSTR(VM_PREFS_ENCRYPT_SWAP_KEY));
529 if (value == NULL) {
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;
537 } else {
538 should_encrypt = CFBooleanGetValue((CFBooleanRef)value);
539 explicit_value = true;
540 }
541 }
542 else {
543 /*fprintf(stderr, "%s: invalid propertyList type %d (not a dictionary)\n", getprogname(), propertyListType);*/
544 }
545 CFRelease(resourceData);
546 CFRelease(propertyList);
547
548done:
549 if (! explicit_value) {
ef8ad44b
A
550#if TARGET_OS_EMBEDDED
551 should_encrypt = FALSE;
552#else
34d340d7 553 /* by default, encrypt swap on laptops only */
ef8ad44b
A
554 /*
555 * Look for battery power source.
556 */
557 should_encrypt = (kCFBooleanTrue == IOPSPowerSourceSupported(NULL, CFSTR(kIOPMBatteryPowerKey)));
558 /*fprintf(stderr, "dynamic_pager: battery power source: %d\n", should_encrypt);*/
559#endif
34d340d7
A
560 }
561
562 return should_encrypt;
563}
564
1815bff5
A
565int
566main(int argc, char **argv)
567{
568 extern char *optarg;
569 extern int optind;
570 char default_filename[] = "/private/var/vm/swapfile";
571 int ch;
ef8ad44b 572 int variable_sized = 1,flags=0;
34d340d7
A
573 boolean_t encrypted_swap;
574
575/*
576 setlinebuf(stdout);
577 setlinebuf(stderr);
578*/
1815bff5
A
579
580 seteuid(getuid());
581 strcpy(fileroot, default_filename);
582
ef8ad44b 583retry:
c3a08f59
A
584 limits[0].size = 20000000;
585 limits[0].low_water = 0;
586
587 hi_water = 0;
588 local_hi_water = 0;
589
34d340d7 590 encrypted_swap = should_encrypt_swap();
c3a08f59 591
34d340d7 592 while ((ch = getopt(argc, argv, "EF:L:H:S:P:QO:")) != EOF) {
1815bff5
A
593 switch((char)ch) {
594
2fc1e207
A
595 case 'E':
596 encrypted_swap = TRUE;
597 break;
598
1815bff5
A
599 case 'F':
600 strncpy(fileroot, optarg, 500);
601 break;
602
603 case 'L':
c3a08f59
A
604 variable_sized = 0;
605 limits[0].low_water = atoi(optarg);
1815bff5
A
606 break;
607 case 'H':
c3a08f59 608 variable_sized = 0;
1815bff5
A
609 hi_water = atoi(optarg);
610 break;
611 case 'S':
c3a08f59
A
612 variable_sized = 0;
613 limits[0].size = atoi(optarg);
1815bff5
A
614 break;
615 case 'P':
616 priority = atoi(optarg);
617 break;
618
34d340d7
A
619 case 'Q':
620 /* just query for "encrypted swap" default value */
621 fprintf(stdout,
622 "dynamic_pager: encrypted swap will be %s\n",
623 encrypted_swap ? "ON": "OFF");
624 exit(0);
625
1815bff5
A
626 default:
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");
83f6dbe8 629 exit(EXIT_FAILURE);
1815bff5
A
630 }
631 }
c3a08f59
A
632
633 if (variable_sized) {
634 static char tmp[1024];
635 struct statfs sfs;
636 char *q;
637 int i;
638 int mib[4];
639 size_t len;
640 unsigned int size;
641 u_int64_t memsize;
34d340d7 642 u_int64_t fs_limit = 0;
c3a08f59
A
643
644 /*
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...
83f6dbe8 650 * 1/8 the remaining free space of the swap volume
c3a08f59
A
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
664 */
665
666 /*
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
669 */
670 strcpy(tmp, fileroot);
ef8ad44b 671 if ((q = strrchr(tmp, '/')))
c3a08f59
A
672 *q = 0;
673
ef8ad44b
A
674 /*
675 * Remove all files in the swap directory.
676 */
677 clean_swap_directory(tmp);
678
34d340d7
A
679 if (statfs(tmp, &sfs) == -1) {
680 /*
681 * Setup the swap directory.
682 */
683 if (mkdir(tmp, 0755) == -1) {
684 (void)fprintf(stderr, "dynamic_pager: cannot create swap directory %s\n", tmp);
685 exit(EXIT_FAILURE);
686 }
687 chown(tmp, 0, 0);
688
689 if (statfs(tmp, &sfs) == -1) {
690 /*
691 * We really can't get filesystem status,
692 * so let's not limit the swap files...
693 */
694 fs_limit = (u_int64_t) -1;
695 }
696 }
697
34d340d7
A
698 if (fs_limit != (u_int64_t) -1) {
699 /*
83f6dbe8 700 * Limit the maximum size of a swap file to 1/8 the free
c3a08f59 701 * space available on the filesystem where the swap files
83f6dbe8
A
702 * are to reside. This will allow us to allocate and
703 * deallocate in finer increments on systems without much
704 * free space.
c3a08f59 705 */
83f6dbe8 706 fs_limit = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize) / 8;
c3a08f59 707 }
34d340d7 708
c3a08f59
A
709 mib[0] = CTL_HW;
710 mib[1] = HW_MEMSIZE;
711 len = sizeof(u_int64_t);
712
713 if (sysctl(mib, 2, &memsize, &len, NULL, 0) < 0) {
714 /*
715 * if the sysctl fails for some reason
716 * use the starting size as the default
717 */
718 memsize = MINIMUM_SIZE;
719 }
720 if (memsize > fs_limit)
721 /*
722 * clip based on filesystem space available
723 */
724 memsize = fs_limit;
725
726 /*
727 * further limit the maximum size of a swap file
728 */
ef8ad44b
A
729 if (memsize <= MINIMUM_SIZE) {
730 (void)fprintf(stderr, "dynamic_pager: Need more space on the disk to enable swapping.\n");
731 sleep(30);
732 goto retry;
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) {
c3a08f59 737 memsize = MAXIMUM_SIZE;
ef8ad44b 738 }
c3a08f59
A
739
740 size = MINIMUM_SIZE;
741
742 /*
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
746 */
747 for (max_valid = 0, i = 0; i < MAX_LIMITS; i++) {
748 limits[i].size = size;
749
750 if (i == 0)
751 limits[i].low_water = size * 2;
752 else {
753 if ((limits[i - 1].size / 2) > HI_WATER_DEFAULT)
754 limits[i].low_water = size + (limits[i - 1].size / 2);
755 else
756 limits[i].low_water = size + limits[i - 1].size;
757 }
c3a08f59
A
758
759 if (i) {
760 /*
761 * make the first 2 files the same size
762 */
763 size = size * 2;
83f6dbe8
A
764 if (size > memsize)
765 break;
c3a08f59
A
766 }
767 max_valid++;
768 }
769 if (max_valid >= MAX_LIMITS)
770 max_valid = MAX_LIMITS - 1;
771
772 hi_water = HI_WATER_DEFAULT;
773 }
1815bff5 774 local_hi_water = hi_water;
c3a08f59
A
775
776 if((limits[0].low_water != 0) && (limits[0].low_water <= (limits[0].size + hi_water))) {
83f6dbe8
A
777 (void)fprintf(stderr, "dynamic_pager: low water trigger must be larger than size + hi_water\n");
778 exit(EXIT_FAILURE);
1815bff5
A
779 }
780 argc -= optind;
781 argv += optind;
c3a08f59 782
ef8ad44b 783 paging_setup(flags, limits[0].size, priority, limits[0].low_water, hi_water,
2fc1e207 784 encrypted_swap);
c3a08f59 785
1815bff5
A
786 return (0);
787}