[PATCH gnumach v3] Implement per task virtual memory limit
From: Diego Nieto Cid * include/mach/gnumach.defs: (vm_set_size_limit) new routine (vm_get_size_limit) likewise * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual memory limit * tests/test-vm.c: (test_vm_limit) add test for the new routines * vm/vm_kern.c: (projected_buffer_allocate) increase size_none when protection is VM_PROT_NONE (projected_buffer_map) likewise * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and size_max_limit * vm/vm_map.c: (vm_map_setup) initialize new fields (vm_map_enforce_limit) new function (vm_map_copy_limits) new function (vm_map_find_entry) call limit enforcer function (vm_map_enter) likewise (vm_map_copyout) likewise (vm_map_copyout_page_list) likewise (vm_map_fork) copy parent limit to the new map and compute and set size_none of the new map * vm/vm_user.c: (vm_set_size_limit) new function (vm_get_size_limit) likewise --- include/mach/gnumach.defs | 33 +++ kern/task.c | 5 ++ tests/test-vm.c | 85 vm/vm_kern.c | 26 + vm/vm_map.c | 113 +- vm/vm_map.h | 13 + vm/vm_user.c | 69 +++ 7 files changed, 342 insertions(+), 2 deletions(-) diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs index f13e866b..a07a1011 100644 --- a/include/mach/gnumach.defs +++ b/include/mach/gnumach.defs @@ -223,3 +223,36 @@ simpleroutine thread_set_name( routine thread_get_name( thread : thread_t; out name : kernel_debug_name_t); + +/* + * Set a task virtual memory limit parameters + * + * HOST_PORT must be the privileged host control port + * if the caller desires to increase the current max limit. + * + * On the other hand, if the max limit is being decreased, the + * unprivileged host control port (as returned by mach_host_self()) + * can be provided. + * + * Returns: + * - KERN_SUCCESS + * - KERN_INVALID_TASK + * - KERN_INVALID_HOST + * - KERN_INVALID_ARGUMENT + * * when current_limit > max_limit + * * attempt to increase max limit without providing + * the privileged host control port. + */ +routine vm_set_size_limit( + host_port : mach_port_t; + map : vm_task_t; + current_limit : vm_size_t; + max_limit : vm_size_t); + +/* + * Get a task virtual memory limit parameters + */ +routine vm_get_size_limit( + map : vm_task_t; + out current_limit : vm_size_t; + out max_limit : vm_size_t); diff --git a/kern/task.c b/kern/task.c index bd57ca2a..e78e856f 100644 --- a/kern/task.c +++ b/kern/task.c @@ -126,6 +126,11 @@ task_create_kernel( trunc_page(VM_MAX_USER_ADDRESS)); if (new_task->map == VM_MAP_NULL) pmap_destroy(new_pmap); + else if (parent_task != TASK_NULL) { + vm_map_lock_read(parent_task->map); + vm_map_copy_limits(new_task->map, parent_task->map); + vm_map_unlock_read(parent_task->map); + } } } if (new_task->map == VM_MAP_NULL) { diff --git a/tests/test-vm.c b/tests/test-vm.c index 4ece792e..8e4ad884 100644 --- a/tests/test-vm.c +++ b/tests/test-vm.c @@ -75,11 +75,96 @@ static void test_wire() // TODO check that all memory is actually wired or unwired } +void test_vm_limit() +{ + kern_return_t err; + vm_address_t mem, mem2, mem3; + const size_t M_128K = 128l * 1024l; + const size_t M_128M = 128l * 1024l * 1024l; + const size_t M_512M = 512l * 1024l * 1024l; + vm_size_t cur; + vm_size_t max; + + /* set VM memory limitations */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M); + ASSERT_RET(err, "cannot set VM limits"); + + /* check limits are actually saved */ + err = vm_get_size_limit(mach_task_self(), &cur, &max); + ASSERT_RET(err, "getting the VM limit failed"); + ASSERT(cur == M_128M, "cur limit was not expected"); + ASSERT(max == M_512M, "max limit was not expected"); + + /* check we can no longer increase the max limit */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 2); + ASSERT(err == KERN_INVALID_ARGUMENT, "raising VM max limit shall fail with KERN_INVALID_ARGUMENT"); + + /* alloc some memory below the limit */ + err = vm_allocate(mach_task_self(), &mem, M_128K, TRUE); + ASSERT_RET(err, "allocating memory below the limit must succeed"); + err = vm_deallocate(mach_task_self(), mem, M_128K); + ASSERT_RET(err, "deallocation failed"); + + /* alloc a bigger chunk to make it hit the limit */ + err = vm_allocate(mach_task_self(), &mem, (M_128M *
[RFC PATCH] Implement per task virtual memory limit
From: Diego Nieto Cid * include/mach/gnumach.defs: (task_set_vm_limit) new routine (task_get_vm_limit) likewise * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual memory limit (task_set_vm_limit) new function (task_get_vm_limit) likewise * tests/test-task.c: (test_vm_limits) add test for the new routines * vm/vm_map.h: (struct vm_map) new fields size_none, map_size_cur_limit and map_size_max_limit * vm/vm_map.c: (vm_map_setup) initialize new fields (vm_map_enforce_limits) new function (vm_map_copy_limits) new function (vm_map_find_entry) call limit enforcer function (vm_map_enter) likewise (vm_map_copyout) likewise (vm_map_copyout_page_list) likewise (vm_map_fork) compute and set size_none of the new map --- include/mach/gnumach.defs | 18 kern/task.c | 74 ++ tests/test-task.c | 45 ++ vm/vm_map.c | 96 ++- vm/vm_map.h | 13 ++ 5 files changed, 244 insertions(+), 2 deletions(-) diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs index f13e866b..5c0b8b3d 100644 --- a/include/mach/gnumach.defs +++ b/include/mach/gnumach.defs @@ -223,3 +223,21 @@ simpleroutine thread_set_name( routine thread_get_name( thread : thread_t; out name : kernel_debug_name_t); + +/* + * Set the target_task virtual memory limit + */ +routine task_set_vm_limit( + target_task : task_t; + host_priv : host_priv_t; + current_limit : vm_size_t; + max_limit : vm_size_t); + +/* + * Get the target_task virtual memory limit + */ +routine task_get_vm_limit( + target_task : task_t; + host_priv : host_priv_t; + out current_limit : vm_size_t; + out max_limit : vm_size_t); diff --git a/kern/task.c b/kern/task.c index bd57ca2a..e8074bfa 100644 --- a/kern/task.c +++ b/kern/task.c @@ -167,6 +167,10 @@ task_create_kernel( pset_reference(pset); new_task->priority = parent_task->priority; task_unlock(parent_task); + + vm_map_lock(parent_task->map); + vm_map_copy_limits(parent_task->map, new_task->map); + vm_map_unlock(parent_task->map); } else { pset = &default_pset; @@ -1355,3 +1359,73 @@ register_new_task_notification( new_task_notification = notification; return KERN_SUCCESS; } + +/* + * task_set_vm_limit + * + * Sets the current/maximum virtual adress space limits + * of the `target_task`. + * + * The host privileged port must be provided to alter the + * limits in any of the following ways: + * + * - limit an arbitrary task; otherwise target_task must be the current task + * - increase the maximum limit + */ +kern_return_t +task_set_vm_limit( + task_t target_task, + host_t host, + vm_size_t current_limit, + vm_size_t max_limit) +{ + if (target_task == TASK_NULL || (current_task() != target_task && host == HOST_NULL)) { + return KERN_INVALID_ARGUMENT; + } + + if (current_limit > max_limit) { + return KERN_INVALID_ARGUMENT; + } + + vm_map_lock(target_task->map); + if (host == HOST_NULL) { + if (target_task->map->map_size_max_limit < max_limit) { + vm_map_unlock(target_task->map); + return KERN_INVALID_ARGUMENT; + } + } + + target_task->map->map_size_cur_limit = current_limit; + target_task->map->map_size_max_limit = max_limit; + vm_map_unlock(target_task->map); + + return KERN_SUCCESS; +} + +/* + * task_get_vm_limit + * + * Gets the current/maximum virtual adress space limits + * of the `target_task`. + * + * The host privileged port must be provided to operate + * on task other than the current task. + */ +kern_return_t +task_get_vm_limit( + task_t target_task, + host_t host, + vm_size_t *current_limit, + vm_size_t *max_limit) +{ + if (target_task == TASK_NULL || (current_task() != target_task && host == HOST_NULL)) { + return KERN_INVALID_ARGUMENT; + } + + vm_map_lock(target_task->map); + *current_limit = target_task->map->map_size_cur_limit; + *max_limit = target_task->map->map_size_max_limit; + vm_map_unlock(target_task->map); + + return KERN_SUCCESS; +} diff --git a/tests/test-task.c b/tests/test-task.c index cbc75e23..789eb680 100644 --- a/tests/test-task.c +++ b/tests/test-task.c @@ -160,6 +160,50 @@ int test_errors() ASSERT(err == MACH_SEND_INVALID_DEST, "task DEAD"); } +void test_vm_limits() +{ +#define HOST_NULL ((host_t) 0) + kern_return_t err; + vm_address_t mem; +
[PATCH hurd v1] Add partial /proc/cpuinfo implementation
From: Diego Nieto Cid Hello, This is a new version of the cpuinfo patch. I added some #ifdef to include the cpuinfo.h or the march_aarch64 header accordingly. I also attempted to implement a version of the content generation function for aarch64 with the information in the link Sergey provided. However, I'm not sure if I included the correct header to get access to the RPC for fetching the privileged registers information. Flag names has been taken from Linux's cpufeatures.h for x86 and gnumach's mach_aarch64_types.h (where I lowercased the HWCAP* definitions). Also, multi-processor support is lacking in both implementations and bogomips calculation is not implemented but set to 0 in aarch64 (where its position is before other provided fields) and skipped for x86. Thanks, Diego -- >8 -- >8 -- >8 -- * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function (rootdir_entries) add entry for cpuinfo file (cpuinfo_x86, cpuinfo_aarch64) implementations for x86 and aarch64 respectively. --- procfs/rootdir.c | 220 +++ 1 file changed, 220 insertions(+) diff --git a/procfs/rootdir.c b/procfs/rootdir.c index b8a8c4d1..37ad2af3 100644 --- a/procfs/rootdir.c +++ b/procfs/rootdir.c @@ -38,6 +38,12 @@ #include "procfs_dir.h" #include "main.h" #include +#if defined (__x86_64__) || defined (__i486__) || defined (__i586__) || defined (__i686__) +#include +#elif defined (__aarch64__) +#warning Aarch64 port of cpuinfo is untested +#include +#endif #include "mach_debug_U.h" #include "pfinet_U.h" @@ -696,6 +702,213 @@ out_fclose: fclose (m); return err; } + +#if defined(__x86_64__) || defined (__i486__) || defined (__i586__) || defined (__i686__) +static char * cpu_features_edx[] = + { +"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", +NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clfush", +NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm", +"ia64", "pbe" + }; + +static char * cpu_features_ecx[] = + { +"sse3", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est", +"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm", +NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", +"tsc_deadline_timer", "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", "hypervisor" + }; + +#define VENDOR_ID_LEN 12 +#define MODEL_NAME_LEN 48 + +static error_t +cpuinfo_x86 (void* hook, char **contents, ssize_t *contents_len) +{ + error_t err = 0; + FILE* m; + int ret, index, stepping, model, family, extended_model, extended_family; + unsigned int eax, ebx, ecx, edx; + unsigned int feature_edx, feature_ecx; + char vendor[VENDOR_ID_LEN + 1] = { 0 }; + char model_name[MODEL_NAME_LEN + 1] = { 0 }; + + m = open_memstream(contents, (size_t *) contents_len); + if (m == NULL) +return errno; + + ret = __get_cpuid(0, &eax, &ebx, &ecx, &edx); + if (ret != 1) +{ + err = EIO; + goto out; +} + + memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(vendor + 1 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + memcpy(vendor + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + + ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx); + if (ret != 1) +{ + err = EIO; + goto out; +} + + feature_edx = edx; + feature_ecx = ecx; + stepping = eax & 0x0F; + model = (eax & 0xF0) >> 4; + family = (eax & 0xF00) >> 8; + extended_model = (eax & 0xF) >> 16; + extended_family = (eax &0xFF0) >> 20; + + if (family == 6 || family == 15) +model += (extended_model << 4); + if (family == 15) +family += extended_family; + + __get_cpuid(0x8000, &eax, &ebx, &ecx, &edx); + if (eax >= 0x8004) +{ + __get_cpuid(0x8002, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 0 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 1 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 3 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + + __get_cpuid(0x8003, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 4 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 5 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 6 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 7 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + + __get_cpuid(0x8004, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 8 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 9 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 10 * sizeof(unsigned int), (char *) &ecx
[PATCH hurd v1] Add partial /proc/cpuinfo implementation
From: Diego Nieto Cid Hello, Sorry for the duplicate, I found a missing semicolon in the unsupported architecture case. Now fixed. Thanks, Diego -- >8 -- >8 -- >8 -- * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function (rootdir_entries) add entry for cpuinfo file (cpuinfo_x86, cpuinfo_aarch64) implementations for x86 and aarch64 respectively. --- procfs/rootdir.c | 220 +++ 1 file changed, 220 insertions(+) diff --git a/procfs/rootdir.c b/procfs/rootdir.c index b8a8c4d1..37ad2af3 100644 --- a/procfs/rootdir.c +++ b/procfs/rootdir.c @@ -38,6 +38,12 @@ #include "procfs_dir.h" #include "main.h" #include +#if defined (__x86_64__) || defined (__i486__) || defined (__i586__) || defined (__i686__) +#include +#elif defined (__aarch64__) +#warning Aarch64 port of cpuinfo is untested +#include +#endif #include "mach_debug_U.h" #include "pfinet_U.h" @@ -696,6 +702,213 @@ out_fclose: fclose (m); return err; } + +#if defined(__x86_64__) || defined (__i486__) || defined (__i586__) || defined (__i686__) +static char * cpu_features_edx[] = + { +"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", +NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clfush", +NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm", +"ia64", "pbe" + }; + +static char * cpu_features_ecx[] = + { +"sse3", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est", +"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm", +NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", +"tsc_deadline_timer", "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", "hypervisor" + }; + +#define VENDOR_ID_LEN 12 +#define MODEL_NAME_LEN 48 + +static error_t +cpuinfo_x86 (void* hook, char **contents, ssize_t *contents_len) +{ + error_t err = 0; + FILE* m; + int ret, index, stepping, model, family, extended_model, extended_family; + unsigned int eax, ebx, ecx, edx; + unsigned int feature_edx, feature_ecx; + char vendor[VENDOR_ID_LEN + 1] = { 0 }; + char model_name[MODEL_NAME_LEN + 1] = { 0 }; + + m = open_memstream(contents, (size_t *) contents_len); + if (m == NULL) +return errno; + + ret = __get_cpuid(0, &eax, &ebx, &ecx, &edx); + if (ret != 1) +{ + err = EIO; + goto out; +} + + memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(vendor + 1 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + memcpy(vendor + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + + ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx); + if (ret != 1) +{ + err = EIO; + goto out; +} + + feature_edx = edx; + feature_ecx = ecx; + stepping = eax & 0x0F; + model = (eax & 0xF0) >> 4; + family = (eax & 0xF00) >> 8; + extended_model = (eax & 0xF) >> 16; + extended_family = (eax &0xFF0) >> 20; + + if (family == 6 || family == 15) +model += (extended_model << 4); + if (family == 15) +family += extended_family; + + __get_cpuid(0x8000, &eax, &ebx, &ecx, &edx); + if (eax >= 0x8004) +{ + __get_cpuid(0x8002, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 0 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 1 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 3 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + + __get_cpuid(0x8003, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 4 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 5 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 6 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 7 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + + __get_cpuid(0x8004, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 8 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 9 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 10 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 11 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); +} + + fprintf(m, +"processor : 0\n" +"vendor_id : %s\n" +"cpu family : %d\n" +"model : %d\n" +"model name : %s\n" +"stepping: %d\n", +vendor, family, model, model_name, stepping); + + fprintf(m, "flags :"); + for (index = 0; index < (sizeof(cpu_features_edx)/sizeof(char*)); index++) +{ + if (cpu_features_edx[index] == NULL) +continue; + if (feature_edx & (1ul << index)) +fprintf(m, " %s", cpu_features_edx[ind
[PATCH gnumach v1] [aarch64] make hwcaps_t a pointer to uint64_t
From: Diego Nieto Cid Hi, I went ahead and made a patch for the hwcaps_t type. I'm not sure if it is that simple. I figured the implementation of the RPC should be changed too, but couldn't find it in gnumach sources. Also, I didn't test this at all. I guess I need to somehow cross compile and run it in qemu... I hope this helps more than being a burden to review :) -- >8 -- >8 -- >8 -- * aarch64/include/mach/aarch64/mach_aarch64.defs: (hwcaps_t) make type an array of uint64_t. * aarch64/include/mach/aarch64/mach_aarch64_types.h (hwcaps_t) make type a pointer to uint64_t --- aarch64/include/mach/aarch64/mach_aarch64.defs| 2 +- aarch64/include/mach/aarch64/mach_aarch64_types.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aarch64/include/mach/aarch64/mach_aarch64.defs b/aarch64/include/mach/aarch64/mach_aarch64.defs index 0fe1eb62..da1e9498 100644 --- a/aarch64/include/mach/aarch64/mach_aarch64.defs +++ b/aarch64/include/mach/aarch64/mach_aarch64.defs @@ -43,7 +43,7 @@ import ; * versions can add more items and more bits (HWCAP3_* and * so forth). */ -type hwcaps_t= array[*:16] of uint32_t; +type hwcaps_t= array[*:16] of uint64_t; routine aarch64_get_hwcaps( host: host_t; diff --git a/aarch64/include/mach/aarch64/mach_aarch64_types.h b/aarch64/include/mach/aarch64/mach_aarch64_types.h index 98fd6c4b..efb9f897 100644 --- a/aarch64/include/mach/aarch64/mach_aarch64_types.h +++ b/aarch64/include/mach/aarch64/mach_aarch64_types.h @@ -25,7 +25,7 @@ #ifndef __ASSEMBLER__ #include -typedef uint32_t *hwcaps_t; +typedef uint64_t *hwcaps_t; #endif /* These definitions are meant to match those in -- 2.47.1
[PATCH] procfs: set d_type on returned direntries
From: Diego Nieto Cid Hello, This sets d_type to DT_DIR in those entries of procfs which are actually directories; which allows libgtop to list processes using the linux port code. Hope this is what you had in mind Samuel. It's not very robust, but I couldn't find other way to do it. Regards, Diego -- >8 -- >8 -- >8 -- --- procfs/netfs.c | 25 - 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/procfs/netfs.c b/procfs/netfs.c index 4ed5eab6..3cf7a8e2 100644 --- a/procfs/netfs.c +++ b/procfs/netfs.c @@ -115,6 +115,29 @@ error_t netfs_attempt_readlink (struct iouser *user, struct node *np, return 0; } +static unsigned char entry_type(char *name) +{ + char c; + int only_numbers; + + if (name[0] == '.' && name[1] == 0) +return DT_DIR; + + if (name[0] == '.' && name[1] == '.' && name[2] == 0) +return DT_DIR; + + if (strcmp(name, "self") == 0) +return DT_DIR; + + for(only_numbers = 1, c = *name; only_numbers && c; c = *++name) +{ + if (c < '0' || c > '9') +only_numbers = 0; +} + + return only_numbers ? DT_DIR : DT_UNKNOWN; +} + /* Helper function for netfs_get_dirents() below. CONTENTS is an argz vector of directory entry names, as returned by procfs_get_contents(). Convert at most NENTRIES of them to dirent structures, put them in @@ -142,7 +165,7 @@ static int putentries (char *contents, size_t contents_len, int nentries, d->d_fileno = 42; /* XXX */ d->d_namlen = namlen; d->d_reclen = reclen; - d->d_type = DT_UNKNOWN; + d->d_type = entry_type(contents); memcpy (d->d_name, contents, namlen + 1); if (pad) memset(d->d_name + namlen + 1, 0, pad); -- 2.47.1
[PATCH v2] procfs: set d_type on returned direntries
From: Diego Nieto Cid Hello, Thanks for the pointer Samuel. I managed to call procfs_lookup and extract from the node stat structure its type. It mostly works except for the ".." and "self" entries: ./test-proc//..: d_type: 0 ./test-proc//self: d_type: 0 Well "self" is probably a symlink or something similar. And ".." probably comes from the parent translator (ext2fs), I'm not sure. Also, I noticed procfs_lookup does a netfs_nref call for the returned node. So, I called netfs_nput after extracting its type. However, I don't think I got the locking correctly. That are all the comments for version 2 of the patch. Regards, Diego -- >8 -- >8 -- >8 -- --- procfs/netfs.c | 17 + 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/procfs/netfs.c b/procfs/netfs.c index 4ed5eab6..83db7517 100644 --- a/procfs/netfs.c +++ b/procfs/netfs.c @@ -121,7 +121,7 @@ error_t netfs_attempt_readlink (struct iouser *user, struct node *np, DATA (if not NULL), write the number of entries processed in *AMT and return the required/used space in DATACNT. */ static int putentries (char *contents, size_t contents_len, int nentries, - char *data, mach_msg_type_number_t *datacnt) + char *data, mach_msg_type_number_t *datacnt, struct node *dir) { int align = __alignof (struct dirent); int i; @@ -138,11 +138,20 @@ static int putentries (char *contents, size_t contents_len, int nentries, if (data) { + error_t err; + struct node *np; struct dirent *d = (struct dirent *) (data + *datacnt); d->d_fileno = 42; /* XXX */ d->d_namlen = namlen; d->d_reclen = reclen; - d->d_type = DT_UNKNOWN; + err = procfs_lookup(dir, contents, &np); + if (err) +d->d_type = DT_UNKNOWN; + else +{ + d->d_type = (np->nn_stat.st_mode & S_IFMT) == S_IFDIR ? DT_DIR : DT_UNKNOWN; + netfs_nput(np); +} memcpy (d->d_name, contents, namlen + 1); if (pad) memset(d->d_name + namlen + 1, 0, pad); @@ -190,7 +199,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, } /* Allocate a buffer if necessary. */ - putentries (contents, contents_len, nentries, NULL, datacnt); + putentries (contents, contents_len, nentries, NULL, datacnt, NULL); if (bufsize < *datacnt) { char *n = mmap (0, *datacnt, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, 0, 0); @@ -201,7 +210,7 @@ error_t netfs_get_dirents (struct iouser *cred, struct node *dir, } /* Do the actual conversion. */ - *amt = putentries (contents, contents_len, nentries, *data, datacnt); + *amt = putentries (contents, contents_len, nentries, *data, datacnt, dir); return 0; } -- 2.47.1
[PATCH hurd v2] Add partial /proc/cpuinfo implementation
From: Diego Nieto Cid Hello, On Sun, Jan 26, 2025 at 10:18:03PM +0100, Samuel Thibault wrote: > > dnie...@gmail.com, le ven. 10 janv. 2025 23:52:28 +, a ecrit: > > --- a/procfs/rootdir.c > > + > > + m = open_memstream(contents, (size_t *) contents_len); > > Please mind the GNU coding style which wants a space before opening > parentheses, here and in other places (function calls, sizeof, etc.). > I hope this time I got it better :) > > + > > + memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned > > int)); > > There is no need to cast &ebx, memcpy takes a void*, so the cast is > implicit in C. > Ok, I removed the casts. > > > + > > + fprintf(m, > > +"processor : 0\n" > > +"vendor_id : %s\n" > > +"cpu family : %d\n" > > +"model : %d\n" > > +"model name : %s\n" > > +"stepping: %d\n", > > +vendor, family, model, model_name, stepping); > > + > > + fprintf(m, "flags :");v > > Linux uses tab characters before ':', better use that too for > compatibility. > Ok, I switched to tabs. Though, I had to use a mix of one and two tabs. > > + > > + m = open_memstream(contents, (size_t *) contents_len); > > + if (m == NULL) > > +return ENOMEM; > > Better return errno like on x86. > Done so. > > + > > + err = aarch64_get_hwcaps(mach_host_self(), &caps, &mdir, &revdir); > > + if (err) > > +goto out; > > + > > + implementer = (mdir & 0xff00) >> 24; > > + variant = (mdir & 0x00f0) >> 20; > > + architecture = (mdir & 0x000f) >> 16; > > + part_num = (mdir & 0xfff0) >> 4; > > + revision = (mdir & 0x000f) >> 0; > > Better align mdir too to make it look nicer. > I aligned the = sign, the variable and the semi-colon. Now it should look better. > > + > > + fprintf(m, "processor : 0\n"); > > + fprintf(m, "BogoMIPS: 0\n"); > > I'd rather not print BogoMIPS at all rather than 0. > Ok, I just removed the BogoMIPS entry until we figure out how to compute it properly. > > Also use tabs as well for compatibility. > Done > > +out: > > + flcose(m); > > You didn't try to compile it ;) > Oops, it didn't cross my mind I could do that :-) -- On Mon, Jan 27, 2025 at 12:25:15AM +0300, Sergey Bugaev wrote: > On Mon, Jan 27, 2025 at 12:18 AM Samuel Thibault > wrote: > > > > Better align mdir too to make it look nicer. > > Speaking of which, it's midr (main identification register) and revidr > (revision identification register), not *dir. > Thanks, I renamed the variables. revidr is unused though, the docs said it was implementation defined so I didn't know what to do with it. -- On Sun, Jan 26, 2025 at 10:27:57PM +0100, Samuel Thibault wrote: > Sergey Bugaev, le lun. 27 janv. 2025 00:25:15 +0300, a ecrit: > > > > +out: > > > > + flcose(m); > > > > > > You didn't try to compile it ;) > > > > I suppose that's not easy for Diego to do, given the state of aarch64-gnu :) > > Sure, but he can comment the very few really-arm64-specific lines and > try to build the rest :) > It didn't occur to me about testing that way. I fiddled with the defined macros and replaced the RPC with some harcoded data, it should work now. Regards, Diego -- >8 -- >8 -- >8 -- * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function (rootdir_entries) add entry for cpuinfo file (cpuinfo_x86, cpuinfo_aarch64) implementations for x86 and aarch64 respectively. --- procfs/rootdir.c | 219 +++ 1 file changed, 219 insertions(+) diff --git a/procfs/rootdir.c b/procfs/rootdir.c index b8a8c4d1..05304015 100644 --- a/procfs/rootdir.c +++ b/procfs/rootdir.c @@ -38,6 +38,12 @@ #include "procfs_dir.h" #include "main.h" #include +#if defined (__x86_64__) || defined (__i486__) || defined (__i586__) || defined (__i686__) +#include +#elif defined (__aarch64__) +#warning Aarch64 port of cpuinfo is untested +#include +#endif #include "mach_debug_U.h" #include "pfinet_U.h" @@ -696,6 +702,212 @@ out_fclose: fclose (m); return err; } + +#if defined (__x86_64__) || defined (__i486__) || defined (__i586__) || defined (__i686__) +static char *cpu_features_edx[] = + { +"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", +NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "clfush", +NULL, "dts", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "ht", "tm", +"ia64", "pbe" + }; + +static char *cpu_features_ecx[] = + { +"sse3", "pclmulqdq", "dtes64", "monitor", "ds_cpl", "vmx", "smx", "est", +"tm2", "ssse3", "cid", "sdbg", "fma", "cx16", "xtpr", "pdcm", +NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", +"tsc_deadline_timer", "aes", "xsave", "osxsave", "avx", "f16c", "rdrand", "hypervisor" + }; + +#define VENDOR_ID_LEN 12 +#define MODEL_NAME_LEN 48 + +static error_t +cpuinfo_x86 (void* hook, char **contents, ssize_t *contents_len) +{ +
[PATCH gnumach v2] Implement per task virtual memory limit
From: Diego Nieto Cid Merry Christmas Hurd community! I hope everyone is doing well and enjoying the holidays. This is version 2 of the VM limit patch, which without the continued support of Sergey, could not be possible. So, thanks Sergey! --- * include/mach/gnumach.defs: (vm_set_size_limit) new routine (vm_get_size_limit) likewise * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual memory limit * tests/test-vm.c: (test_vm_limit) add test for the new routines * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and size_max_limit * vm/vm_map.c: (vm_map_setup) initialize new fields (vm_map_enforce_limit) new function (vm_map_copy_limits) new function (vm_map_find_entry) call limit enforcer function (vm_map_enter) likewise (vm_map_copyout) likewise (vm_map_copyout_page_list) likewise (vm_map_fork) copy parent limit to the new map and compute and set size_none of the new map * vm/vm_user.c: (vm_set_size_limit) new function (vm_get_size_limit) likewise --- include/mach/gnumach.defs | 33 + kern/task.c | 5 ++ tests/test-vm.c | 85 + vm/vm_map.c | 99 ++- vm/vm_map.h | 13 + vm/vm_user.c | 69 +++ 6 files changed, 302 insertions(+), 2 deletions(-) diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs index f13e866b..a07a1011 100644 --- a/include/mach/gnumach.defs +++ b/include/mach/gnumach.defs @@ -223,3 +223,36 @@ simpleroutine thread_set_name( routine thread_get_name( thread : thread_t; out name : kernel_debug_name_t); + +/* + * Set a task virtual memory limit parameters + * + * HOST_PORT must be the privileged host control port + * if the caller desires to increase the current max limit. + * + * On the other hand, if the max limit is being decreased, the + * unprivileged host control port (as returned by mach_host_self()) + * can be provided. + * + * Returns: + * - KERN_SUCCESS + * - KERN_INVALID_TASK + * - KERN_INVALID_HOST + * - KERN_INVALID_ARGUMENT + * * when current_limit > max_limit + * * attempt to increase max limit without providing + * the privileged host control port. + */ +routine vm_set_size_limit( + host_port : mach_port_t; + map : vm_task_t; + current_limit : vm_size_t; + max_limit : vm_size_t); + +/* + * Get a task virtual memory limit parameters + */ +routine vm_get_size_limit( + map : vm_task_t; + out current_limit : vm_size_t; + out max_limit : vm_size_t); diff --git a/kern/task.c b/kern/task.c index bd57ca2a..a3204279 100644 --- a/kern/task.c +++ b/kern/task.c @@ -126,6 +126,11 @@ task_create_kernel( trunc_page(VM_MAX_USER_ADDRESS)); if (new_task->map == VM_MAP_NULL) pmap_destroy(new_pmap); + else if (parent_task != TASK_NULL) { + vm_map_lock_read(parent_task->map); + vm_map_copy_limits(parent_task->map, new_task->map); + vm_map_unlock_read(parent_task->map); + } } } if (new_task->map == VM_MAP_NULL) { diff --git a/tests/test-vm.c b/tests/test-vm.c index 4ece792e..8e4ad884 100644 --- a/tests/test-vm.c +++ b/tests/test-vm.c @@ -75,11 +75,96 @@ static void test_wire() // TODO check that all memory is actually wired or unwired } +void test_vm_limit() +{ + kern_return_t err; + vm_address_t mem, mem2, mem3; + const size_t M_128K = 128l * 1024l; + const size_t M_128M = 128l * 1024l * 1024l; + const size_t M_512M = 512l * 1024l * 1024l; + vm_size_t cur; + vm_size_t max; + + /* set VM memory limitations */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M); + ASSERT_RET(err, "cannot set VM limits"); + + /* check limits are actually saved */ + err = vm_get_size_limit(mach_task_self(), &cur, &max); + ASSERT_RET(err, "getting the VM limit failed"); + ASSERT(cur == M_128M, "cur limit was not expected"); + ASSERT(max == M_512M, "max limit was not expected"); + + /* check we can no longer increase the max limit */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 2); + ASSERT(err == KERN_INVALID_ARGUMENT, "raising VM max limit shall fail with KERN_INVALID_ARGUMENT"); + + /* alloc some memory below the limit */ + err = vm_allocate(mach_task_self(), &mem, M_128K, TRUE); + ASSERT_RET(err, "allocating memory below the limit must succeed"); + err = vm_deallocate(mach_task_self(), mem, M_128K); + ASSERT_RET(err, "deallocation failed"); + + /* alloc a bigger chunk to make it hit the limit */ +
[PATCH gnumach v1] Implement per task virtual memory limit
From: Diego Nieto Cid This is the first iteration of the patch. I incorporated the suggestions made by Sergey is his review. I also used the implementation of the vm_set_size_limit based on the host port being the reciever of the RPC (that Sergey sent to the other thread). There remains a TODO regarding how to get swap size to set the initial limit parameters to the value suggested by Samuel here[1]. [1] https://lists.gnu.org/archive/html/bug-hurd/2024-12/msg00217.html --- * include/mach/gnumach.defs: (vm_set_size_limit) new routine (vm_get_size_limit) likewise * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual memory limit * tests/test-vm.c: (test_vm_limit) add test for the new routines * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and size_max_limit * vm/vm_map.c: (vm_map_setup) initialize new fields (vm_map_enforce_limit) new function (vm_map_copy_limits) new function (vm_map_find_entry) call limit enforcer function (vm_map_enter) likewise (vm_map_copyout) likewise (vm_map_copyout_page_list) likewise (vm_map_fork) copy parent limit to the new map and compute and set size_none of the new map * vm/vm_user.c: (vm_set_size_limit) new function (vm_get_size_limit) likewise --- include/mach/gnumach.defs | 17 + kern/task.c | 5 +++ tests/test-vm.c | 44 ++ vm/vm_map.c | 79 +++ vm/vm_map.h | 13 +++ vm/vm_user.c | 66 6 files changed, 224 insertions(+) diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs index f13e866b..a09256b8 100644 --- a/include/mach/gnumach.defs +++ b/include/mach/gnumach.defs @@ -223,3 +223,20 @@ simpleroutine thread_set_name( routine thread_get_name( thread : thread_t; out name : kernel_debug_name_t); + +/* + * Set a task virtual memory limit parameters + */ +routine vm_set_size_limit( + host_port : mach_port_t; + map : vm_task_t; + current_limit : vm_size_t; + max_limit : vm_size_t); + +/* + * Get a task virtual memory limit parameters + */ +routine vm_get_size_limit( + map : vm_task_t; + out current_limit : vm_size_t; + out max_limit : vm_size_t); diff --git a/kern/task.c b/kern/task.c index bd57ca2a..255f4388 100644 --- a/kern/task.c +++ b/kern/task.c @@ -126,6 +126,11 @@ task_create_kernel( trunc_page(VM_MAX_USER_ADDRESS)); if (new_task->map == VM_MAP_NULL) pmap_destroy(new_pmap); + else { + vm_map_lock(parent_task->map); + vm_map_copy_limits(parent_task->map, new_task->map); + vm_map_unlock(parent_task->map); + } } } if (new_task->map == VM_MAP_NULL) { diff --git a/tests/test-vm.c b/tests/test-vm.c index 4ece792e..74fcc309 100644 --- a/tests/test-vm.c +++ b/tests/test-vm.c @@ -75,11 +75,55 @@ static void test_wire() // TODO check that all memory is actually wired or unwired } +void test_vm_limit() +{ + kern_return_t err; + vm_address_t mem; + const size_t M_128M = 128l * 1024l * 1024l; + const size_t M_512M = 512l * 1024l * 1024l; + vm_size_t cur; + vm_size_t max; + + /* set VM memory limitations */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M); + ASSERT(err == KERN_SUCCESS, "cannot set VM limits"); + + /* check limits are actually saved */ + err = vm_get_size_limit(mach_task_self(), &cur, &max); + ASSERT(err == KERN_SUCCESS, "getting the VM limit failed"); + ASSERT(cur == M_128M, "cur limit was not expected"); + ASSERT(max == M_512M, "max limit was not expected"); + + /* check we can no longer increase the hard limit */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 2); + ASSERT(err == KERN_INVALID_HOST, "raising VM hard limit shall fail"); + + /* alloc some memory below the limit */ + err = vm_allocate(mach_task_self(), &mem, (128l * 1024l), TRUE); + ASSERT(err == KERN_SUCCESS, "allocating memory below the limit must succeed"); + vm_deallocate(mach_task_self(), mem, (128l * 1024l)); + + /* alloc a bigger chunk to make it hit the limit */ + err = vm_allocate(mach_task_self(), &mem, (M_512M * 2), TRUE); + ASSERT(err == KERN_NO_SPACE, "allocation must fail with KERN_NO_SPACE"); + + /* check that "root" can increase the hard limit */ + err = vm_set_size_limit(host_priv(), mach_task_self(), M_128M, M_512M * 2); + ASSERT(err == KERN_SUCCESS, "privileged tasks shall be allowed to increase the max limit"); + + /* check limits are actually saved */ + err = vm_get_size_limit(mach_task_self(), &cur, &max); + ASSERT(err == KERN_SUCCESS
[RFC PATCH hurd] Add partial /proc/cpuinfo implementation
From: Diego Nieto Cid Hello, This is a patch to introduce the cpuinfo file to the hierarchy exposed by procfs. I used as guidance the Wikipedia article on the CPUID instruction and the GNU/Linux output to sort each line in the output. There remains a lot of fields that I haven't covered yet and SMP is not supported (in alignment with the output of /proc/stat). But it seems to help in the porting of libgtop to GNU/Hurd, which I started from a copy of Linux implementation. Beside that, I probably need to cache the CPUID provided information as I don't think it would change over time. The output below is what currently is implemented: demo@debian:~/dev/hurd/upstream/hurd/build-tree$ settrans -a test-proc procfs/procfs demo@debian:~/dev/hurd/upstream/hurd/build-tree$ cat ./test-proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 25 model : 80 model name : AMD Ryzen 9 5900HX with Radeon Graphics stepping: 0 flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clfsh mmx fxsr sse sse2 htt sse3 pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes_ni rdrnd So what else do you think should be included in our implementation of cpuinfo? Thanks, Diego -- >8 -- >8 -- >8 -- * procfs/rootdir.c: (rootdir_gc_cpuinfo) new function (rootdir_entries): add entry for cpuinfo file --- procfs/rootdir.c | 127 +++ 1 file changed, 127 insertions(+) diff --git a/procfs/rootdir.c b/procfs/rootdir.c index b8a8c4d1..966e2eee 100644 --- a/procfs/rootdir.c +++ b/procfs/rootdir.c @@ -38,6 +38,7 @@ #include "procfs_dir.h" #include "main.h" #include +#include #include "mach_debug_U.h" #include "pfinet_U.h" @@ -696,6 +697,125 @@ out_fclose: fclose (m); return err; } + +static char * features_edx[] = + { +"fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", +NULL, "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "psn", "clfsh", +NULL, "ds", "acpi", "mmx", "fxsr", "sse", "sse2", "ss", "htt", "tm", + "ia64", "pbe" + }; + +static char * features_ecx[] = + { +"sse3", "pclmulqdq", "dtes64", "monitor", "ds-cpl", "vmx", "smx", "est", +"tm3", "ssse3", "cnxt-id", "sdbg", "fma", "cx16", "xtpr", "pdcm", +NULL, "pcid", "dca", "sse4_1", "sse4_2", "x2apic", "movbe", "popcnt", + "tsc_deadline", "aes_ni", "xsave", "osxsave", "avx", "f16c", "rdrnd", "hypervisor" + }; + + +static error_t +rootdir_gc_cpuinfo (void *hook, char **contents, ssize_t *contents_len) +{ +# define VENDOR_ID_LEN 12 +# define MODEL_NAME_LEN 48 + error_t err = 0; + int ret, index, stepping, model, family, extended_model, extended_family; + unsigned int eax, ebx, ecx, edx; + unsigned int feature_edx, feature_ecx; + char vendor[VENDOR_ID_LEN + 1] = { 0 }; + char model_name[MODEL_NAME_LEN + 1] = { 0 }; + FILE* m; + + m = open_memstream(contents, (size_t *) contents_len); + if (m == NULL) +return errno; + + ret = __get_cpuid(0, &eax, &ebx, &ecx, &edx); + if (ret != 1) +{ + err = EIO; + goto out; +} + + memcpy(vendor + 0 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(vendor + 1 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + memcpy(vendor + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + + ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx); + if (ret != 1) +{ + err = EIO; + goto out; +} + + feature_edx = edx; + feature_ecx = ecx; + stepping = eax & 0x0F; + model = (eax & 0xF0) >> 4; + family = (eax & 0xF00) >> 8; + extended_model = (eax & 0xF) >> 16; + extended_family = (eax &0xFF0) >> 20; + + if (family == 6 || family == 15) +model += (extended_model << 4); + if (family == 15) +family += extended_family; + + __get_cpuid(0x8000, &eax, &ebx, &ecx, &edx); + if (eax >= 0x8004) +{ + __get_cpuid(0x8002, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 0 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 1 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 2 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 3 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + + __get_cpuid(0x8003, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 4 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 5 * sizeof(unsigned int), (char *) &ebx, sizeof(unsigned int)); + memcpy(model_name + 6 * sizeof(unsigned int), (char *) &ecx, sizeof(unsigned int)); + memcpy(model_name + 7 * sizeof(unsigned int), (char *) &edx, sizeof(unsigned int)); + + __get_cpuid(0x8004, &eax, &ebx, &ecx, &edx); + memcpy(model_name + 8 * sizeof(unsigned int), (char *) &eax, sizeof(unsigned int)); + memcpy(model_name + 9 *
[PATCH hurd v1] ext2fs: raise RLIMIT_AS if running as the root fs
From: Diego Nieto Cid Hello, The ext2fs side of the changes seems to be as strightforward as calling setrlimit when the bootstraap port is null, given the following lines of diskfs_init_main: if (diskfs_boot_filesystem ()) /* We are the bootstrap filesystem. */ *bootstrap = MACH_PORT_NULL; ... I'm not sure abount the bit declaring variables inside the if block being conforming to the standards. Now I'll look into using the RPC in GLIBC's implementation of setrlimit. But I still need to figure out how to install the headers with the new RPC routines. Regards, Diego -- >8 -- >8 -- >8 -- * ext2fs/ext2fs.c(main): set current and max RLIMIT_AS to RLIM_INFINITY when running as the root filesystem. --- ext2fs/ext2fs.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/ext2fs/ext2fs.c b/ext2fs/ext2fs.c index 3836bdf6..689e5e5b 100644 --- a/ext2fs/ext2fs.c +++ b/ext2fs/ext2fs.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -230,6 +232,15 @@ main (int argc, char **argv) store = diskfs_init_main (&startup_argp, argc, argv, &store_parsed, &bootstrap); + /* Lift default address space limits when we are the root filesystem */ + if (bootstrap == MACH_PORT_NULL) +{ + struct rlimit limits = { RLIM_INFINITY, RLIM_INFINITY }; + int err = setrlimit (RLIMIT_AS, &limits); + if (err == -1) +perror ("error lifting address space limits"); +} + if (store->size < SBLOCK_OFFS + SBLOCK_SIZE) ext2_panic ("device too small for superblock (%" PRIi64 " bytes)", store->size); if (store->log2_blocks_per_page < 0) -- 2.47.1
[PATCH glibc v1] Hurd limits: implement RLIMIT_AS against Mach RPCs
From: Diego Nieto Cid Check for VM limit RPCs * config.h.in: add #undef for HAVE_MACH_VM_GET_SIZE_LIMIT and HAVE_MACH_VM_SET_SIZE_LIMIT. * sysdeps/mach/configure.ac: use mach_RPC_CHECK to check for vm_set_size_limit and vm_get_size_limit RPCs in gnumach.defs. * sysdeps/mach/configure: regenerate file. Use vm_get_size_limit to initialize RLIMIT_AS * hurd/hurdrlimit.c(init_rlimit): use vm_get_size_limit to initialize RLIMIT_AS entry of the _hurd_rlimits array. Notify the kernel of the new VM size limits * sysdeps/mach/hurd/setrlimit.c: use the vm_set_size_limit RPC, if available, to notify the kernel of the new limits. Retry RPC calls if they were interrupted by a signal. --- config.h.in | 6 hurd/hurdrlimit.c | 5 +++ sysdeps/mach/configure| 60 +++ sysdeps/mach/configure.ac | 4 +++ sysdeps/mach/hurd/setrlimit.c | 57 - 5 files changed, 131 insertions(+), 1 deletion(-) mode change 100644 => 100755 sysdeps/mach/configure diff --git a/config.h.in b/config.h.in index 29126ea933..3dbb3fd6af 100644 --- a/config.h.in +++ b/config.h.in @@ -164,6 +164,12 @@ /* Mach specific: define if the `thread_get_name' RPC is available. */ #undef HAVE_MACH_THREAD_GET_NAME +/* Mach specific: define if the `vm_get_size_limit' RPC is available. */ +#undef HAVE_MACH_VM_GET_SIZE_LIMIT + +/* Mach specific: define if the `vm_set_size_limit' RPC is available. */ +#undef HAVE_MACH_VM_SET_SIZE_LIMIT + /* Mach/i386 specific: define if the `i386_io_perm_*' RPCs are available. */ #undef HAVE_I386_IO_PERM_MODIFY diff --git a/hurd/hurdrlimit.c b/hurd/hurdrlimit.c index 6cb5045bfe..6b0d8a26a3 100644 --- a/hurd/hurdrlimit.c +++ b/hurd/hurdrlimit.c @@ -39,6 +39,11 @@ init_rlimit (void) for (i = 0; i < RLIM_NLIMITS; ++i) { +#ifdef HAVE_MACH_VM_GET_SIZE_LIMIT + if (i == RLIMIT_AS) +__vm_get_size_limit (__mach_task_self (), +&_hurd_rlimits[i].rlim_cur, &_hurd_rlimits[i].rlim_max); +#endif if (_hurd_rlimits[i].rlim_max == 0) _hurd_rlimits[i].rlim_max = RLIM_INFINITY; if (_hurd_rlimits[i].rlim_cur == 0) diff --git a/sysdeps/mach/configure b/sysdeps/mach/configure old mode 100644 new mode 100755 index 311b2dd30b..0161937ab4 --- a/sysdeps/mach/configure +++ b/sysdeps/mach/configure @@ -581,6 +581,66 @@ if test $libc_cv_mach_rpc_thread_get_name = yes; then fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for vm_set_size_limit in gnumach.defs" >&5 +printf %s "checking for vm_set_size_limit in gnumach.defs... " >&6; } +if test ${libc_cv_mach_rpc_vm_set_size_limit+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP_TRADITIONAL "vm_set_size_limit" >/dev/null 2>&1 +then : + libc_cv_mach_rpc_vm_set_size_limit=yes +else case e in #( + e) libc_cv_mach_rpc_vm_set_size_limit=no ;; +esac +fi +rm -rf conftest* + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_mach_rpc_vm_set_size_limit" >&5 +printf "%s\n" "$libc_cv_mach_rpc_vm_set_size_limit" >&6; } +if test $libc_cv_mach_rpc_vm_set_size_limit = yes; then + printf "%s\n" "#define HAVE_MACH_VM_SET_SIZE_LIMIT 1" >>confdefs.h + +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for vm_get_size_limit in gnumach.defs" >&5 +printf %s "checking for vm_get_size_limit in gnumach.defs... " >&6; } +if test ${libc_cv_mach_rpc_vm_get_size_limit+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP_TRADITIONAL "vm_get_size_limit" >/dev/null 2>&1 +then : + libc_cv_mach_rpc_vm_get_size_limit=yes +else case e in #( + e) libc_cv_mach_rpc_vm_get_size_limit=no ;; +esac +fi +rm -rf conftest* + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_mach_rpc_vm_get_size_limit" >&5 +printf "%s\n" "$libc_cv_mach_rpc_vm_get_size_limit" >&6; } +if test $libc_cv_mach_rpc_vm_get_size_limit = yes; then + printf "%s\n" "#define HAVE_MACH_VM_GET_SIZE_LIMIT 1" >>confdefs.h + +fi + ac_fn_c_check_header_preproc "$LINENO" "mach/machine/ndr_def.h" "ac_cv_header_mach_machine_ndr_def_h" if test "x$ac_cv_header_mach_machine_ndr_def_h" = xyes diff --git a/sysdeps/mach/configure.ac b/sysdeps/mach/configure.ac index 3a6f2443e2..237b8be937 100644 --- a/sysdeps/mach/configure.ac +++ b/sysdeps/mach/configure.ac @@ -100,6 +100,10 @@ mach_RPC_CHECK(gnumach.defs, thread_set_name, HAVE_MACH_THREAD_SET_NAME) mach_RPC_CHECK(gnumach.defs, thread_get_name, HAVE_MACH_THREAD_GET_NAME) +mach_RPC_CHECK(gnumach.defs, vm_set_size_limit, + HAVE_MACH_VM_SET_SIZE_LIMIT) +mach_RPC_CHECK(gnumach.defs, vm_get_size_li
[PATCH] Virtual Memory Limits
From: Diego Nieto Cid Hello, These are all the patches in the series of virtual memory limits support, through the POSIX setrlimit's RLIMIT_AS resource. It encompasses changes to gnumach, hurd and glibc. I tested the glibc part using a simple program that sets a limit of 1GB and then tries to mmap a bigger chunk of memory. It was run using the script provided by the library, testrun.sh. The gnumach part has it's own test in the kernel test suite. What I couldn't test is the Hurd patch, particularly the translators like ext2fs which must be started with settrans and I didn't find a way to use the patched glibc. (I'm not sure how to use testrun.sh from glibc in a settrans call) Any comments appreciated :) Thanks, Diego
[PATCH gnumach v4] Implement per task virtual memory limit
From: Diego Nieto Cid * include/mach/gnumach.defs: (vm_set_size_limit) new routine (vm_get_size_limit) likewise * kern/task.c: (task_create_kernel) if parent_task is not null copy virtual memory limit * tests/test-vm.c: (test_vm_limit) add test for the new routines * vm/vm_kern.c: (projected_buffer_allocate) increase size_none when protection is VM_PROT_NONE (projected_buffer_map) likewise * vm/vm_map.h: (struct vm_map) new fields size_none, size_cur_limit and size_max_limit * vm/vm_map.c: (vm_map_setup) initialize new fields (vm_map_enforce_limit) new function (vm_map_copy_limits) new function (vm_map_find_entry) call limit enforcer function (vm_map_enter) likewise (vm_map_copyout) likewise (vm_map_copyout_page_list) likewise (vm_map_fork) copy parent limit to the new map and compute and set size_none of the new map * vm/vm_user.c: (vm_set_size_limit) new function (vm_get_size_limit) likewise --- include/mach/gnumach.defs | 33 kern/task.c | 5 ++ tests/test-vm.c | 85 + vm/vm_kern.c | 26 + vm/vm_map.c | 110 +- vm/vm_map.h | 13 + vm/vm_user.c | 69 7 files changed, 339 insertions(+), 2 deletions(-) diff --git a/include/mach/gnumach.defs b/include/mach/gnumach.defs index f13e866b..a07a1011 100644 --- a/include/mach/gnumach.defs +++ b/include/mach/gnumach.defs @@ -223,3 +223,36 @@ simpleroutine thread_set_name( routine thread_get_name( thread : thread_t; out name : kernel_debug_name_t); + +/* + * Set a task virtual memory limit parameters + * + * HOST_PORT must be the privileged host control port + * if the caller desires to increase the current max limit. + * + * On the other hand, if the max limit is being decreased, the + * unprivileged host control port (as returned by mach_host_self()) + * can be provided. + * + * Returns: + * - KERN_SUCCESS + * - KERN_INVALID_TASK + * - KERN_INVALID_HOST + * - KERN_INVALID_ARGUMENT + * * when current_limit > max_limit + * * attempt to increase max limit without providing + * the privileged host control port. + */ +routine vm_set_size_limit( + host_port : mach_port_t; + map : vm_task_t; + current_limit : vm_size_t; + max_limit : vm_size_t); + +/* + * Get a task virtual memory limit parameters + */ +routine vm_get_size_limit( + map : vm_task_t; + out current_limit : vm_size_t; + out max_limit : vm_size_t); diff --git a/kern/task.c b/kern/task.c index bd57ca2a..e78e856f 100644 --- a/kern/task.c +++ b/kern/task.c @@ -126,6 +126,11 @@ task_create_kernel( trunc_page(VM_MAX_USER_ADDRESS)); if (new_task->map == VM_MAP_NULL) pmap_destroy(new_pmap); + else if (parent_task != TASK_NULL) { + vm_map_lock_read(parent_task->map); + vm_map_copy_limits(new_task->map, parent_task->map); + vm_map_unlock_read(parent_task->map); + } } } if (new_task->map == VM_MAP_NULL) { diff --git a/tests/test-vm.c b/tests/test-vm.c index 4ece792e..8e4ad884 100644 --- a/tests/test-vm.c +++ b/tests/test-vm.c @@ -75,11 +75,96 @@ static void test_wire() // TODO check that all memory is actually wired or unwired } +void test_vm_limit() +{ + kern_return_t err; + vm_address_t mem, mem2, mem3; + const size_t M_128K = 128l * 1024l; + const size_t M_128M = 128l * 1024l * 1024l; + const size_t M_512M = 512l * 1024l * 1024l; + vm_size_t cur; + vm_size_t max; + + /* set VM memory limitations */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M); + ASSERT_RET(err, "cannot set VM limits"); + + /* check limits are actually saved */ + err = vm_get_size_limit(mach_task_self(), &cur, &max); + ASSERT_RET(err, "getting the VM limit failed"); + ASSERT(cur == M_128M, "cur limit was not expected"); + ASSERT(max == M_512M, "max limit was not expected"); + + /* check we can no longer increase the max limit */ + err = vm_set_size_limit(mach_host_self(), mach_task_self(), M_128M, M_512M * 2); + ASSERT(err == KERN_INVALID_ARGUMENT, "raising VM max limit shall fail with KERN_INVALID_ARGUMENT"); + + /* alloc some memory below the limit */ + err = vm_allocate(mach_task_self(), &mem, M_128K, TRUE); + ASSERT_RET(err, "allocating memory below the limit must succeed"); + err = vm_deallocate(mach_task_self(), mem, M_128K); + ASSERT_RET(err, "deallocation failed"); + + /* alloc a bigger chunk to make it hit the limit */ + err = vm_allocate(mach_task_self(), &mem, (M_128M
[PATCH hurd v2] libpager: raise RLIMIT_AS if permissions allow us
From: Diego Nieto Cid * libpager/demuxer.c(pager_start_workers): set current and max RLIMIT_AS to RLIM_INFINITY when the current user has access to the privileged host port. --- libpager/demuxer.c | 8 1 file changed, 8 insertions(+) diff --git a/libpager/demuxer.c b/libpager/demuxer.c index 4c406602..4e3f5d1e 100644 --- a/libpager/demuxer.c +++ b/libpager/demuxer.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include "priv.h" #include "memory_object_S.h" @@ -316,9 +319,14 @@ pager_start_workers (struct port_bucket *pager_bucket, int i; pthread_t t; struct pager_requests *requests; + struct rlimit limits = { RLIM_INFINITY, RLIM_INFINITY }; assert_backtrace (out_requests != NULL); + /* Lift default address space limits if we are allowed */ + if (setrlimit (RLIMIT_AS, &limits) == -1 && errno != EPERM) +perror ("error lifting address space limits"); + requests = malloc (sizeof *requests); if (requests == NULL) { -- 2.47.1