]> git.saurik.com Git - apple/system_cmds.git/blame - dynamic_pager.tproj/dynamic_pager.c
system_cmds-433.8.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
A
13#include <mach/mach_syscalls.h>
14#include <mach/mig_errors.h>
15#include <sys/param.h>
c3a08f59 16#include <sys/mount.h>
1815bff5
A
17#include <sys/file.h>
18#include <sys/mman.h>
19#include <sys/stat.h>
20#include <sys/sysctl.h>
21#include <sys/gmon.h>
34d340d7 22#include <sys/types.h>
1815bff5 23#include <errno.h>
1815bff5
A
24#include <limits.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <nlist.h>
29#include <ctype.h>
30#include <unistd.h>
31#include <paths.h>
34d340d7
A
32#include <dirent.h>
33
34#include <CoreFoundation/CoreFoundation.h>
35#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
36#include <IOKit/ps/IOPowerSources.h>
37#include <IOKit/ps/IOPowerSourcesPrivate.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;
1c51fdde
A
103 if ((kr = vm_protect(mach_task_self(),
104 (vm_address_t)bufRequest,
105 max_size + MAX_TRAILER_SIZE,
106 FALSE, VM_PROT_ALL)) != KERN_SUCCESS)
107 return kr;
1815bff5
A
108 mlock(bufRequest, max_size + MAX_TRAILER_SIZE);
109 if ((kr = vm_allocate(mach_task_self(),
110 (vm_address_t *)&bufReply,
111 max_size + MAX_TRAILER_SIZE,
112 TRUE)) != KERN_SUCCESS)
113 return kr;
1c51fdde
A
114 if ((kr = vm_protect(mach_task_self(),
115 (vm_address_t)bufReply,
116 max_size + MAX_TRAILER_SIZE,
117 FALSE, VM_PROT_ALL)) != KERN_SUCCESS)
118 return kr;
1815bff5
A
119 mlock(bufReply, max_size + MAX_TRAILER_SIZE);
120 while(TRUE) {
83f6dbe8
A
121 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options,
122 0, max_size, rcv_name,
123 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
1815bff5
A
124 if (mr == MACH_MSG_SUCCESS) {
125 /* we have a request message */
126
127 if(!(default_pager_alerts_server(
128 &bufRequest->Head, &bufReply->Head)))
129 backing_store_triggers_server(
130 &bufRequest->Head, &bufReply->Head);
131
132 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
133 bufReply->RetCode != KERN_SUCCESS) {
134 if (bufReply->RetCode == MIG_NO_REPLY)
135 /*
136 * This return code is a little tricky--
137 * it appears that the demux routine found an
138 * error of some sort, but since that error
139 * would not normally get returned either to
140 * the local user or the remote one, we pretend it's
141 * ok.
142 */
143
144 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
145 mach_msg_destroy(&bufRequest->Head);
146 continue;
147 }
148
149 if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
150 /* no reply port, so destroy the reply */
151 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
152 mach_msg_destroy(&bufReply->Head);
153 } else {
154 break;
155 }
156 } else {
157 break;
158 }
159 }
160
161 (void)vm_deallocate(mach_task_self(),
162 (vm_address_t) bufRequest,
163 max_size + MAX_TRAILER_SIZE);
164 (void)vm_deallocate(mach_task_self(),
165 (vm_address_t) bufReply,
166 max_size + MAX_TRAILER_SIZE);
167 return KERN_FAILURE;
168
169}
170
171
172kern_return_t
173backing_store_triggers(dynamic_pager, hi_wat, flags, port)
174 mach_port_t dynamic_pager;
175 int hi_wat;
176 int flags;
177 mach_port_t port;
178{
c3a08f59
A
179 int cur_limits;
180
181 if (file_count > max_valid)
182 cur_limits = max_valid;
183 else
184 cur_limits = file_count;
185
186 if((hi_wat + limits[cur_limits].size) > limits[cur_limits].low_water)
1815bff5 187 return KERN_FAILURE; /* let ipc system clean up port */
756446ec
A
188
189 /* If there was a previous registration, throw it away */
190 if (notify_port != MACH_PORT_NULL) {
191 mach_port_deallocate(mach_task_self(), notify_port);
192 notify_port = MACH_PORT_NULL;
193 }
194
1815bff5
A
195 notify_port = port;
196 notify_high = hi_wat;
197 if(hi_water < notify_high) {
198 local_hi_water = notify_high;
199 } else {
200 local_hi_water = hi_water;
201 }
202 if(notify_high > hi_water) {
203 default_pager_space_alert(trigger_port, HI_WAT_ALERT);
204 }
205 return KERN_SUCCESS;
206}
207
208
209kern_return_t
210default_pager_space_alert(alert_port, flags)
211 mach_port_t alert_port;
212 int flags;
213{
214 char subfile[512];
215 FILE *file_ptr;
c3a08f59 216 off_t filesize;
1815bff5 217 int error;
756446ec 218 kern_return_t ret;
c3a08f59
A
219 int cur_limits;
220 unsigned int cur_size;
221 unsigned int notifications;
1815bff5 222
c3a08f59 223
1815bff5 224 if(flags & HI_WAT_ALERT) {
c3a08f59 225
1815bff5 226 file_count++;
c3a08f59
A
227
228 if (file_count > max_valid)
229 cur_limits = max_valid;
230 else
231 cur_limits = file_count;
232
233 cur_size = limits[cur_limits].size;
234 filesize = cur_size;
235
236 /*
237 * because the LO_WAT threshold changes relative to
238 * the size of the swap file we're creating
239 * we need to reset the LO_WAT_ALERT threshold each
240 * time we create a new swap file
241 */
242 if (limits[cur_limits].low_water)
243 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
244 else
245 notifications = HI_WAT_ALERT;
246
1815bff5
A
247 sprintf(subfile, "%s%d", fileroot, file_count);
248 file_ptr = fopen(subfile, "w+");
83f6dbe8
A
249 if (file_ptr == NULL) {
250 /* force error recovery below */
251 error = -1;
252 } else {
253 fchmod(fileno(file_ptr), (mode_t)01600);
254 error = fcntl(fileno(file_ptr), F_SETSIZE, &filesize);
255 if(error) {
256 error = ftruncate(fileno(file_ptr), filesize);
257 }
258 if(error)
259 unlink(subfile);
260 fclose(file_ptr);
756446ec 261 }
c3a08f59 262
1815bff5 263 if(error == -1) {
1815bff5 264 file_count--;
c3a08f59
A
265
266 if (file_count > max_valid)
267 cur_limits = max_valid;
268 else
269 cur_limits = file_count;
270
271 if (limits[cur_limits].low_water)
272 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
273 else
274 notifications = HI_WAT_ALERT;
275
1815bff5
A
276 local_hi_water = local_hi_water>>2;
277 if(notify_high >= (local_hi_water)) {
756446ec 278 if(notify_port != MACH_PORT_NULL) {
1815bff5
A
279 /* notify monitoring app of */
280 /* backing store shortage */
281 backing_store_alert(notify_port,
282 HI_WAT_ALERT);
283 mach_port_deallocate(mach_task_self(),
284 notify_port);
756446ec 285 notify_port = MACH_PORT_NULL;
1815bff5 286 notify_high = 0;
1815bff5
A
287 }
288 }
c3a08f59 289 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
1815bff5
A
290 } else {
291 if(hi_water < notify_high) {
292 if(local_hi_water < notify_high) {
293 bs_recovery = notify_high - local_hi_water;
294 }
295 local_hi_water = notify_high;
296 } else {
297 if(local_hi_water < hi_water) {
298 bs_recovery = hi_water - local_hi_water;
299 }
300 local_hi_water = hi_water;
301 }
c3a08f59
A
302 ret = macx_swapon(subfile, flags, cur_size, priority);
303
756446ec
A
304 if(ret) {
305 unlink(subfile);
306 file_count--;
c3a08f59
A
307
308 if (file_count > max_valid)
309 cur_limits = max_valid;
310 else
311 cur_limits = file_count;
312
313 if (limits[cur_limits].low_water)
314 notifications = HI_WAT_ALERT | LO_WAT_ALERT;
315 else
316 notifications = HI_WAT_ALERT;
317
756446ec
A
318 local_hi_water = local_hi_water>>2;
319 if(notify_high >= (local_hi_water)) {
320 if(notify_port != MACH_PORT_NULL) {
321 /* notify monitoring app of */
322 /* backing store shortage */
323 backing_store_alert(
324 notify_port,
325 HI_WAT_ALERT);
326 mach_port_deallocate(
327 mach_task_self(),
328 notify_port);
329 notify_port = MACH_PORT_NULL;
330 notify_high = 0;
331 }
332 }
c3a08f59
A
333 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
334 } else if(bs_recovery <= cur_size) {
1815bff5
A
335 if((bs_recovery != 0) && (notify_port)) {
336 backing_store_alert(notify_port,
337 LO_WAT_ALERT);
338 mach_port_deallocate(mach_task_self(),
339 notify_port);
756446ec 340 notify_port = MACH_PORT_NULL;
1815bff5 341 notify_high = 0;
1815bff5
A
342 bs_recovery = 0;
343 }
344 } else
c3a08f59 345 bs_recovery = bs_recovery-cur_size;
1815bff5 346 }
c3a08f59 347 macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port);
1815bff5
A
348 }
349 if(flags & LO_WAT_ALERT) {
1815bff5
A
350 sprintf(subfile, "%s%d", fileroot, file_count);
351 if(hi_water < notify_high) {
352 local_hi_water = notify_high;
353 } else {
354 local_hi_water = hi_water;
355 }
756446ec 356 if((bs_recovery != 0) && (notify_port != MACH_PORT_NULL)) {
1815bff5
A
357 backing_store_alert(notify_port, LO_WAT_ALERT);
358 mach_port_deallocate(mach_task_self(), notify_port);
756446ec 359 notify_port = MACH_PORT_NULL;
1815bff5 360 notify_high = 0;
1815bff5
A
361 bs_recovery = 0;
362 }
c3a08f59
A
363 if((error = macx_swapoff(subfile, flags)) == 0) {
364
1815bff5
A
365 unlink(subfile);
366 file_count--;
c3a08f59
A
367
368 if (file_count > max_valid)
369 cur_limits = max_valid;
370 else
371 cur_limits = file_count;
372 } else {
373 if (file_count > max_valid)
374 cur_limits = max_valid;
375 else
376 cur_limits = file_count;
1815bff5 377 }
c3a08f59
A
378 /*
379 * only need to reset the LO_WAT_ALERT... the HI_WAT size is fixed,
380 * it doesn't change even if the swap file size shrinks or grows
381 */
382 macx_triggers(local_hi_water, limits[cur_limits].low_water, LO_WAT_ALERT, alert_port);
1815bff5
A
383 }
384 return KERN_SUCCESS;
385}
386
387void
388wait_on_paging_trigger(trigger_port)
389 mach_port_t trigger_port;
390{
391 kern_return_t result;
392 result = server_alert_loop(4096, trigger_port, MACH_MSG_OPTION_NONE);
393 if (result != KERN_SUCCESS) {
394 fprintf(stderr, "dynamic_pager: default pager alert failed\n");
83f6dbe8 395 exit(EXIT_FAILURE);
1815bff5 396 }
83f6dbe8 397 exit(EXIT_SUCCESS);
1815bff5
A
398}
399
400void
2fc1e207 401paging_setup(flags, size, priority, low, high, encrypted)
1815bff5
A
402 int flags;
403 int size;
404 int priority;
405 int low;
406 int high;
2fc1e207 407 boolean_t encrypted;
1815bff5
A
408{
409 off_t filesize = size;
410 char subfile[512];
411 FILE *file_ptr;
756446ec 412 int error;
1815bff5
A
413
414 file_count = 0;
415 sprintf(subfile, "%s%d", fileroot, file_count);
416 file_ptr = fopen(subfile, "w+");
83f6dbe8
A
417 if (file_ptr == NULL) {
418 fprintf(stderr, "dynamic_pager: cannot create paging file %s!\n",
419 subfile);
420 exit(EXIT_FAILURE);
421 }
1815bff5 422 fchmod(fileno(file_ptr), (mode_t)01600);
83f6dbe8 423
756446ec
A
424 error = fcntl(fileno(file_ptr), F_SETSIZE, &filesize);
425 if(error) {
83f6dbe8 426 error = ftruncate(fileno(file_ptr), filesize);
756446ec 427 }
1815bff5 428 fclose(file_ptr);
83f6dbe8
A
429
430 if (error == -1) {
431 fprintf(stderr, "dynamic_pager: cannot extend paging file size %s to %llu!\n",
432 subfile, filesize);
433 exit(EXIT_FAILURE);
434 }
1815bff5 435
2fc1e207
A
436 if (macx_triggers(0, 0,
437 (encrypted
438 ? SWAP_ENCRYPT_ON
439 : SWAP_ENCRYPT_OFF),
440 MACH_PORT_NULL) != 0) {
441 fprintf(stderr,
442 "dynamic_pager: warning: "
443 "could not turn encrypted swap %s\n",
444 (encrypted ? "on" : "off"));
445 }
446
1815bff5 447 macx_swapon(subfile, flags, size, priority);
c3a08f59 448
1815bff5 449 if(hi_water) {
1c51fdde
A
450 mach_msg_type_name_t poly;
451
1815bff5
A
452 if (mach_port_allocate(mach_task_self(),
453 MACH_PORT_RIGHT_RECEIVE,
454 &trigger_port) != KERN_SUCCESS) {
83f6dbe8
A
455 fprintf(stderr,"dynamic_pager: allocation of trigger port failed\n");
456 exit(EXIT_FAILURE);
1815bff5 457 }
1c51fdde
A
458 /* create a send right on our local port */
459 mach_port_extract_right(mach_task_self(), trigger_port,
460 MACH_MSG_TYPE_MAKE_SEND, &trigger_port, &poly);
1815bff5 461 macx_triggers(high, low, HI_WAT_ALERT, trigger_port);
c3a08f59 462
1815bff5 463 if(low) {
c3a08f59 464 macx_triggers(high, low, LO_WAT_ALERT, trigger_port);
1815bff5
A
465 }
466 /* register control port for applications wishing to */
467 /* get backing store notifications or change dynamic */
468 /* pager settings. */
469 set_dp_control_port(mach_host_self(), trigger_port);
470 wait_on_paging_trigger(trigger_port);
471 }
83f6dbe8 472 exit(EXIT_SUCCESS);
1815bff5 473}
34d340d7
A
474
475static void
476clean_swap_directory(const char *path)
477{
478 DIR *dir;
479 struct dirent *entry;
480 char buf[1024];
481
482 dir = opendir(path);
483 if (dir == NULL) {
484 fprintf(stderr,"dynamic_pager: cannot open swap directory %s\n", path);
485 exit(EXIT_FAILURE);
486 }
487
488 while ((entry = readdir(dir)) != NULL) {
489 if (entry->d_namlen>= 4 && strncmp(entry->d_name, "swap", 4) == 0) {
490 snprintf(buf, sizeof buf, "%s/%s", path, entry->d_name);
491 unlink(buf);
492 }
493 }
494
495 closedir(dir);
496}
497
498#define VM_PREFS_PLIST "/Library/Preferences/com.apple.virtualMemory.plist"
499#define VM_PREFS_ENCRYPT_SWAP_KEY "UseEncryptedSwap"
500
501static boolean_t
502should_encrypt_swap(void)
503{
504 CFPropertyListRef propertyList;
505 CFTypeID propertyListType;
506 CFStringRef errorString;
507 CFDataRef resourceData;
508 SInt32 errorCode;
509 CFURLRef fileURL;
510 CFTypeRef value;
511 boolean_t should_encrypt;
512 boolean_t explicit_value;
513 CFTypeRef snap;
514
515 explicit_value = false;
516
517 fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)VM_PREFS_PLIST, strlen(VM_PREFS_PLIST), false);
518 if (fileURL == NULL) {
519 /*fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), VM_PREFS_PLIST);*/
520 goto done;
521 }
522
523 if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) {
524 /*fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), VM_PREFS_PLIST, (int)errorCode);*/
525 CFRelease(fileURL);
526 goto done;
527 }
528
529 CFRelease(fileURL);
530 propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString);
531 if (propertyList == NULL) {
532 /*fprintf(stderr, "%s: cannot get XML propertyList %s\n", getprogname(), VM_PREFS_PLIST);*/
533 CFRelease(resourceData);
534 goto done;
535 }
536
537 propertyListType = CFGetTypeID(propertyList);
538
539 if (propertyListType == CFDictionaryGetTypeID()) {
540 value = (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef) propertyList, CFSTR(VM_PREFS_ENCRYPT_SWAP_KEY));
541 if (value == NULL) {
542 /* no value: use the default value */
543 } else if (CFGetTypeID(value) != CFBooleanGetTypeID()) {
544 fprintf(stderr, "%s: wrong type for key \"%s\"\n",
545 getprogname(), VM_PREFS_ENCRYPT_SWAP_KEY);
546 /* bogus value, assume it's "true" for safety's sake */
547 should_encrypt = true;
548 explicit_value = true;
549 } else {
550 should_encrypt = CFBooleanGetValue((CFBooleanRef)value);
551 explicit_value = true;
552 }
553 }
554 else {
555 /*fprintf(stderr, "%s: invalid propertyList type %d (not a dictionary)\n", getprogname(), propertyListType);*/
556 }
557 CFRelease(resourceData);
558 CFRelease(propertyList);
559
560done:
561 if (! explicit_value) {
562 /* by default, encrypt swap on laptops only */
563 mach_timespec_t w;
564 kern_return_t kr;
565
566 /* wait up to 60 seconds for IOKit to quiesce */
567 w.tv_sec = 60;
568 w.tv_nsec = 0;
569 kr = IOKitWaitQuiet(kIOMasterPortDefault, &w);
570 if (kr != kIOReturnSuccess) {
571 /*
572 * Can't tell if we're on a laptop,
573 * assume we do want encrypted swap.
574 */
575 should_encrypt = TRUE;
576 /*fprintf(stderr, "dynamic_pager: IOKitWaitQuiet ret 0x%x (%s)\n", kr, mach_error_string(kr));*/
577 } else {
578 /*
579 * Look for battery power source.
580 */
581 snap = IOPSCopyPowerSourcesInfo();
582 should_encrypt = (kCFBooleanTrue == IOPSPowerSourceSupported(snap, CFSTR(kIOPMBatteryPowerKey)));
583 CFRelease(snap);
584 /*fprintf(stderr, "dynamic_pager: battery power source: %d\n", should_encrypt);*/
585 }
586 }
587
588 return should_encrypt;
589}
590
1815bff5
A
591int
592main(int argc, char **argv)
593{
594 extern char *optarg;
595 extern int optind;
596 char default_filename[] = "/private/var/vm/swapfile";
597 int ch;
c3a08f59 598 int variable_sized = 1;
34d340d7
A
599 boolean_t encrypted_swap;
600
601/*
602 setlinebuf(stdout);
603 setlinebuf(stderr);
604*/
1815bff5
A
605
606 seteuid(getuid());
607 strcpy(fileroot, default_filename);
608
c3a08f59
A
609 limits[0].size = 20000000;
610 limits[0].low_water = 0;
611
612 hi_water = 0;
613 local_hi_water = 0;
614
34d340d7 615 encrypted_swap = should_encrypt_swap();
c3a08f59 616
34d340d7 617 while ((ch = getopt(argc, argv, "EF:L:H:S:P:QO:")) != EOF) {
1815bff5
A
618 switch((char)ch) {
619
2fc1e207
A
620 case 'E':
621 encrypted_swap = TRUE;
622 break;
623
1815bff5
A
624 case 'F':
625 strncpy(fileroot, optarg, 500);
626 break;
627
628 case 'L':
c3a08f59
A
629 variable_sized = 0;
630 limits[0].low_water = atoi(optarg);
1815bff5
A
631 break;
632 case 'H':
c3a08f59 633 variable_sized = 0;
1815bff5
A
634 hi_water = atoi(optarg);
635 break;
636 case 'S':
c3a08f59
A
637 variable_sized = 0;
638 limits[0].size = atoi(optarg);
1815bff5
A
639 break;
640 case 'P':
641 priority = atoi(optarg);
642 break;
643
34d340d7
A
644 case 'Q':
645 /* just query for "encrypted swap" default value */
646 fprintf(stdout,
647 "dynamic_pager: encrypted swap will be %s\n",
648 encrypted_swap ? "ON": "OFF");
649 exit(0);
650
1815bff5
A
651 default:
652 (void)fprintf(stderr,
653 "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n");
83f6dbe8 654 exit(EXIT_FAILURE);
1815bff5
A
655 }
656 }
c3a08f59
A
657
658 if (variable_sized) {
659 static char tmp[1024];
660 struct statfs sfs;
661 char *q;
662 int i;
663 int mib[4];
664 size_t len;
665 unsigned int size;
666 u_int64_t memsize;
34d340d7 667 u_int64_t fs_limit = 0;
c3a08f59
A
668
669 /*
670 * if we get here, then none of the following options were specified... -L, H, or -S
671 * drop into a new mode that scales the size of the swap file based on how much free
672 * space is left on the volume being used for swap and the amount of physical ram
673 * installed on the system...
674 * basically, we'll pick a maximum size that doesn't exceed the following limits...
83f6dbe8 675 * 1/8 the remaining free space of the swap volume
c3a08f59
A
676 * the size of phsyical ram
677 * MAXIMUM_SIZE - currently set to 1 Gbyte...
678 * once we have the maximum, we'll create a list of sizes and low_water limits
679 * we'll start with 2 files of MINIMUM_SIZE - currently 64 Mbytes...
680 * subsequent entries will double in size up to the calculated maximum... the low_water
681 * limit will be the sum of the current file size and the previous file size for each entry...
682 * as we add or delete files, we'll use the current file_count as an index into this
683 * table... if it's beyond the table size, we'll use the last entry
684 * the table entry will determine the size of the file to be created and the new low_water mark...
685 * the high_water mark is set to HI_WATER_DEFAULT which must be smaller than MINIMUM_SIZE...
686 * currently it is set to 40,000,000 to match the size being requested by the application
687 * monitoring low space conditions... having it set to the same size keeps us from creating
688 * an additional swap file when it really isn't necessary
689 */
690
691 /*
692 * get rid of the filename at the end of the swap file specification
693 * we only want the portion of the pathname that should already exist
694 */
695 strcpy(tmp, fileroot);
696 if (q = strrchr(tmp, '/'))
697 *q = 0;
698
34d340d7
A
699 if (statfs(tmp, &sfs) == -1) {
700 /*
701 * Setup the swap directory.
702 */
703 if (mkdir(tmp, 0755) == -1) {
704 (void)fprintf(stderr, "dynamic_pager: cannot create swap directory %s\n", tmp);
705 exit(EXIT_FAILURE);
706 }
707 chown(tmp, 0, 0);
708
709 if (statfs(tmp, &sfs) == -1) {
710 /*
711 * We really can't get filesystem status,
712 * so let's not limit the swap files...
713 */
714 fs_limit = (u_int64_t) -1;
715 }
716 }
717
718 /*
719 * Remove all files in the swap directory.
720 */
721 clean_swap_directory(tmp);
722
723 if (fs_limit != (u_int64_t) -1) {
724 /*
83f6dbe8 725 * Limit the maximum size of a swap file to 1/8 the free
c3a08f59 726 * space available on the filesystem where the swap files
83f6dbe8
A
727 * are to reside. This will allow us to allocate and
728 * deallocate in finer increments on systems without much
729 * free space.
c3a08f59 730 */
83f6dbe8 731 fs_limit = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize) / 8;
c3a08f59 732 }
34d340d7 733
c3a08f59
A
734 mib[0] = CTL_HW;
735 mib[1] = HW_MEMSIZE;
736 len = sizeof(u_int64_t);
737
738 if (sysctl(mib, 2, &memsize, &len, NULL, 0) < 0) {
739 /*
740 * if the sysctl fails for some reason
741 * use the starting size as the default
742 */
743 memsize = MINIMUM_SIZE;
744 }
745 if (memsize > fs_limit)
746 /*
747 * clip based on filesystem space available
748 */
749 memsize = fs_limit;
750
751 /*
752 * further limit the maximum size of a swap file
753 */
754 if (memsize > MAXIMUM_SIZE)
755 memsize = MAXIMUM_SIZE;
756
757 size = MINIMUM_SIZE;
758
759 /*
760 * start small and work our way up to the maximum
761 * sized allowed... this way, we don't tie up too
762 * much disk space if we never do any real paging
763 */
764 for (max_valid = 0, i = 0; i < MAX_LIMITS; i++) {
765 limits[i].size = size;
766
767 if (i == 0)
768 limits[i].low_water = size * 2;
769 else {
770 if ((limits[i - 1].size / 2) > HI_WATER_DEFAULT)
771 limits[i].low_water = size + (limits[i - 1].size / 2);
772 else
773 limits[i].low_water = size + limits[i - 1].size;
774 }
c3a08f59
A
775
776 if (i) {
777 /*
778 * make the first 2 files the same size
779 */
780 size = size * 2;
83f6dbe8
A
781 if (size > memsize)
782 break;
c3a08f59
A
783 }
784 max_valid++;
785 }
786 if (max_valid >= MAX_LIMITS)
787 max_valid = MAX_LIMITS - 1;
788
789 hi_water = HI_WATER_DEFAULT;
790 }
1815bff5 791 local_hi_water = hi_water;
c3a08f59
A
792
793 if((limits[0].low_water != 0) && (limits[0].low_water <= (limits[0].size + hi_water))) {
83f6dbe8
A
794 (void)fprintf(stderr, "dynamic_pager: low water trigger must be larger than size + hi_water\n");
795 exit(EXIT_FAILURE);
1815bff5
A
796 }
797 argc -= optind;
798 argv += optind;
c3a08f59 799
2fc1e207
A
800 paging_setup(0, limits[0].size, priority, limits[0].low_water, hi_water,
801 encrypted_swap);
c3a08f59 802
1815bff5
A
803 return (0);
804}