X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/c3a08f5910a028cdace032ed79beb406c750d530..8459d7254751dbe55e98bb49dd51e1796df2bb0d:/dynamic_pager.tproj/dynamic_pager.c?ds=inline diff --git a/dynamic_pager.tproj/dynamic_pager.c b/dynamic_pager.tproj/dynamic_pager.c index c168eab..8352791 100644 --- a/dynamic_pager.tproj/dynamic_pager.c +++ b/dynamic_pager.tproj/dynamic_pager.c @@ -10,8 +10,8 @@ #ifndef MACH_BSD #define MACH_BSD #endif -#include #include +#include #include #include #include @@ -20,8 +20,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -30,13 +30,15 @@ #include #include #include +#include + +#include #include #include #include #include - /* * HI_WATER_DEFAULT set to this funny value to * match the size that the low space application @@ -49,6 +51,22 @@ #define MAX_LIMITS 8 +#if TARGET_OS_EMBEDDED + +#include + +#define USE_SYSTEMCONFIGURATION_PRIVATE_HEADERS 1 +#include + +enum { + VM_PAGING_MODE_DISABLED = 0, + VM_PAGING_MODE_FREEZE, + VM_PAGING_MODE_DYNAMIC +}; + +#define VM_FREEZE_SYSCTL "vm.freeze_enabled" +#define FREEZE_FIXED_SWAP_SIZE (128 * 1024 * 1024) +#endif struct limit { unsigned int size; @@ -95,28 +113,17 @@ server_alert_loop( max_size + MAX_TRAILER_SIZE, TRUE)) != KERN_SUCCESS) return kr; - if ((kr = vm_protect(mach_task_self(), - (vm_address_t)bufRequest, - max_size + MAX_TRAILER_SIZE, - FALSE, VM_PROT_ALL)) != KERN_SUCCESS) - return kr; mlock(bufRequest, max_size + MAX_TRAILER_SIZE); if ((kr = vm_allocate(mach_task_self(), (vm_address_t *)&bufReply, max_size + MAX_TRAILER_SIZE, TRUE)) != KERN_SUCCESS) return kr; - if ((kr = vm_protect(mach_task_self(), - (vm_address_t)bufReply, - max_size + MAX_TRAILER_SIZE, - FALSE, VM_PROT_ALL)) != KERN_SUCCESS) - return kr; mlock(bufReply, max_size + MAX_TRAILER_SIZE); while(TRUE) { - mr = mach_msg_overwrite_trap(&bufRequest->Head, MACH_RCV_MSG|options, - 0, max_size, rcv_name, - MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, - (mach_msg_header_t *) 0, 0); + mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options, + 0, max_size, rcv_name, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (mr == MACH_MSG_SUCCESS) { /* we have a request message */ @@ -208,9 +215,8 @@ default_pager_space_alert(alert_port, flags) int flags; { char subfile[512]; - FILE *file_ptr; off_t filesize; - int error; + int error=0, fd=0; kern_return_t ret; int cur_limits; unsigned int cur_size; @@ -241,16 +247,28 @@ default_pager_space_alert(alert_port, flags) notifications = HI_WAT_ALERT; sprintf(subfile, "%s%d", fileroot, file_count); - file_ptr = fopen(subfile, "w+"); - fchmod(fileno(file_ptr), (mode_t)01600); - error = fcntl(fileno(file_ptr), F_SETSIZE, &filesize); - if(error) { - error = ftruncate(fileno(file_ptr), filesize); + fd = open(subfile, O_CREAT|O_EXCL|O_RDWR,(mode_t)(S_IRUSR|S_IWUSR)); + if (fd == -1) { + /* force error recovery below */ + error = -1; + } +#if TARGET_OS_EMBEDDED + else { + error = fcntl(fd, F_SETPROTECTIONCLASS, PROTECTION_CLASS_F); + } +#endif + + if(!error) { + error = fcntl(fd, F_SETSIZE, &filesize); + if(error) { + error = ftruncate(fd, filesize); + } + if(error) + unlink(subfile); + close(fd); } - fclose(file_ptr); if(error == -1) { - unlink(subfile); file_count--; if (file_count > max_valid) @@ -262,6 +280,8 @@ default_pager_space_alert(alert_port, flags) notifications = HI_WAT_ALERT | LO_WAT_ALERT; else notifications = HI_WAT_ALERT; + + notifications |= SWAP_FILE_CREATION_ERROR; local_hi_water = local_hi_water>>2; if(notify_high >= (local_hi_water)) { @@ -276,7 +296,6 @@ default_pager_space_alert(alert_port, flags) notify_high = 0; } } - macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port); } else { if(hi_water < notify_high) { if(local_hi_water < notify_high) { @@ -289,7 +308,8 @@ default_pager_space_alert(alert_port, flags) } local_hi_water = hi_water; } - ret = macx_swapon(subfile, flags, cur_size, priority); + ret = macx_swapon((uint64_t)(uintptr_t)subfile, + flags, cur_size, priority); if(ret) { unlink(subfile); @@ -320,7 +340,6 @@ default_pager_space_alert(alert_port, flags) notify_high = 0; } } - macx_triggers(local_hi_water, limits[cur_limits].low_water, notifications, alert_port); } else if(bs_recovery <= cur_size) { if((bs_recovery != 0) && (notify_port)) { backing_store_alert(notify_port, @@ -350,7 +369,8 @@ default_pager_space_alert(alert_port, flags) notify_high = 0; bs_recovery = 0; } - if((error = macx_swapoff(subfile, flags)) == 0) { + if((error = macx_swapoff((uint64_t)(uintptr_t)subfile, + flags)) == 0) { unlink(subfile); file_count--; @@ -382,46 +402,76 @@ wait_on_paging_trigger(trigger_port) result = server_alert_loop(4096, trigger_port, MACH_MSG_OPTION_NONE); if (result != KERN_SUCCESS) { fprintf(stderr, "dynamic_pager: default pager alert failed\n"); - exit(1); + exit(EXIT_FAILURE); } - exit(0); + exit(EXIT_SUCCESS); } void -paging_setup(flags, size, priority, low, high) +paging_setup(flags, size, priority, low, high, encrypted) int flags; int size; int priority; int low; int high; + boolean_t encrypted; { off_t filesize = size; char subfile[512]; - FILE *file_ptr; - int error; + int error, fd = 0; file_count = 0; sprintf(subfile, "%s%d", fileroot, file_count); - file_ptr = fopen(subfile, "w+"); - fchmod(fileno(file_ptr), (mode_t)01600); - error = fcntl(fileno(file_ptr), F_SETSIZE, &filesize); + fd = open(subfile, O_CREAT|O_EXCL|O_RDWR, ((mode_t)(S_IRUSR|S_IWUSR))); + if (fd == -1) { + fprintf(stderr, "dynamic_pager: cannot create paging file %s!\n", + subfile); + exit(EXIT_FAILURE); + } + +#if TARGET_OS_EMBEDDED + error = fcntl(fd, F_SETPROTECTIONCLASS, PROTECTION_CLASS_F); + if (error == -1) { + fprintf(stderr, "dynamic_pager: cannot set file protection class!\n"); + unlink(subfile); + close(fd); + exit(EXIT_FAILURE); + } +#endif + + error = fcntl(fd, F_SETSIZE, &filesize); if(error) { - error = ftruncate(fileno(file_ptr), filesize); + error = ftruncate(fd, filesize); + } + close(fd); + + if (error == -1) { + fprintf(stderr, "dynamic_pager: cannot extend paging file size %s to %llu!\n", + subfile, filesize); + exit(EXIT_FAILURE); } - fclose(file_ptr); - macx_swapon(subfile, flags, size, priority); + if (macx_triggers(0, 0, + (encrypted + ? SWAP_ENCRYPT_ON + : SWAP_ENCRYPT_OFF), + MACH_PORT_NULL) != 0) { + fprintf(stderr, + "dynamic_pager: warning: " + "could not turn encrypted swap %s\n", + (encrypted ? "on" : "off")); + } + + macx_swapon((uint64_t)(uintptr_t)subfile, flags, size, priority); if(hi_water) { mach_msg_type_name_t poly; - daemon(0,0); - if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &trigger_port) != KERN_SUCCESS) { - fprintf(stderr,"allocation of trigger port failed\n"); - exit(1); + fprintf(stderr,"dynamic_pager: allocation of trigger port failed\n"); + exit(EXIT_FAILURE); } /* create a send right on our local port */ mach_port_extract_right(mach_task_self(), trigger_port, @@ -437,8 +487,132 @@ paging_setup(flags, size, priority, low, high) set_dp_control_port(mach_host_self(), trigger_port); wait_on_paging_trigger(trigger_port); } - exit(0); + exit(EXIT_SUCCESS); +} + +static void +clean_swap_directory(const char *path) +{ + DIR *dir; + struct dirent *entry; + char buf[1024]; + + dir = opendir(path); + if (dir == NULL) { + fprintf(stderr,"dynamic_pager: cannot open swap directory %s\n", path); + exit(EXIT_FAILURE); + } + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_namlen>= 4 && strncmp(entry->d_name, "swap", 4) == 0) { + snprintf(buf, sizeof buf, "%s/%s", path, entry->d_name); + unlink(buf); + } + } + + closedir(dir); +} + +#define VM_PREFS_PLIST "/Library/Preferences/com.apple.virtualMemory.plist" +#define VM_PREFS_DISABLE_ENCRYPT_SWAP_KEY "DisableEncryptedSwap" + +static boolean_t +should_encrypt_swap(void) +{ + CFPropertyListRef propertyList; + CFTypeID propertyListType; + CFStringRef errorString; + CFDataRef resourceData; + SInt32 errorCode; + CFURLRef fileURL; + CFTypeRef disable_encrypted_swap; + boolean_t should_encrypt = TRUE; + boolean_t explicit_value = FALSE; + + fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)VM_PREFS_PLIST, strlen(VM_PREFS_PLIST), false); + if (fileURL == NULL) { + /*fprintf(stderr, "%s: CFURLCreateFromFileSystemRepresentation(%s) failed\n", getprogname(), VM_PREFS_PLIST);*/ + goto done; + } + + if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, fileURL, &resourceData, NULL, NULL, &errorCode)) { + /*fprintf(stderr, "%s: CFURLCreateDataAndPropertiesFromResource(%s) failed: %d\n", getprogname(), VM_PREFS_PLIST, (int)errorCode);*/ + CFRelease(fileURL); + goto done; + } + + CFRelease(fileURL); + propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resourceData, kCFPropertyListMutableContainers, &errorString); + if (propertyList == NULL) { + /*fprintf(stderr, "%s: cannot get XML propertyList %s\n", getprogname(), VM_PREFS_PLIST);*/ + CFRelease(resourceData); + goto done; + } + + propertyListType = CFGetTypeID(propertyList); + + if (propertyListType == CFDictionaryGetTypeID()) { + disable_encrypted_swap = (CFTypeRef) CFDictionaryGetValue((CFDictionaryRef) propertyList, CFSTR(VM_PREFS_DISABLE_ENCRYPT_SWAP_KEY)); + if (disable_encrypted_swap == NULL) { + /* no value: use the default value i.e. encrypted swap ON */ + } else if (CFGetTypeID(disable_encrypted_swap) != CFBooleanGetTypeID()) { + fprintf(stderr, "%s: wrong type for key \"%s\"\n", + getprogname(), VM_PREFS_DISABLE_ENCRYPT_SWAP_KEY); + /* bogus value, assume it's "true" for safety's sake */ + should_encrypt = TRUE; + explicit_value = TRUE; + } else { + should_encrypt = CFBooleanGetValue((CFBooleanRef)disable_encrypted_swap) ? FALSE : TRUE; + explicit_value = TRUE; + } + } + else { + /*fprintf(stderr, "%s: invalid propertyList type %d (not a dictionary)\n", getprogname(), propertyListType);*/ + } + CFRelease(resourceData); + CFRelease(propertyList); + +done: + if (! explicit_value) { +#if TARGET_OS_EMBEDDED + should_encrypt = FALSE; +#endif + } + + return should_encrypt; } + +#if TARGET_OS_EMBEDDED + +#define VM_PREFS_PAGING_MODE_PLIST "com.apple.virtualMemoryMode.plist" +#define VM_PREFS_PAGING_MODE_KEY "Mode" + +static uint32_t +get_paging_mode(void) +{ + SCPreferencesRef paging_prefs; + uint32_t paging_mode; + + paging_mode = VM_PAGING_MODE_FREEZE; /* default */ + + paging_prefs = SCPreferencesCreate(NULL, CFSTR("dynamic_pager"), CFSTR(VM_PREFS_PAGING_MODE_PLIST)); + if (paging_prefs) { + CFNumberRef value; + + value = SCPreferencesGetValue(paging_prefs, CFSTR(VM_PREFS_PAGING_MODE_KEY)); + + if (value && (CFGetTypeID( value ) == CFNumberGetTypeID() ) ) { + CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &paging_mode); + } + + CFRelease(paging_prefs); + } + + return paging_mode; +} + +#endif + int main(int argc, char **argv) { @@ -446,21 +620,41 @@ main(int argc, char **argv) extern int optind; char default_filename[] = "/private/var/vm/swapfile"; int ch; - int variable_sized = 1; + int variable_sized = 1,flags=0; + boolean_t encrypted_swap; + static char tmp[1024]; + struct statfs sfs; + char *q; +#if TARGET_OS_EMBEDDED + int err; + uint32_t paging_mode; + size_t paging_mode_length; +#endif + +/* + setlinebuf(stdout); + setlinebuf(stderr); +*/ seteuid(getuid()); strcpy(fileroot, default_filename); +retry: limits[0].size = 20000000; limits[0].low_water = 0; hi_water = 0; local_hi_water = 0; + encrypted_swap = should_encrypt_swap(); - while ((ch = getopt(argc, argv, "F:L:H:S:P:O:")) != EOF) { + while ((ch = getopt(argc, argv, "EF:L:H:S:P:QO:")) != EOF) { switch((char)ch) { + case 'E': + encrypted_swap = TRUE; + break; + case 'F': strncpy(fileroot, optarg, 500); break; @@ -481,23 +675,82 @@ main(int argc, char **argv) priority = atoi(optarg); break; + case 'Q': + /* just query for "encrypted swap" default value */ + fprintf(stdout, + "dynamic_pager: encrypted swap will be %s\n", + encrypted_swap ? "ON": "OFF"); + exit(0); + default: (void)fprintf(stderr, "usage: dynamic_pager [-F filename] [-L low water alert trigger] [-H high water alert trigger] [-S file size] [-P priority]\n"); - exit(1); + exit(EXIT_FAILURE); } } + /* + * get rid of the filename at the end of the swap file specification + * we only want the portion of the pathname that should already exist + */ + strcpy(tmp, fileroot); + if ((q = strrchr(tmp, '/'))) + *q = 0; + + /* + * Remove all files in the swap directory. + */ + clean_swap_directory(tmp); + +#if TARGET_OS_EMBEDDED + paging_mode = get_paging_mode(); + paging_mode_length = sizeof(paging_mode); + + switch (paging_mode) { + case VM_PAGING_MODE_DISABLED: + /* Paging disabled; nothing to do here so exit */ + exit(EXIT_SUCCESS); + + case VM_PAGING_MODE_FREEZE: + /* Freeze mode; one swap file of fixed size, so enable and override defaults if set */ + err = sysctlbyname(VM_FREEZE_SYSCTL, NULL, 0, &paging_mode, paging_mode_length); + if (err < 0) { + (void)fprintf(stderr, "dynamic_pager: cannot set %s\n", VM_FREEZE_SYSCTL); + exit(EXIT_FAILURE); + } + variable_sized = 0; + limits[0].size = FREEZE_FIXED_SWAP_SIZE; + break; + + case VM_PAGING_MODE_DYNAMIC: + /* Dynamic paging selected; proceed normally */ + break; + + default: + /* Invalid option */ + (void)fprintf(stderr, "dynamic_pager: invalid paging_mode %d\n", paging_mode); + exit(EXIT_FAILURE); + } +#endif + + if (statfs(tmp, &sfs) == -1) { + /* + * Setup the swap directory. + */ + if (mkdir(tmp, 0755) == -1) { + (void)fprintf(stderr, "dynamic_pager: cannot create swap directory %s\n", tmp); + exit(EXIT_FAILURE); + } + } + chown(tmp, 0, 0); + if (variable_sized) { - static char tmp[1024]; - struct statfs sfs; - char *q; int i; int mib[4]; size_t len; unsigned int size; u_int64_t memsize; - u_int64_t fs_limit; + u_int64_t fs_limit = 0; /* * if we get here, then none of the following options were specified... -L, H, or -S @@ -505,7 +758,7 @@ main(int argc, char **argv) * space is left on the volume being used for swap and the amount of physical ram * installed on the system... * basically, we'll pick a maximum size that doesn't exceed the following limits... - * 1/4 the remaining free space of the swap volume + * 1/8 the remaining free space of the swap volume * the size of phsyical ram * MAXIMUM_SIZE - currently set to 1 Gbyte... * once we have the maximum, we'll create a list of sizes and low_water limits @@ -521,26 +774,25 @@ main(int argc, char **argv) * an additional swap file when it really isn't necessary */ - /* - * get rid of the filename at the end of the swap file specification - * we only want the portion of the pathname that should already exist - */ - strcpy(tmp, fileroot); - if (q = strrchr(tmp, '/')) - *q = 0; - - if (statfs(tmp, &sfs) != -1) { - /* - * limit the maximum size of a swap file to 1/4 the free + if (statfs(tmp, &sfs) == -1) { + /* + * We really can't get filesystem status, + * so let's not limit the swap files... + */ + fs_limit = (u_int64_t) -1; + } + + if (fs_limit != (u_int64_t) -1) { + /* + * Limit the maximum size of a swap file to 1/8 the free * space available on the filesystem where the swap files - * are to reside + * are to reside. This will allow us to allocate and + * deallocate in finer increments on systems without much + * free space. */ - fs_limit = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize) / 4; - - } else { - (void)fprintf(stderr, "usage: swap directory must exist\n"); - exit(1); + fs_limit = ((u_int64_t)sfs.f_bfree * (u_int64_t)sfs.f_bsize) / 8; } + mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; len = sizeof(u_int64_t); @@ -561,8 +813,16 @@ main(int argc, char **argv) /* * further limit the maximum size of a swap file */ - if (memsize > MAXIMUM_SIZE) + if (memsize <= MINIMUM_SIZE) { + (void)fprintf(stderr, "dynamic_pager: Need more space on the disk to enable swapping.\n"); + sleep(30); + goto retry; + } else if (memsize <= (MINIMUM_SIZE*2)) { + (void)fprintf(stderr, "dynamic_pager: Activating emergency swap file immediately.\n"); + flags |= USE_EMERGENCY_SWAP_FILE_FIRST; + } else if (memsize > MAXIMUM_SIZE) { memsize = MAXIMUM_SIZE; + } size = MINIMUM_SIZE; @@ -582,14 +842,14 @@ main(int argc, char **argv) else limits[i].low_water = size + limits[i - 1].size; } - if (size >= memsize) - break; if (i) { /* * make the first 2 files the same size */ size = size * 2; + if (size > memsize) + break; } max_valid++; } @@ -601,13 +861,14 @@ main(int argc, char **argv) local_hi_water = hi_water; if((limits[0].low_water != 0) && (limits[0].low_water <= (limits[0].size + hi_water))) { - (void)fprintf(stderr, "usage: low water trigger must be larger than size + hi_water\n"); - exit(1); + (void)fprintf(stderr, "dynamic_pager: low water trigger must be larger than size + hi_water\n"); + exit(EXIT_FAILURE); } argc -= optind; argv += optind; - paging_setup(0, limits[0].size, priority, limits[0].low_water, hi_water); + paging_setup(flags, limits[0].size, priority, limits[0].low_water, hi_water, + encrypted_swap); return (0); }