Hi all, I attach the patches I am using to run a simple real-time test (cyclictest) on OSv. The patches are based on github issue 386 (https://github.com/cloudius-systems/osv/issues/386) and are a forward port of patches by Nadav Har'El and Claudio Fontana; I only applied a few changes to them.
With these patches, cyclictest can start and measure some latencies... In the next few days, I'll run some more accurate tests, and I'll report on the latencies introduced by OSv. Luca P.S.: please cc me if you reply because I am not subscribed to the mailing list -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/osv-dev/CAJfkoWpFDkukjNb%3DR2kxugjo2YBqeMgpoCuM26QiVdwaB6k5pw%40mail.gmail.com.
From f030ce398d628075b1c3e07eee2ebae1b1af10ce Mon Sep 17 00:00:00 2001 From: Claudio Fontana <[email protected]> Date: Thu, 7 Aug 2014 17:28:39 +0200 Subject: [PATCH 3/3] libc/time: add stub for clock_nanosleep we only support CLOCK_MONOTONIC, and we don't support remainder due to the signal implementation. Signed-off-by: Claudio Fontana <[email protected]> --- include/api/sys/time.h | 14 ++++++++++++- include/osv/sched.hh | 10 ++++++++++ libc/time.cc | 45 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/include/api/sys/time.h b/include/api/sys/time.h index 1948bcd1..bec28a19 100644 --- a/include/api/sys/time.h +++ b/include/api/sys/time.h @@ -50,7 +50,19 @@ struct timezone { #define timersub(s,t,a) ( (a)->tv_sec = (s)->tv_sec - (t)->tv_sec, \ ((a)->tv_usec = (s)->tv_usec - (t)->tv_usec) < 0 && \ ((a)->tv_usec += 1000000, (a)->tv_sec--) ) -#endif + +#define timespecisset(t) ((t)->tv_sec || (t)->tv_nsec) +#define timespecclear(t) ((t)->tv_sec = (t)->tv_nsec = 0) +#define timespeccmp(s,t,op) ((s)->tv_sec == (t)->tv_sec ? \ + (s)->tv_nsec op (t)->tv_nsec : (s)->tv_sec op (t)->tv_sec) +#define timespecadd(s,t,a) ( (a)->tv_sec = (s)->tv_sec + (t)->tv_sec, \ + ((a)->tv_nsec = (s)->tv_nsec + (t)->tv_nsec) >= 1000000000L && \ + ((a)->tv_nsec -= 1000000000L, (a)->tv_sec++) ) +#define timespecsub(s,t,a) ( (a)->tv_sec = (s)->tv_sec - (t)->tv_sec, \ + ((a)->tv_nsec = (s)->tv_nsec - (t)->tv_nsec) < 0 && \ + ((a)->tv_nsec += 1000000000L, (a)->tv_sec--) ) + +#endif /* _GNU_SOURCE || _BSD_SOURCE */ #ifdef __cplusplus } diff --git a/include/osv/sched.hh b/include/osv/sched.hh index 8f217320..28ba2edc 100644 --- a/include/osv/sched.hh +++ b/include/osv/sched.hh @@ -513,6 +513,8 @@ public: inline void wake_with_from_mutex(Action action); template <class Rep, class Period> static void sleep(std::chrono::duration<Rep, Period> duration); + template <class Clock, class Duration> + static void sleep(std::chrono::time_point<Clock, Duration> time_point); /** * Let the other thread on the current CPU run if there is any. * @@ -1407,6 +1409,14 @@ void thread::sleep(std::chrono::duration<Rep, Period> duration) sleep_impl(t); } +template <class Clock, class Duration> +void thread::sleep(std::chrono::time_point<Clock, Duration> time_point) +{ + timer t(*current()); + t.set(time_point); + wait_until([&] { return t.expired(); }); +} + template <class Action> inline void thread::wake_with_irq_or_preemption_disabled(Action action) diff --git a/libc/time.cc b/libc/time.cc index 2660eb86..abafe113 100644 --- a/libc/time.cc +++ b/libc/time.cc @@ -15,7 +15,7 @@ #include <osv/sched.hh> #include "pthread.hh" -u64 convert(const timespec& ts) +static u64 convert(const timespec& ts) { return ts.tv_sec * 1000000000 + ts.tv_nsec; } @@ -36,6 +36,9 @@ int gettimeofday(struct timeval* tv, struct timezone* tz) OSV_LIBC_API int nanosleep(const struct timespec* req, struct timespec* rem) { + if (!req || req->tv_nsec < 0 || req->tv_nsec >= 1000000000L || req->tv_sec < 0) + return libc_error(EINVAL); + sched::thread::sleep(std::chrono::nanoseconds(convert(*req))); return 0; } @@ -60,6 +63,9 @@ static inline void fill_ts(std::chrono::duration<Rep, Period> d, timespec *ts) OSV_LIBC_API int clock_gettime(clockid_t clk_id, struct timespec* ts) { + if (!ts) + return libc_error(EINVAL); + switch (clk_id) { case CLOCK_BOOTTIME: case CLOCK_MONOTONIC: @@ -127,3 +133,40 @@ clock_t clock(void) clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); return ts.tv_sec * 1000000000L + ts.tv_nsec; } + +int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain) +{ + /* XXX we are implementing really only CLOCK_MONOTONIC, */ + /* and we don't support remain, due to signals. */ + WARN_STUBBED(); + if (!request) { + return libc_error(EINVAL); + } + if (remain) { + assert(0); /* XXX not supported yet */ + } + if (clock_id != CLOCK_MONOTONIC) { + assert(0); /* XXX not supported yet */ + } + + switch (flags) { + case 0: + return nanosleep(request, NULL); + case TIMER_ABSTIME: { + struct timespec ts; + if (clock_gettime(clock_id, &ts) != 0) { + return -1; + } + if (timespeccmp(request, &ts, <=)) { + return 0; + } + sched::thread::sleep(osv::clock::uptime::time_point( + osv::clock::uptime::duration(convert(*request)))); + return 0; + } + default: + return libc_error(EINVAL); + } +} -- 2.30.2
From d53b28b16d625c85c14d1585f654524e869a423e Mon Sep 17 00:00:00 2001 From: Nadav Har'El <[email protected]> Date: Wed, 29 Mar 2023 12:05:13 +0300 Subject: [PATCH 1/3] sched: low-level support for POSIX-compatible real-time scheduling This patch adds "real-time priority" to OSv threads, which can be used to implement Posix's SCHED_FIFO and SCHED_RR scheduling policies. Refs #386. It doesn't yet "Fixes" it because this patch does not yet add the Posix front-end to this implementation (sched_setscheduler() et al.) - this will be added in a separate patch. We add to each thread a _realtime._priority, an unsigned integer. Normal (non-real-time) threads have _realtime._priority of 0, and are time-shared fairly as before, while realtime threads have _realtime._priority > 0, and preempt any thread with a lower realtime priority, including all normal threads. We add a new API, t->set_realtime_priority(priority), to make a thread real-time scheduled. The "priority" is an unsigned int as explained above (0 means normal thread). The resulting scheduling policy is fully compatible with POSIX's SCHED_FIFO. This includes following the intricate rules on what happens to the running thread's place in the queue when there are several real-time threads with the same priority and the running thread yields, waits, or is preempted by a higher-priority thread. This patch also adds an exercise for these cases in tests/misc-scheduler.cc. This patch does not yet implement time slices as needed for supporting POSIX's SCHED_RR policy. This patch already includes a time-slice setting API, t->set_realtime_time_slice(duration), but if used it produces a warning message, and the scheduler currently ignores this setting. Adding support for real-time time slices is left for a future patch: To implement time slices, we will need to keep in _realtime the total time this thread ran since starting this slice (only zeroed when the thread is removed from the run queue), and each time we switch to a realtime thread with timeslice >= 0, we'll set an appropriate timer (for timeslice minus already used time). I tested that although this patch adds code into the scheduler it does not noticably slow down context switches of normal threads. E.g., tests/misc-ctxsw.so measured colocated context switch time of 308ns before this patch, and exactly the same after it. While developing this patch I tested it using added workloads in tests/misc-scheduler.cc, included in this patch, but unfortunately this is not a real regression test - it has no assertions and cannot automatically detect failure (and, like all the "misc-*" tests, it doesn't run automatically in "make check"). Signed-off-by: Nadav Har'El <[email protected]> --- core/sched.cc | 79 +++++++++++++++++++++++++++++++----- include/osv/sched.hh | 75 +++++++++++++++++++++++++++++++++- tests/misc-scheduler.cc | 89 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 12 deletions(-) diff --git a/core/sched.cc b/core/sched.cc index 65842ff3..fcb92a5b 100644 --- a/core/sched.cc +++ b/core/sched.cc @@ -26,6 +26,7 @@ #include <osv/preempt-lock.hh> #include <osv/app.hh> #include <osv/symbols.hh> +#include <osv/stubbing.hh> MAKE_SYMBOL(sched::thread::current); MAKE_SYMBOL(sched::cpu::current); @@ -289,8 +290,26 @@ void cpu::reschedule_from_interrupt(bool called_from_yield, return; #endif } else if (!called_from_yield) { - auto &t = *runqueue.begin(); - if (p->_runtime.get_local() < t._runtime.get_local()) { + auto &t = *runqueue.begin(); + if (p->_realtime._priority > 0) { + // Only switch to a higher-priority realtime thread, or to + // the next one with equal priority if yielding. + if (t._realtime._priority < p->_realtime._priority) { +#ifdef __aarch64__ + return switch_data; +#else + return; +#endif + } + if (t._realtime._priority == p->_realtime._priority && + !called_from_yield) { +#ifdef __aarch64__ + return switch_data; +#else + return; +#endif + } + } else if (p->_runtime.get_local() < t._runtime.get_local()) { preemption_timer.cancel(); auto delta = p->_runtime.time_until(t._runtime.get_local()); if (delta > 0) { @@ -309,7 +328,10 @@ void cpu::reschedule_from_interrupt(bool called_from_yield, p->_detached_state->st.store(thread::status::queued); if (!called_from_yield) { - enqueue(*p); + // POSIX requires that if a real-time thread doesn't yield but + // rather is preempted by a higher-priority thread, it be + // reinserted into the runqueue first, not last, among its equals. + enqueue_first_equal(*p); } trace_sched_preempt(); @@ -350,16 +372,18 @@ void cpu::reschedule_from_interrupt(bool called_from_yield, n->_runtime.add_context_switch_penalty(); } preemption_timer.cancel(); - if (!called_from_yield) { - if (!runqueue.empty()) { - auto& t = *runqueue.begin(); - auto delta = n->_runtime.time_until(t._runtime.get_local()); - if (delta > 0) { - preemption_timer.set_with_irq_disabled(now + delta); + if (n->_realtime._priority == 0) { + if (!called_from_yield) { + if (!runqueue.empty()) { + auto& t = *runqueue.begin(); + auto delta = n->_runtime.time_until(t._runtime.get_local()); + if (delta > 0) { + preemption_timer.set_with_irq_disabled(now + delta); + } } + } else { + preemption_timer.set_with_irq_disabled(now + preempt_after); } - } else { - preemption_timer.set_with_irq_disabled(now + preempt_after); } if (app_thread.load(std::memory_order_relaxed) != n->_app) { // don't write into a cache line if it can be avoided @@ -530,6 +554,16 @@ void cpu::enqueue(thread& t) runqueue.insert_equal(t); } +// When the run queue has several threads with equal thread_runtime_compare, +// enqueue() puts a thread after its equals, while enqueue_first_equal() +// puts it before its equals. The distinction is mostly interesting for real- +// time priority threads. +void cpu::enqueue_first_equal(thread& t) +{ + trace_sched_queue(&t); + runqueue.insert_before(runqueue.lower_bound(t), t); +} + void cpu::init_on_cpu() { arch.init_on_cpu(); @@ -866,6 +900,29 @@ float thread::priority() const return _runtime.priority(); } +void thread::set_realtime_priority(unsigned priority) +{ + _realtime._priority = priority; +} + +unsigned thread::realtime_priority() const +{ + return _realtime._priority; +} + +void thread::set_realtime_time_slice(thread_realtime::duration time_slice) +{ + if (time_slice > 0) { + WARN_ONCE("set_realtime_time_slice() used but real-time time slices" + " not yet supported"); + } + _realtime._time_slice = time_slice; +} + +thread_realtime::duration thread::realtime_time_slice() const { + return _realtime._time_slice; +} + sched::thread::status thread::get_status() const { return _detached_state->st.load(std::memory_order_relaxed); diff --git a/include/osv/sched.hh b/include/osv/sched.hh index 8a2694cb..c1f414ba 100644 --- a/include/osv/sched.hh +++ b/include/osv/sched.hh @@ -313,6 +313,24 @@ private: int _renormalize_count; }; +// "Normal" threads are fairly time-shared according to the thread_runtime +// data above. Such normal threads all have _realtime._priority == 0. +// A "real-time" thread is one where _realtime._priority > 0. The scheduler +// always picks the thread with the highest _realtime_priority to run next; +// In particular normal threads run only when no real-time thread wants to +// run. When several real-time threads with equal _realtime._priority want to +// run, each one is run for _realtime._time_slice before switching to the +// next one; If _realtime_time_slice is == 0, there is no limit on the amount +// of time the thread may run - it will only be preempted when it waits, +// yields, or a higher-priority thread comes along. +// The realtime scheduling policy matches the POSIX SCHED_RR and SCHED_FIFO. +class thread_realtime { +public: + using duration = thread_runtime::duration; + unsigned _priority = 0; + duration _time_slice = duration::zero(); +}; + // "tau" controls the length of the history we consider for scheduling, // or more accurately the rate of decay of an exponential moving average. // In particular, it can be seen that if a thread has been monopolizing the @@ -615,6 +633,54 @@ public: * explained in set_priority(). */ float priority() const; + /** + * Set thread's real-time priority + * + * By default new threads have a "real-time priority" of 0 and participate + * in fair time-sharing of the CPUs (the share is determined by + * set_priority()). + * + * A thread becomes a "real-time" thread by setting a positive integer as + * real-time priority. The scheduler always picks the thread with the + * highest real-time priority to run next; In particular normal threads run + * only when no real-time thread wants to run. When several real-time + * threads with equal real-time priority want to run, each one is run + * until it waits or yields, before switching to the next one. + * + * The real-time scheduling policy matches POSIX's "SCHED_FIFO" policy, + * or "SCHED_RR" if set_realtime_time_slice() was also used. + */ + void set_realtime_priority(unsigned priority); + /** + * Get thread's real-time priority + * + * Returns the thread's real-time priority, an unsigned integer whose + * meaning is explained in set_realtime_priority(). + */ + unsigned realtime_priority() const; + /** + * Set thread's real-time scheduling time slice. + * + * By default, real-time threads (see set_realtime_priority()) continue to + * run until they yield, wait, or are preempted by a higher-priority + * real-time thread. When a time_slice > 0 is set for a thread with this + * function, a thread will be limited to that time-slice before it + * automatically yields to the next thread of equal real-time priority (if + * any). Setting time_slice == 0 reverts to the default behavior, disabling + * the time-slice limit for the thread. + * + * With time_slice == 0, the real-time scheduling policy matches POSIX's + * "SCHED_FIFO" policy. With time_slice > 0, it matches POSIX's "SCHED_RR" + * policy. + */ + void set_realtime_time_slice(thread_realtime::duration time_slice); + /** + * Get thread's real-time scheduling time slice + * + * Returns the thread's real-time scheduling time slice, whose meaning is + * explained in set_realtime_time_slice(). + */ + thread_realtime::duration realtime_time_slice() const; /** * Prevent a waiting thread from ever waking (returns false if the thread * was not in waiting state). This capability is not safe: If the thread @@ -708,6 +774,7 @@ private: // // wake() on any state except waiting is discarded. thread_runtime _runtime; + thread_realtime _realtime; // part of the thread state is detached from the thread structure, // and freed by rcu, so that waking a thread and destroying it can // occur in parallel without synchronization via thread_handle @@ -862,7 +929,12 @@ osv::clock::uptime::duration process_cputime(); class thread_runtime_compare { public: bool operator()(const thread& t1, const thread& t2) const { - return t1._runtime.get_local() < t2._runtime.get_local(); + if (t1._realtime._priority > t2._realtime._priority) + return true; + else if (t2._realtime._priority > 0) + return false; + else + return t1._runtime.get_local() < t2._runtime.get_local(); } }; @@ -931,6 +1003,7 @@ struct cpu : private timer_base::client { thread_runtime::duration preempt_after = thyst); #endif void enqueue(thread& t); + void enqueue_first_equal(thread& t); void init_idle_thread(); virtual void timer_fired() override; class notifier; diff --git a/tests/misc-scheduler.cc b/tests/misc-scheduler.cc index f4de24d1..578fabe1 100644 --- a/tests/misc-scheduler.cc +++ b/tests/misc-scheduler.cc @@ -139,6 +139,66 @@ void priority_test(std::vector<float> ps) } #endif +#ifdef __OSV__ +void realtime_test(std::vector<int> ps) +{ + std::cerr << "Starting realtime test\n"; + std::vector<std::thread> threads; + mutex mtx; + for (auto p : ps) { + threads.push_back(std::thread([p]() { + sched::thread::current()->set_realtime_priority(p); + std::cout << "Starting thread with realtime priority " << p << "\n"; + // Sleep a bit, to let all test threads get started. The thread + // starting the test threads is not realtime, so it can be preempted + // by the test threads. + sleep(1); + for (int i=0; i<10; i++) { + _loop(100000); + std::cout << p << std::flush; + } + })); + } + for (auto &t : threads) { + t.join(); + } + std::cerr << "\nRealtime test done\n"; +} + +void realtime_test2(bool yield) +{ + std::cerr << "Starting realtime test #2 - FIFO order, yield=" << yield << "\n"; + std::vector<std::thread> threads; + mutex mtx; + std::atomic<int> last_seen(-1); + for (int p = 0; p < 10; p++) { + threads.push_back(std::thread([p,yield,&last_seen]() { + sched::thread::current()->set_realtime_priority(1); + // Sleep a bit, to let all test threads get started. The thread + // starting the test threads is not realtime, so it can be preempted + // by the test threads. + sleep(1); + for (int i = 0 ; i < 3; i++) { + for(int j = 0; j < 100000; j++) { + if (last_seen.exchange(p) != p) { + std::cout << p << std::flush; // context-switched to p + } + _loop(1); + } + if (yield) + sched::thread::yield(); + else + sched::thread::sleep(std::chrono::milliseconds(1)); + } + })); + } + for (auto &t : threads) { + t.join(); + } + std::cerr << "\nRealtime test #2 done\n"; +} +#endif + int main() { @@ -148,6 +208,35 @@ int main() return 0; } + #ifdef __OSV__ + // Tests for thread::set_realtime() support for POSIX-like realtime + // scheduling. + // TODO: Move this code into a real test, and in addition to just + // printing progress, also save it into a string and check this string. + // (Need to check the first 10 characters of this string repeat 2 more + // times and that's it). + realtime_test({0, 1, 2}); + realtime_test2(false); + realtime_test2(true); + // Check that intermittent thread with priority 2 doesn't force + // realtime_test2 to context-switch more often than it normally + // should (each time we the priority-2 thread sleeps, we need to + // go back to the same priority-1 thread that previously ran - not + // to the next one). We expect the output from the test below to + // be identical to that from the test above. + std::cout << "Additional intermittent thread with priority 2\n"; + std::atomic<bool> stop(false); + std::thread ti([&stop]() { + sched::thread::current()->set_realtime_priority(2); + while (!stop.load()) { + sched::thread::sleep(std::chrono::milliseconds(5)); + } + }); + realtime_test2(true); + stop.store(true); + ti.join(); +#endif + #ifdef __OSV__ auto p = sched::thread::priority_default; priority_test({p, p*4}); -- 2.30.2
From 6148dcc3295c0fbaf47de4f4ff817f31d281488a Mon Sep 17 00:00:00 2001 From: Claudio Fontana <[email protected]> Date: Thu, 17 Jul 2014 16:52:35 +0200 Subject: [PATCH 2/3] libc: add RT sched API implement the sched_ API using the OSv realtime scheduler implementation. This includes: sched_setscheduler, sched_getscheduler, sched_setparam, sched_getparam, sched_get_priority_min, sched_get_priority_max, and a stub for Linux-only sched_rr_get_interval. They are implemented together as they are tightly integrated, and one calls the other. Signed-off-by: Claudio Fontana <[email protected]> --- Makefile | 2 + core/sched.cc | 20 ++++-- include/api/sched.h | 8 +++ include/osv/sched.hh | 2 +- libc/pthread.cc | 22 ------- libc/rt/sched.cc | 140 ++++++++++++++++++++++++++++++++++++++++ tests/misc-scheduler.cc | 8 ++- 7 files changed, 171 insertions(+), 31 deletions(-) create mode 100644 libc/rt/sched.cc diff --git a/Makefile b/Makefile index ea6f1c0d..ffe5abbb 100644 --- a/Makefile +++ b/Makefile @@ -1913,6 +1913,8 @@ libc += mallopt.o libc += linux/makedev.o +libc += rt/sched.o + musl += fenv/fegetexceptflag.o musl += fenv/feholdexcept.o musl += fenv/fesetexceptflag.o diff --git a/core/sched.cc b/core/sched.cc index fcb92a5b..0f8571d5 100644 --- a/core/sched.cc +++ b/core/sched.cc @@ -890,6 +890,21 @@ void thread::yield(thread_runtime::duration preempt_after) #endif } +void thread::set_realtime_priority(int policy, unsigned int priority) +{ + switch (policy) { + case SCHED_OTHER: + _realtime._priority = 0; + break; + case SCHED_FIFO: + case SCHED_RR: + _realtime._priority = priority; + break; + default: + assert(0); + } +} + void thread::set_priority(float priority) { _runtime.set_priority(priority); @@ -900,11 +915,6 @@ float thread::priority() const return _runtime.priority(); } -void thread::set_realtime_priority(unsigned priority) -{ - _realtime._priority = priority; -} - unsigned thread::realtime_priority() const { return _realtime._priority; diff --git a/include/api/sched.h b/include/api/sched.h index 1893efc1..bd527619 100644 --- a/include/api/sched.h +++ b/include/api/sched.h @@ -9,6 +9,14 @@ extern "C" { #define __NEED_struct_timespec #define __NEED_pid_t #define __NEED_time_t +#define __NEED_size_t + +/* It is convenient to expose these values here instead + * of calling the sched_get_priority_min/max functions + * each time. + */ +#define SCHED_PRIO_MIN 1 +#define SCHED_PRIO_MAX 99 #include <bits/alltypes.h> diff --git a/include/osv/sched.hh b/include/osv/sched.hh index c1f414ba..8f217320 100644 --- a/include/osv/sched.hh +++ b/include/osv/sched.hh @@ -650,7 +650,7 @@ public: * The real-time scheduling policy matches POSIX's "SCHED_FIFO" policy, * or "SCHED_RR" if set_realtime_time_slice() was also used. */ - void set_realtime_priority(unsigned priority); + void set_realtime_priority(int policy, unsigned priority); /** * Get thread's real-time priority * diff --git a/libc/pthread.cc b/libc/pthread.cc index de5979e8..76e6d982 100644 --- a/libc/pthread.cc +++ b/libc/pthread.cc @@ -960,28 +960,6 @@ void pthread_exit(void *retval) t->_thread->exit(); } -// Following 4 functions provide minimal implementation -// that ONLY covers default Linux SCHED_OTHER policy -int sched_get_priority_min(int policy) -{ - switch (policy) { - case SCHED_OTHER: - return 0; - default: - return EINVAL; - } -} - -int sched_get_priority_max(int policy) -{ - switch (policy) { - case SCHED_OTHER: - return 0; - default: - return EINVAL; - } -} - int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) { diff --git a/libc/rt/sched.cc b/libc/rt/sched.cc new file mode 100644 index 00000000..8f70160e --- /dev/null +++ b/libc/rt/sched.cc @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2014 Huawei Technologies Duesseldorf GmbH + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include <errno.h> +#include <api/sched.h> +#include <api/sys/resource.h> +#include <osv/sched.hh> + +#include <osv/stubbing.hh> + +/* sched_rr_get_interval writes the time quantum for SCHED_RR + * processes to the tp timespec structure. + * This is Linux-specific, and we don't implement this yet. + */ + +int sched_rr_get_interval(pid_t pid, struct timespec * tp) +{ + WARN_STUBBED(); + errno = ENOSYS; + return -1; +} + +static int sched_get_priority_minmax(int policy, int value) +{ + switch (policy) { + case SCHED_OTHER: + return 0; + case SCHED_FIFO: + return value; + default: + errno = EINVAL; + /* error return value unfortunately overlaps with allowed + * POSIX API allowed range. Another good reason to have + * only rt priorities > 0. + */ + return -1; + } +} + +int sched_get_priority_min(int policy) +{ + return sched_get_priority_minmax(policy, SCHED_PRIO_MIN); +} + +int sched_get_priority_max(int policy) +{ + return sched_get_priority_minmax(policy, SCHED_PRIO_MAX); +} + +static int sched_setparam_aux(sched::thread *t, int policy, int prio) +{ + switch (policy) { + case SCHED_OTHER: + if (prio != 0) { + errno = EINVAL; + return -1; + } + break; + case SCHED_FIFO: + if (prio < SCHED_PRIO_MIN || prio > SCHED_PRIO_MAX) { + errno = EINVAL; + return -1; + } + break; + default: + errno = EINVAL; + return -1; + } + + t->set_realtime_priority(policy, prio); + return 0; +} + +int sched_setscheduler(pid_t pid, int policy, + const struct sched_param *param) +{ + sched::thread *t = sched::thread::current(); + + if (pid != 0 && pid != getpid()) { + errno = ESRCH; + return -1; + } + if (!param) { + errno = EINVAL; + return -1; + } + + return sched_setparam_aux(t, policy, param->sched_priority); +} + +int sched_getscheduler(pid_t pid) +{ + sched::thread *t = sched::thread::current(); + + if (pid != 0 && pid != getpid()) { + errno = ESRCH; + return -1; + } + + if (t->realtime_priority() == 0) { + return SCHED_OTHER; + }; + + return SCHED_FIFO; +} + +/* +int sched_setparam(pid_t pid, const struct sched_param *param) +{ + sched::thread *t = sched::thread::current(); + + if (pid != 0 && pid != getpid()) { + errno = ESRCH; + return -1; + } + if (!param) { + errno = EINVAL; + return -1; + } + + return sched_setparam_aux(t, t->get_realtime().policy(), param->sched_priority); +} +*/ + +int sched_getparam(pid_t pid, struct sched_param *param) +{ + sched::thread *t = sched::thread::current(); + + if (pid != 0 && pid != getpid()) { + errno = ESRCH; + return -1; + } + + param->sched_priority = t->realtime_priority(); + return 0; +} diff --git a/tests/misc-scheduler.cc b/tests/misc-scheduler.cc index 578fabe1..c29a1f26 100644 --- a/tests/misc-scheduler.cc +++ b/tests/misc-scheduler.cc @@ -140,14 +140,16 @@ void priority_test(std::vector<float> ps) #endif #ifdef __OSV__ -void realtime_test(std::vector<int> ps) +void realtime_test(int policy, std::vector<int> ps) { + assert(policy == SCHED_RR || policy == SCHED_FIFO); std::cerr << "Starting realtime test\n"; std::vector<std::thread> threads; mutex mtx; for (auto p : ps) { - threads.push_back(std::thread([p]() { - sched::thread::current()->set_realtime_priority(p); + assert(p >= SCHED_PRIO_MIN && p <= SCHED_PRIO_MAX); + threads.push_back(std::thread([policy, p]() { + sched::thread::current()->set_realtime_priority(policy, p); std::cout << "Starting thread with realtime priority " << p << "\n"; // Sleep a bit, to let all test threads get started. The thread // starting the test threads is not realtime, so it can be preempted -- 2.30.2
