summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-08-04 19:09:14 -0400
committerIan Moffett <ian@osmora.org>2025-08-04 19:16:17 -0400
commit695c9b7475c88e337e15eed5ee6f8b607b515a77 (patch)
tree3bee7c1efd6f1db66beca57ae9e72960d84dc779 /sys
parentb3f3697c9f2d63fe280a0954294ff9a2c56a6b71 (diff)
kernel: sched: Add sched_suspend()
Introduce a new sched_suspend() function that allows the caller to suspend themselves (equivalent to a yield) for a specified amount of time described by a 'timeval'. Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys')
-rw-r--r--sys/include/sys/sched.h2
-rw-r--r--sys/kern/kern_sched.c86
2 files changed, 88 insertions, 0 deletions
diff --git a/sys/include/sys/sched.h b/sys/include/sys/sched.h
index 7a859b2..19ceb7e 100644
--- a/sys/include/sys/sched.h
+++ b/sys/include/sys/sched.h
@@ -33,6 +33,7 @@
#include <sys/proc.h>
#include <sys/cdefs.h>
#include <sys/limits.h>
+#include <sys/time.h>
/*
* Scheduler CPU information
@@ -66,6 +67,7 @@ void sched_stat(struct sched_stat *statp);
void sched_init(void);
void sched_yield(void);
+void sched_suspend(struct proc *td, const struct timeval *tv);
void sched_detach(struct proc *td);
__dead void sched_enter(void);
diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c
index 23a1ebb..9c5e215 100644
--- a/sys/kern/kern_sched.c
+++ b/sys/kern/kern_sched.c
@@ -315,6 +315,92 @@ proc_unpin(struct proc *td)
td->flags &= ~PROC_PINNED;
}
+/*
+ * Suspend a process for a specified amount
+ * of time. This calling process will yield for
+ * the amount of time specified in 'tv'
+ *
+ * @td: Process to suspend (NULL for current)
+ * @tv: Time value to use
+ *
+ * XXX: 'tv' being NULL is equivalent to calling
+ * sched_detach()
+ */
+void
+sched_suspend(struct proc *td, const struct timeval *tv)
+{
+ struct timer tmr;
+ const time_t USEC_PER_SEC = 1000000;
+ ssize_t usec;
+ time_t usec_cur, usec_tmp;
+ bool have_timer = true;
+ tmrr_status_t tmr_status;
+
+ if (td == NULL)
+ td = this_td();
+ if (__unlikely(td == NULL))
+ return;
+
+ if (tv == NULL) {
+ sched_detach(td);
+ return;
+ }
+
+ /*
+ * Now, we need a generic timer so that we can compute
+ * how much time has elapsed since this process has
+ * requested to be suspended. However, we cannot assume
+ * that it would be present. If the lookup fails, all we
+ * can do is try to estimate how much time went by which
+ * works fine too, just not as accurate.
+ */
+ tmr_status = req_timer(TIMER_GP, &tmr);
+ if (tmr_status != TMRR_SUCCESS) {
+ have_timer = false;
+ }
+
+ /* We need microsecond precision */
+ if (tmr.get_time_sec == NULL) {
+ have_timer = false;
+ }
+
+ /*
+ * Compute the max time in microseconds that
+ * we will wait. We are using both tv->tv_sec
+ * and tv->tv_usec
+ */
+ usec = tv->tv_usec;
+ usec += tv->tv_sec * USEC_PER_SEC;
+ usec_cur = (have_timer) ? tmr.get_time_usec() : 0;
+
+ for (;;) {
+ sched_yield();
+
+ /*
+ * If we have a timer in our paws, compute how much
+ * time went by. Otherwise we estimate by subtracting
+ * the scheduler quantum.
+ *
+ * XXX: The timing here works decently as intended. However,
+ * it would be nice to smoothen out any jitter. Such can
+ * probably be done by subtracting 'usec' by the exponential
+ * moving average of 'usec_tmp' rather than the raw original
+ * value.
+ */
+ if (have_timer) {
+ usec_tmp = (tmr.get_time_usec() - usec_cur);
+ } else {
+ usec_tmp = DEFAULT_TIMESLICE_USEC;
+ }
+
+ /* We are done here! */
+ usec -= usec_tmp;
+ if (usec <= 0) {
+ break;
+ }
+ }
+}
+
void
sched_init(void)
{