diff options
-rw-r--r-- | lib/libc/src/stdio/fopen.c | 2 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/hpet.c | 1 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/lapic.c | 1 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/machdep.c | 108 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/proc_machdep.c | 2 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/tsc.c | 109 | ||||
-rw-r--r-- | sys/arch/amd64/conf/GENERIC | 2 | ||||
-rw-r--r-- | sys/dev/acpi/uacpi.c | 13 | ||||
-rw-r--r-- | sys/include/arch/amd64/asm.h | 10 | ||||
-rw-r--r-- | sys/include/arch/amd64/cpu.h | 12 | ||||
-rw-r--r-- | sys/include/arch/amd64/tsc.h | 55 | ||||
-rw-r--r-- | sys/include/dev/timer.h | 5 | ||||
-rw-r--r-- | sys/include/sys/sched.h | 2 | ||||
-rw-r--r-- | sys/include/sys/sysctl.h | 5 | ||||
-rw-r--r-- | sys/include/sys/vmstat.h | 48 | ||||
-rw-r--r-- | sys/include/sys/workqueue.h | 101 | ||||
-rw-r--r-- | sys/include/vm/physmem.h | 4 | ||||
-rw-r--r-- | sys/include/vm/stat.h | 39 | ||||
-rw-r--r-- | sys/kern/init_main.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_proc.c | 32 | ||||
-rw-r--r-- | sys/kern/kern_spawn.c | 36 | ||||
-rw-r--r-- | sys/kern/kern_sysctl.c | 6 | ||||
-rw-r--r-- | sys/kern/kern_work.c | 274 | ||||
-rw-r--r-- | sys/vm/vm_physmem.c | 39 | ||||
-rw-r--r-- | sys/vm/vm_stat.c | 95 | ||||
-rw-r--r-- | usr.bin/kstat/kstat.c | 52 | ||||
-rw-r--r-- | usr.bin/sysctl/sysctl.c | 39 |
27 files changed, 1048 insertions, 48 deletions
diff --git a/lib/libc/src/stdio/fopen.c b/lib/libc/src/stdio/fopen.c index c15dace..efeb577 100644 --- a/lib/libc/src/stdio/fopen.c +++ b/lib/libc/src/stdio/fopen.c @@ -51,6 +51,8 @@ fopen(const char *__restrict path, const char *__restrict mode) seal |= (O_WRONLY | O_CREAT); } else if (strcmp(mode, "r+") == 0) { seal |= O_RDWR; + } else if (strcmp(mode, "rb") == 0) { + seal |= O_RDONLY; } else { return NULL; } diff --git a/sys/arch/amd64/amd64/hpet.c b/sys/arch/amd64/amd64/hpet.c index 3b0ca46..9191bee 100644 --- a/sys/arch/amd64/amd64/hpet.c +++ b/sys/arch/amd64/amd64/hpet.c @@ -197,6 +197,7 @@ hpet_init(void) timer.get_time_usec = hpet_time_usec; timer.get_time_nsec = hpet_time_nsec; timer.get_time_sec = hpet_time_sec; + timer.flags = TIMER_MONOTONIC; register_timer(TIMER_GP, &timer); return 0; } diff --git a/sys/arch/amd64/amd64/lapic.c b/sys/arch/amd64/amd64/lapic.c index 022592c..ceb5428 100644 --- a/sys/arch/amd64/amd64/lapic.c +++ b/sys/arch/amd64/amd64/lapic.c @@ -364,5 +364,6 @@ lapic_init(void) lapic_timer.name = "LAPIC_INTEGRATED_TIMER"; lapic_timer.stop = lapic_timer_stop; lapic_timer.oneshot_us = lapic_timer_oneshot_us; + lapic_timer.flags = 0; register_timer(TIMER_SCHED, &lapic_timer); } diff --git a/sys/arch/amd64/amd64/machdep.c b/sys/arch/amd64/amd64/machdep.c index 3f8580a..3338caa 100644 --- a/sys/arch/amd64/amd64/machdep.c +++ b/sys/arch/amd64/amd64/machdep.c @@ -74,6 +74,12 @@ #define CPU_SMEP 0 #endif +#if defined(__CPU_UMIP) +#define CPU_UMIP __CPU_UMIP +#else +#define CPU_UMIP 0 +#endif + int ibrs_enable(void); int simd_init(void); void syscall_isr(void); @@ -238,17 +244,90 @@ init_ipis(void) } static void +cpu_get_vendor(struct cpu_info *ci) +{ + uint32_t unused, ebx, ecx, edx; + char vendor_str[13]; + + /* + * This CPUID returns a 12 byte CPU vendor string + * that we'll put together and use to detect the vendor. + */ + CPUID(0, unused, ebx, ecx, edx); + + /* Dword 0 */ + vendor_str[0] = ebx & 0xFF; + vendor_str[1] = (ebx >> 8) & 0xFF; + vendor_str[2] = (ebx >> 16) & 0xFF; + vendor_str[3] = (ebx >> 24) & 0xFF; + + /* Dword 1 */ + vendor_str[4] = edx & 0xFF; + vendor_str[5] = (edx >> 8) & 0xFF; + vendor_str[6] = (edx >> 16) & 0xFF; + vendor_str[7] = (edx >> 24) & 0xFF; + + /* Dword 2 */ + vendor_str[8] = ecx & 0xFF; + vendor_str[9] = (ecx >> 8) & 0xFF; + vendor_str[10] = (ecx >> 16) & 0xFF; + vendor_str[11] = (ecx >> 24) & 0xFF; + vendor_str[12] = '\0'; + + /* Is this an AMD CPU? */ + if (strcmp(vendor_str, "AuthenticAMD") == 0) { + ci->vendor = CPU_VENDOR_AMD; + return; + } + + /* Is this an Intel CPU? */ + if (strcmp(vendor_str, "GenuineIntel") == 0) { + ci->vendor = CPU_VENDOR_INTEL; + return; + } + + /* + * Some buggy Intel CPUs report the string "GenuineIotel" + * instead of "GenuineIntel". This is rare but we should + * still handle it as it can happen. Probably a good idea + * to log it so the user can know about their rare CPU + * quirk and brag to their friends :~) + */ + if (strcmp(vendor_str, "GenuineIotel") == 0) { + pr_trace_bsp("vendor_str=%s\n", vendor_str); + pr_trace_bsp("detected vendor string quirk\n"); + ci->vendor = CPU_VENDOR_INTEL; + return; + } + + ci->vendor = CPU_VENDOR_OTHER; +} + +static void cpu_get_info(struct cpu_info *ci) { - uint32_t eax, ebx, unused; + uint32_t unused, eax, ebx, ecx, edx; uint8_t ext_model, ext_family; + /* Get the vendor information */ + cpu_get_vendor(ci); + /* Extended features */ - CPUID(0x07, unused, ebx, unused, unused); + CPUID(0x07, unused, ebx, ecx, unused); if (ISSET(ebx, BIT(7))) ci->feat |= CPU_FEAT_SMEP; if (ISSET(ebx, BIT(20))) ci->feat |= CPU_FEAT_SMAP; + if (ISSET(ecx, BIT(2))) + ci->feat |= CPU_FEAT_UMIP; + + /* + * Processor power management information bits as well + * as bits describing RAS capabilities + */ + CPUID(0x80000007, unused, unused, unused, edx); + if (ISSET(edx, BIT(8))) + ci->feat |= CPU_FEAT_TSCINV; /* * Processor info and feature bits @@ -278,6 +357,30 @@ cpu_get_info(struct cpu_info *ci) } } +/* + * The CR4.UMIP bit prevents user programs from + * executing instructions related to accessing + * system memory structures. This should be enabled + * by default if supported. + */ +static void +cpu_enable_umip(void) +{ + struct cpu_info *ci = this_cpu(); + uint64_t cr4; + + if (!CPU_UMIP) { + pr_trace_bsp("UMIP not configured\n"); + return; + } + + if (ISSET(ci->feat, CPU_FEAT_UMIP)) { + cr4 = amd64_read_cr4(); + cr4 |= CR4_UMIP; + amd64_write_cr4(cr4); + } +} + void cpu_shootdown_tlb(vaddr_t va) { @@ -509,6 +612,7 @@ cpu_startup(struct cpu_info *ci) cpu_get_info(ci); cpu_enable_smep(); + cpu_enable_umip(); enable_simd(); lapic_init(); diff --git a/sys/arch/amd64/amd64/proc_machdep.c b/sys/arch/amd64/amd64/proc_machdep.c index ad807fe..a1d6563 100644 --- a/sys/arch/amd64/amd64/proc_machdep.c +++ b/sys/arch/amd64/amd64/proc_machdep.c @@ -256,7 +256,7 @@ sched_switch_to(struct trapframe *tf, struct proc *td) /* Update stats */ cpustat = &ci->stat; - cpustat->nswitch++; + atomic_inc_64(&cpustat->nswitch); ci->curtd = td; pcbp = &td->pcb; diff --git a/sys/arch/amd64/amd64/tsc.c b/sys/arch/amd64/amd64/tsc.c new file mode 100644 index 0000000..2111cd0 --- /dev/null +++ b/sys/arch/amd64/amd64/tsc.c @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/cdefs.h> +#include <sys/driver.h> +#include <sys/syslog.h> +#include <machine/tsc.h> +#include <machine/asm.h> +#include <machine/cpuid.h> + +/* See kconf(9) */ +#if defined(__USER_TSC) +#define USER_TSC __USER_TSC +#else +#define USER_TSC 0 +#endif /* __USER_TSC */ + +#define pr_trace(fmt, ...) kprintf("tsc: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +static uint64_t tsc_i = 0; + +uint64_t +rdtsc_rel(void) +{ + return rdtsc() - tsc_i; +} + +/* + * Check if the TSC and RDTSC instruction is + * supported on the current CPU. + * + * Returns zero if supported, otherwise a less + * than zero value is returned. + */ +static int +tsc_check(void) +{ + uint32_t edx, unused; + + CPUID(1, unused, unused, unused, edx); + if (ISSET(edx, BIT(4))) { + return 0; + } + + return -ENOTSUP; +} + +static int +tsc_init(void) +{ + uint64_t cr4; + int error; + + /* Is the TSC even supported? */ + if ((error = tsc_check()) != 0) { + pr_error("TSC not supported by machine\n"); + return error; + } + + cr4 = amd64_read_cr4(); + tsc_i = rdtsc(); + pr_trace("initial count @ %d\n", rdtsc_rel()); + + /* + * If we USER_TSC is configured to "yes" then + * we'll need to enable the 'rdtsc' instruction + * in user mode. + */ + if (!USER_TSC) { + cr4 &= ~CR4_TSD; + } else { + cr4 |= CR4_TSD; + } + + amd64_write_cr4(cr4); + return 0; +} + +DRIVER_EXPORT(tsc_init, "x86-tsc"); diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index 6f573f3..6bf3af5 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -7,6 +7,8 @@ // option SPECTRE_IBRS no // Enable the IBRS CPU feature option SERIAL_DEBUG yes // Enable kmsg serial logging +option CPU_UMIP yes // Enable User-mode Instruction Prevention option USER_KMSG no // Show kmsg in user consoles +option USER_TSC no // Enable 'rdtsc' in user mode option CPU_SMEP yes // Supervisor Memory Exec Protection option I8042_POLL yes // Use polling for the i8042 diff --git a/sys/dev/acpi/uacpi.c b/sys/dev/acpi/uacpi.c index ffec436..6c2bf50 100644 --- a/sys/dev/acpi/uacpi.c +++ b/sys/dev/acpi/uacpi.c @@ -55,6 +55,9 @@ #include <vm/vm.h> #include <string.h> +#define pr_trace(fmt, ...) kprintf("acpi: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + typedef struct { uacpi_io_addr base; uacpi_size length; @@ -633,25 +636,25 @@ uacpi_init(void) ret = uacpi_initialize(0); if (uacpi_unlikely_error(ret)) { - kprintf("uacpi init error: %s\n", uacpi_status_to_string(ret)); + pr_error("uacpi init error: %s\n", uacpi_status_to_string(ret)); return -1; } ret = uacpi_namespace_load(); if (uacpi_unlikely_error(ret)) { - kprintf("uacpi namespace load error: %s\n", uacpi_status_to_string(ret)); + pr_error("uacpi namespace load error: %s\n", uacpi_status_to_string(ret)); return -1; } ret = uacpi_namespace_initialize(); if (uacpi_unlikely_error(ret)) { - kprintf("uacpi namespace init error: %s\n", uacpi_status_to_string(ret)); + pr_error("uacpi namespace init error: %s\n", uacpi_status_to_string(ret)); return -1; } ret = uacpi_finalize_gpe_initialization(); if (uacpi_unlikely_error(ret)) { - kprintf("uacpi GPE init error: %s\n", uacpi_status_to_string(ret)); + pr_error("uacpi GPE init error: %s\n", uacpi_status_to_string(ret)); return -1; } @@ -661,7 +664,7 @@ uacpi_init(void) ); if (uacpi_unlikely_error(ret)) { - kprintf("failed to install power button event: %s\n", + pr_error("failed to install power button event: %s\n", uacpi_status_to_string(ret) ); return -1; diff --git a/sys/include/arch/amd64/asm.h b/sys/include/arch/amd64/asm.h index 8d2c812..aca49d2 100644 --- a/sys/include/arch/amd64/asm.h +++ b/sys/include/arch/amd64/asm.h @@ -34,6 +34,16 @@ #include <sys/param.h> #include <machine/msr.h> +/* CR4 bits */ +#define CR4_TSD BIT(2) /* Timestamp disable */ +#define CR4_DE BIT(3) /* Debugging extensions */ +#define CR4_PSE BIT(4) /* Page size extensions */ +#define CR4_PCE BIT(8) /* Performance monitoring counter enable */ +#define CR4_UMIP BIT(11) /* User mode instruction prevention */ +#define CR4_LA57 BIT(12) /* Level 5 paging enable */ +#define CR4_VMXE BIT(13) /* Virtual machine extensions enable */ +#define CR4_SMXE BIT(14) /* Safer mode extensions enable */ + /* * Contains information for the current * core. Stored in %GS. diff --git a/sys/include/arch/amd64/cpu.h b/sys/include/arch/amd64/cpu.h index 4586163..6ed675e 100644 --- a/sys/include/arch/amd64/cpu.h +++ b/sys/include/arch/amd64/cpu.h @@ -42,14 +42,22 @@ #define CPU_IRQ(IRQ_N) (BIT((IRQ_N)) & 0xFF) /* Feature bits */ -#define CPU_FEAT_SMAP BIT(0) -#define CPU_FEAT_SMEP BIT(1) +#define CPU_FEAT_SMAP BIT(0) +#define CPU_FEAT_SMEP BIT(1) +#define CPU_FEAT_UMIP BIT(2) +#define CPU_FEAT_TSCINV BIT(3) /* TSC invariant */ + +/* CPU vendors */ +#define CPU_VENDOR_OTHER 0x00000000 +#define CPU_VENDOR_INTEL 0x00000001 +#define CPU_VENDOR_AMD 0x00000002 typedef uint16_t ipi_pend_t; struct cpu_info { uint32_t apicid; uint32_t feat; + uint32_t vendor; /* Vendor (see CPU_VENDOR_*) */ uint8_t ipi_dispatch : 1; /* 1: IPIs being dispatched */ uint8_t ipi_id; ipi_pend_t ipi_pending[N_IPIVEC]; diff --git a/sys/include/arch/amd64/tsc.h b/sys/include/arch/amd64/tsc.h new file mode 100644 index 0000000..d9eed4f --- /dev/null +++ b/sys/include/arch/amd64/tsc.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MACHINE_TSC_H_ +#define _MACHINE_TSC_H_ + +#include <sys/types.h> +#include <sys/cdefs.h> +#include <sys/param.h> + +uint64_t rdtsc_rel(void); + +__always_inline static inline uint64_t +rdtsc(void) +{ + uint32_t lo, hi; + + __ASMV( + "rdtsc" + : "=d" (hi), + "=a" (lo) + : + : "memory" + ); + + return COMBINE32(hi, lo); +} + +#endif /* !_MACHINE_TSC_H_ */ diff --git a/sys/include/dev/timer.h b/sys/include/dev/timer.h index fe91323..2ca6d62 100644 --- a/sys/include/dev/timer.h +++ b/sys/include/dev/timer.h @@ -31,11 +31,15 @@ #define _DEV_TIMER_H_ #include <sys/types.h> +#include <sys/param.h> /* Timer IDs */ #define TIMER_SCHED 0x00000000U /* Scheduler reserved timer */ #define TIMER_GP 0x00000001U /* General purpose timer */ +/* Timer flags */ +#define TIMER_MONOTONIC BIT(0) + /* Number of timer IDs, adjust when adding timer IDs */ #define TIMER_ID_COUNT 2 @@ -79,6 +83,7 @@ struct timer { void(*oneshot_ms)(size_t ms); void(*oneshot_us)(size_t ms); void(*stop)(void); + uint8_t flags; }; tmrr_status_t register_timer(timer_id_t id, const struct timer *tmr); diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h index 19ceb7e..8b0ba02 100644 --- a/sys/include/sys/sched.h +++ b/sys/include/sys/sched.h @@ -42,7 +42,7 @@ * @idle: Number of milliseconds idle */ struct sched_cpu { - uint32_t nswitch; + uint64_t nswitch; }; /* diff --git a/sys/include/sys/sysctl.h b/sys/include/sys/sysctl.h index 3b8d3c7..ce7510d 100644 --- a/sys/include/sys/sysctl.h +++ b/sys/include/sys/sysctl.h @@ -56,6 +56,11 @@ #define HW_MACHINE 7 /* + * List of 'proc.*' identifiers + */ +#define PROC_COUNT 8 + +/* * Option types (i.e., int, string, etc) for * sysctl entries. * diff --git a/sys/include/sys/vmstat.h b/sys/include/sys/vmstat.h new file mode 100644 index 0000000..b7faeb2 --- /dev/null +++ b/sys/include/sys/vmstat.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_VMSTAT_H_ +#define _SYS_VMSTAT_H_ + +#include <sys/types.h> + +/* + * Virtual memory statistics + * + * @mem_avail: Available memory in MiB + * @mem_used: Allocated memory in MiB + * @mem_total: Total system memory in MiB + */ +struct vm_stat { + uint32_t mem_avail; + uint32_t mem_used; + size_t mem_total; +}; + +#endif /* !_VM_STAT_H_ */ diff --git a/sys/include/sys/workqueue.h b/sys/include/sys/workqueue.h new file mode 100644 index 0000000..9925f79 --- /dev/null +++ b/sys/include/sys/workqueue.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_WORKQUEUE_H_ +#define _SYS_WORKQUEUE_H_ + +#if defined(_KERNEL) + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/mutex.h> +#include <sys/proc.h> + +struct workqueue; +struct work; + +/* + * A work function can either refer to a work thread + * entry (or actual work to be done + */ +typedef void(*workfunc_t)(struct workqueue *wqp, struct work *wp); + +/* + * Represents work that may be added to a + * workqueue. + * + * @name: Name of this work/task [i] + * @data: Optional data to be passed with work [p] + * @func: Function with work to be done [p] + * @cookie: Used for validating the work structure [i] + * + * Field attributes: + * - [i]: Used internally + * - [p]: Used as parameter + */ +struct work { + char *name; + void *data; + workfunc_t func; + TAILQ_ENTRY(work) link; +}; + +/* + * A workqueue contains tasks that are + * queued up to be completed in their own + * thread context. + * + * @name: Name of workqueue. + * @work: Start of the workqueue + * @ipl: IPL that work here must run with + * @max_work: Max number of jobs that can be queued + * @nwork: Number of tasks to be done + * @cookie: For validating workqueues + * @worktd: Thread associated with the workqueue + * @lock: Protects the workqueue + */ +struct workqueue { + char *name; + TAILQ_HEAD(, work) work; + uint8_t ipl; + size_t max_work; + ssize_t nwork; + uint16_t cookie; + struct proc *worktd; + struct mutex *lock; +}; + +struct workqueue *workqueue_new(const char *name, size_t max_work, int ipl); + +int workqueue_enq(struct workqueue *wqp, const char *name, struct work *wp); +int workqueue_destroy(struct workqueue *wqp); +int work_destroy(struct work *wp); + +#endif /* !_KERNEL */ +#endif /* !_SYS_WORKQUEUE_H_ */ diff --git a/sys/include/vm/physmem.h b/sys/include/vm/physmem.h index ae11530..3f1da61 100644 --- a/sys/include/vm/physmem.h +++ b/sys/include/vm/physmem.h @@ -32,6 +32,10 @@ #include <sys/types.h> +uint32_t vm_mem_used(void); +uint32_t vm_mem_free(void); +size_t vm_mem_total(void); + void vm_physmem_init(void); uintptr_t vm_alloc_frame(size_t count); void vm_free_frame(uintptr_t base, size_t count); diff --git a/sys/include/vm/stat.h b/sys/include/vm/stat.h new file mode 100644 index 0000000..7e9a4a9 --- /dev/null +++ b/sys/include/vm/stat.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VM_STAT_H_ +#define _VM_STAT_H_ + +#include <sys/types.h> +#include <sys/vmstat.h> + +int vm_stat_get(struct vm_stat *vmstat); +void vm_stat_init(void); + +#endif /* !_VM_STAT_H_ */ diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 541355a..577b7ec 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -43,6 +43,7 @@ #include <machine/cpu.h> #include <machine/cdefs.h> #include <vm/vm.h> +#include <vm/stat.h> #include <string.h> #define _START_PATH "/usr/sbin/init" @@ -104,6 +105,9 @@ main(void) /* Init the virtual file system */ vfs_init(); + /* Init vmstats */ + vm_stat_init(); + /* Expose the console to devfs */ cons_expose(); diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index 87dcc74..8bc5680 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -29,14 +29,18 @@ #include <sys/types.h> #include <sys/proc.h> +#include <sys/errno.h> #include <sys/cdefs.h> #include <sys/vnode.h> +#include <sys/tree.h> #include <sys/syscall.h> #include <sys/filedesc.h> #include <sys/fcntl.h> #include <string.h> #include <crc32.h> +extern volatile size_t g_nthreads; + pid_t getpid(void) { @@ -50,7 +54,6 @@ getpid(void) return td->pid; } - pid_t getppid(void) { @@ -100,6 +103,33 @@ proc_coredump(struct proc *td, uintptr_t fault_addr) fd_close(fd); } +int +proc_init(struct proc *td, struct proc *parent) +{ + struct mmap_lgdr *mlgdr; + + mlgdr = dynalloc(sizeof(*mlgdr)); + if (mlgdr == NULL) { + return -ENOMEM; + } + + /* Add to parent leafq */ + TAILQ_INSERT_TAIL(&parent->leafq, td, leaf_link); + atomic_inc_int(&parent->nleaves); + atomic_inc_64(&g_nthreads); + td->parent = parent; + td->exit_status = -1; + td->cred = parent->cred; + + /* Initialize the mmap ledger */ + mlgdr->nbytes = 0; + RBT_INIT(lgdr_entries, &mlgdr->hd); + td->mlgdr = mlgdr; + td->flags |= PROC_WAITED; + signals_init(td); + return 0; +} + scret_t sys_getpid(struct syscall_args *scargs) { diff --git a/sys/kern/kern_spawn.c b/sys/kern/kern_spawn.c index b9551f3..7962ced 100644 --- a/sys/kern/kern_spawn.c +++ b/sys/kern/kern_spawn.c @@ -34,10 +34,8 @@ #include <sys/mman.h> #include <sys/systm.h> #include <sys/errno.h> -#include <sys/atomic.h> #include <sys/syslog.h> #include <sys/syscall.h> -#include <sys/atomic.h> #include <sys/signal.h> #include <sys/limits.h> #include <sys/sched.h> @@ -50,7 +48,6 @@ #define ARGVP_MAX (ARG_MAX / sizeof(void *)) static size_t next_pid = 1; -extern volatile size_t g_nthreads; /* * TODO: envp @@ -145,7 +142,6 @@ pid_t spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **newprocp) { struct proc *newproc; - struct mmap_lgdr *mlgdr; int error; pid_t pid; @@ -156,19 +152,10 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new return -ENOMEM; } - mlgdr = dynalloc(sizeof(*mlgdr)); - if (mlgdr == NULL) { - dynfree(newproc); - try_free_data(p); - pr_error("could not alloc proc mlgdr (-ENOMEM)\n"); - return -ENOMEM; - } - memset(newproc, 0, sizeof(*newproc)); error = md_spawn(newproc, cur, (uintptr_t)func); if (error < 0) { dynfree(newproc); - dynfree(mlgdr); try_free_data(p); pr_error("error initializing proc\n"); return error; @@ -184,23 +171,16 @@ spawn(struct proc *cur, void(*func)(void), void *p, int flags, struct proc **new cur->flags |= PROC_LEAFQ; } - /* Add to parent leafq */ - TAILQ_INSERT_TAIL(&cur->leafq, newproc, leaf_link); - atomic_inc_int(&cur->nleaves); - newproc->parent = cur; - newproc->data = p; - newproc->exit_status = -1; - newproc->cred = cur->cred; - - /* Initialize the mmap ledger */ - mlgdr->nbytes = 0; - RBT_INIT(lgdr_entries, &mlgdr->hd); - newproc->mlgdr = mlgdr; - newproc->flags |= PROC_WAITED; + error = proc_init(newproc, cur); + if (error < 0) { + dynfree(newproc); + try_free_data(p); + pr_error("error initializing proc\n"); + return error; + } - atomic_inc_64(&g_nthreads); + newproc->data = p; newproc->pid = next_pid++; - signals_init(newproc); sched_enqueue_td(newproc); pid = newproc->pid; return pid; diff --git a/sys/kern/kern_sysctl.c b/sys/kern/kern_sysctl.c index a4c16bb..1f5e578 100644 --- a/sys/kern/kern_sysctl.c +++ b/sys/kern/kern_sysctl.c @@ -40,6 +40,7 @@ HYRA_VERSION " " \ HYRA_BUILDDATE +extern size_t g_nthreads; static uint32_t pagesize = DEFAULT_PAGESIZE; static char machine[] = HYRA_ARCH; static char hyra[] = "Hyra"; @@ -62,7 +63,10 @@ static struct sysctl_entry common_optab[] = { /* 'hw.*' */ [HW_PAGESIZE] = { HW_PAGESIZE, SYSCTL_OPTYPE_INT_RO, &pagesize }, [HW_NCPU] = { HW_NCPU, SYSCTL_OPTYPE_INT, NULL }, - [HW_MACHINE] = {HW_MACHINE, SYSCTL_OPTYPE_STR_RO, &machine } + [HW_MACHINE] = {HW_MACHINE, SYSCTL_OPTYPE_STR_RO, &machine }, + + /* 'proc.*' */ + [PROC_COUNT] = { PROC_COUNT, SYSCTL_OPTYPE_INT_RO, &g_nthreads } }; static int diff --git a/sys/kern/kern_work.c b/sys/kern/kern_work.c new file mode 100644 index 0000000..918af89 --- /dev/null +++ b/sys/kern/kern_work.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/panic.h> +#include <sys/proc.h> +#include <sys/sched.h> +#include <sys/syslog.h> +#include <sys/workqueue.h> +#include <vm/dynalloc.h> +#include <string.h> + +#define pr_trace(fmt, ...) kprintf("workq: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +extern struct proc g_proc0; + +/* + * The workqueue cookie value that is used for + * verifying if a workqueue object is properly + * set up or not. + */ +#define WQ_COOKIE 0xFC0B + +/* + * A worker services work in the queue + * and there is one per workqueue. + */ +static void +workqueue_worker(void) +{ + struct proc *td; + struct workqueue *wqp; + struct work *wp; + + td = this_td(); + if ((wqp = td->data) == NULL) { + panic("no workqueue in thread\n"); + } + + /* + * Weird things can happen, just be careful + * here... + */ + if (wqp->cookie != WQ_COOKIE) { + panic("bad WQ_COOKIE in worker\n"); + } + + for (;;) { + mutex_acquire(wqp->lock, 0); + wp = TAILQ_FIRST(&wqp->work); + + /* Try again later if empty */ + if (wp == NULL) { + mutex_release(wqp->lock); + sched_yield(); + continue; + } + + wp->func(wqp, wp); + TAILQ_REMOVE(&wqp->work, wp, link); + + /* + * Decrement the amount of work that is + * left to get done. Check for underflows + * which should not happen unless something + * clobbers the fields. + */ + if ((--wqp->nwork) < 0) { + panic("wqp nwork underflow\n"); + } + + mutex_release(wqp->lock); + sched_yield(); + } +} + +/* + * Allocates a new work queue that may be used + * to hold queued up tasks. + * + * @name: Name to give the workqueue + * @max_work: Maximum number of jobs to be added + * @ipl: IPL that the work must operate in + * + * Returns a pointer to the new workqueue on success, + * otherwise a value of NULL is returned. + */ +struct workqueue * +workqueue_new(const char *name, size_t max_work, int ipl) +{ + struct workqueue *wqp; + struct proc *td; + + td = this_td(); + if (__unlikely(td == NULL)) { + pr_error("no thread in workqueue_new()\n"); + return NULL; + } + + wqp = dynalloc(sizeof(*wqp)); + if (wqp == NULL) { + return NULL; + } + + wqp->name = strdup(name); + TAILQ_INIT(&wqp->work); + wqp->ipl = ipl; + wqp->max_work = max_work; + wqp->nwork = 0; + wqp->cookie = WQ_COOKIE; + wqp->lock = mutex_new(wqp->name); + + /* + * We need to spawn the work thread which + * is behind the management of this specific + * workqueue. It typically does something like + * dequeuing at the head of the workqueue, performing + * the work, cleaning up as needed and dequeuing the + * next and waiting if there are none yet. + */ + spawn( + &g_proc0, workqueue_worker, + wqp, 0, + &wqp->worktd + ); + + return wqp; +} + +/* + * Enqueue a work item onto a specific + * workqueue. + * + * @wqp: Pointer to specific workqueue + * @name: Name to set for work unit + * @wp: Pointer to work that should be enqueued + * + * Returns zero on success, otherwise a less than + * zero value is returned. + */ +int +workqueue_enq(struct workqueue *wqp, const char *name, struct work *wp) +{ + if (wqp == NULL || wp == NULL) { + return -EINVAL; + } + + if (name == NULL) { + return -EINVAL; + } + + /* Verify that we have a valid workqueue */ + if (__unlikely(wqp->cookie != WQ_COOKIE)) { + panic("workq: bad cookie on work enqueue\n"); + } + + wp->name = strdup(name); + mutex_acquire(wqp->lock, 0); + + /* + * If we have reached the max amount of jobs + * that we can enqueue here, just log it and + * bail. + */ + if (wqp->nwork >= wqp->max_work) { + pr_error("max jobs reached for '%s'\n", wqp->name); + mutex_release(wqp->lock); + return -EAGAIN; + } + + TAILQ_INSERT_TAIL(&wqp->work, wp, link); + ++wqp->nwork; + mutex_release(wqp->lock); + return 0; +} + +/* + * Destroy a workqueue and free resources + * associated with it. + * + * @wqp: Pointer to workqueue to destroy + * + * Returns zero on success, otherwise a less + * than zero value is returned. + */ +int +workqueue_destroy(struct workqueue *wqp) +{ + if (wqp == NULL) { + return -EINVAL; + } + + /* Should not happen but just make sure */ + if (__unlikely(wqp->cookie != WQ_COOKIE)) { + panic("workq: bad cookie on destroy\n"); + } + + /* Free the name if we have it */ + if (wqp->name != NULL) { + dynfree(wqp->name); + } + + if (wqp->lock != NULL) { + mutex_free(wqp->lock); + } + + /* Brutally murder any workthreads */ + if (wqp->worktd != NULL) { + exit1(wqp->worktd, 0); + wqp->worktd = NULL; + } + + /* + * Zero before we free for security reasons, we + * don't really know what will be queued up but + * for certain things, it is best if we make it + * as if it never existed in the first place. + * + * XXX: There is no need to free the workqueue here as + * we had to pass it to spawn() to run the worker. + * + * During an exit, spawn() will free the thread data + * meaning this is already cleaned up. + */ + memset(wqp, 0, sizeof(*wqp)); + return 0; +} + +/* + * Cleanup after work + * + * @wp: Work to clean up + */ +int +work_destroy(struct work *wp) +{ + if (wp == NULL) { + return -EINVAL; + } + + if (wp->name != NULL) { + dynfree(wp->name); + } + + return 0; +} diff --git a/sys/vm/vm_physmem.c b/sys/vm/vm_physmem.c index 89f9ee6..b6e7347 100644 --- a/sys/vm/vm_physmem.c +++ b/sys/vm/vm_physmem.c @@ -36,6 +36,11 @@ #include <vm/vm.h> #include <string.h> +#define BYTES_PER_MIB 8388608 + +static size_t pages_free = 0; +static size_t pages_used = 0; +static size_t pages_total = 0; static size_t highest_frame_idx = 0; static size_t bitmap_size = 0; static size_t bitmap_free_start = 0; @@ -60,9 +65,11 @@ physmem_populate_bitmap(void) for (size_t i = 0; i < resp->entry_count; ++i) { ent = resp->entries[i]; + pages_total += ent->length / DEFAULT_PAGESIZE; if (ent->type != LIMINE_MEMMAP_USABLE) { /* This memory is not usable */ + pages_used += ent->length / DEFAULT_PAGESIZE; continue; } @@ -73,6 +80,8 @@ physmem_populate_bitmap(void) for (size_t j = 0; j < ent->length; j += DEFAULT_PAGESIZE) { clrbit(bitmap, (ent->base + j) / DEFAULT_PAGESIZE); } + + pages_free += ent->length / DEFAULT_PAGESIZE; } } @@ -203,6 +212,36 @@ vm_free_frame(uintptr_t base, size_t count) spinlock_release(&lock); } +/* + * Return the amount of memory in MiB that is + * currently allocated. + */ +uint32_t +vm_mem_used(void) +{ + return (pages_used * DEFAULT_PAGESIZE) / BYTES_PER_MIB; +} + +/* + * Return the amount of memory in MiB that is + * currently free. + */ +uint32_t +vm_mem_free(void) +{ + return (pages_free * DEFAULT_PAGESIZE) / BYTES_PER_MIB; +} + +/* + * Return the total amount of memory supported + * by the machine. + */ +size_t +vm_mem_total(void) +{ + return (pages_total * DEFAULT_PAGESIZE) / BYTES_PER_MIB; +} + void vm_physmem_init(void) { diff --git a/sys/vm/vm_stat.c b/sys/vm/vm_stat.c new file mode 100644 index 0000000..3e39047 --- /dev/null +++ b/sys/vm/vm_stat.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/errno.h> +#include <fs/ctlfs.h> +#include <vm/physmem.h> +#include <vm/vm.h> +#include <vm/stat.h> +#include <string.h> + +#include <sys/syslog.h> + +static struct ctlops vm_stat_ctl; + +/* + * ctlfs hook to read the virtual memory + * statistics. + */ +static int +vm_stat_read(struct ctlfs_dev *cdp, struct sio_txn *sio) +{ + struct vm_stat stat; + int error; + + if (sio->len > sizeof(stat)) { + sio->len = sizeof(stat); + } + + error = vm_stat_get(&stat); + if (error < 0) { + return error; + } + + memcpy(sio->buf, &stat, sio->len); + return sio->len; +} + +int +vm_stat_get(struct vm_stat *vmstat) +{ + if (vmstat == NULL) { + return -EINVAL; + } + + vmstat->mem_avail = vm_mem_free(); + vmstat->mem_used = vm_mem_used(); + vmstat->mem_total = vm_mem_total(); + return 0; +} + +void +vm_stat_init(void) +{ + char devname[] = "vm"; + struct ctlfs_dev ctl; + + /* Register a stat control file */ + ctl.mode = 0444; + ctlfs_create_node(devname, &ctl); + ctl.devname = devname; + ctl.ops = &vm_stat_ctl; + ctlfs_create_entry("stat", &ctl); +} + +static struct ctlops vm_stat_ctl = { + .read = vm_stat_read, + .write = NULL +}; diff --git a/usr.bin/kstat/kstat.c b/usr.bin/kstat/kstat.c index e3b7e6a..cbbe602 100644 --- a/usr.bin/kstat/kstat.c +++ b/usr.bin/kstat/kstat.c @@ -28,10 +28,51 @@ */ #include <sys/sched.h> +#include <sys/vmstat.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> +#define MIB_PER_GIB 1024 + +static void +print_size_mib(const char *name, size_t mib) +{ + if (name == NULL) { + return; + } + + if (mib >= MIB_PER_GIB) { + printf("%s: %d GiB\n", name, mib / MIB_PER_GIB); + } else { + printf("%s: %d MiB\n", name, mib); + } +} + +static void +get_vm_stat(void) +{ + struct vm_stat vmstat; + int retval, fd; + + fd = open("/ctl/vm/stat", O_RDONLY); + if (fd < 0) { + printf("failed to open '/ctl/vm/stat'\n"); + return; + } + + retval = read(fd, &vmstat, sizeof(vmstat)); + if (retval <= 0) { + printf("failed to read vmstat\n"); + return; + } + + close(fd); + print_size_mib("memory available", vmstat.mem_avail); + print_size_mib("memory used", vmstat.mem_used); + print_size_mib("memory total", vmstat.mem_total); +} + static void get_sched_stat(void) { @@ -56,12 +97,10 @@ get_sched_stat(void) nonline = (stat.ncpu - noffline); online_percent = (uint16_t)(((double)nonline / (nonline + noffline)) * 100); - printf("-------------------------------\n"); - printf("Number of tasks: %d\n", stat.nproc); - printf("Number of cores online: %d\n", stat.ncpu); - printf("Scheduler quantum: %d usec\n", stat.quantum_usec); + printf("number of tasks: %d\n", stat.nproc); + printf("number of cores online: %d\n", stat.ncpu); + printf("scheduler quantum: %d usec\n", stat.quantum_usec); printf("CPU is %d%% online\n", online_percent); - printf("-------------------------------\n"); /* * Log out some per-cpu information @@ -75,6 +114,9 @@ get_sched_stat(void) int main(void) { + printf("-- scheduler statistics --\n"); get_sched_stat(); + printf("-- memory statistics --\n"); + get_vm_stat(); return 0; } diff --git a/usr.bin/sysctl/sysctl.c b/usr.bin/sysctl/sysctl.c index d4275a7..4a84484 100644 --- a/usr.bin/sysctl/sysctl.c +++ b/usr.bin/sysctl/sysctl.c @@ -46,13 +46,18 @@ #define NAME_NCPU "ncpu" #define NAME_MACHINE "machine" +/* Proc var string constants */ +#define NAME_COUNT "count" + /* Name start string constants */ #define NAME_KERN "kern" #define NAME_HW "hw" +#define NAME_PROC "proc" /* Name start int constants */ #define NAME_DEF_KERN 0 #define NAME_DEF_HW 1 +#define NAME_DEF_PROC 2 /* * Print the contents read from a sysctl @@ -105,6 +110,12 @@ name_to_def(const char *name) } return -1; + case 'p': + if (strcmp(name, NAME_PROC) == 0) { + return NAME_DEF_PROC; + } + + return -1; } return -1; @@ -178,6 +189,28 @@ hw_node(const char *node, bool *is_str) } /* + * Handle parsing of 'proc.*' node names + * + * @node: Node name to parse + * @is_str: Set to true if string + */ +static int +proc_node(const char *node, bool *is_str) +{ + switch (*node) { + case 'c': + if (strcmp(node, NAME_COUNT) == 0) { + *is_str = false; + return PROC_COUNT; + } + + return -1; + } + + return -1; +} + +/* * Convert string node to a sysctl name * definition. * @@ -215,6 +248,8 @@ node_to_def(int name, const char *node, bool *is_str) return kern_node(node, is_str); case NAME_DEF_HW: return hw_node(node, is_str); + case NAME_DEF_PROC: + return proc_node(node, is_str); } return -1; @@ -240,12 +275,12 @@ main(int argc, char **argv) p = strtok(var, "."); if (p == NULL) { - printf("sysctl: bad var\n"); + printf("sysctl: bad var \"%s\"\n", p); return -1; } if ((root = name_to_def(p)) < 0) { - printf("sysctl: bad var \"%s\"", p); + printf("sysctl: bad var \"%s\"\n", p); return root; } |