Skip to content

Commit c3ab303

Browse files
0.20.0 --- openkal 0.19.0: kal_task_cores, kal_task_set_priority (#44)
kal_task_cores from sysfs topology within the affinity mask; kal_task_set_priority as SCHED_OTHER, SCHED_BATCH, SCHED_IDLE. Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent ff8ce45 commit c3ab303

4 files changed

Lines changed: 88 additions & 7 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.
55

66
```toml
77
[dependencies]
8-
openkal = "0.17.0"
8+
openkal = "0.19.0"
99

1010
[target.'cfg(os = "linux")'.dependencies]
11-
openkal-linux = "0.19.0"
11+
openkal-linux = "0.20.0"
1212
```
1313

1414
## Why it does not use a C library

‎mcpp.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
namespace = "mcpplibs"
33
name = "openkal-linux"
4-
version = "0.19.0"
4+
version = "0.20.0"
55
description = "The reference implementation of openkal for Linux, written on the kernel's own system-call interface so that it can be placed beneath a C library as well as above one."
66
license = "Apache-2.0"
77

@@ -64,7 +64,7 @@ provides-interfaces = [
6464
]
6565

6666
[dependencies]
67-
openkal = "0.18.0"
67+
openkal = "0.19.0"
6868

6969
# The package contributes definitions and no modules. The interface it
7070
# implements is declared by the specification package, which this package

‎src/sys.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ enum : okl_long {
107107
nr_renameat = 264, nr_readlinkat = 267, nr_dup3 = 292, nr_execveat = 322,
108108
nr_close_range = 436, nr_prlimit64 = 302,
109109
nr_dup2 = 33, nr_utimensat = 280, nr_symlinkat = 266, nr_fstatfs = 138,
110-
nr_getrandom = 318,
110+
nr_getrandom = 318, nr_sched_setscheduler = 144,
111111
// openkal 0.15: the region a context stands on is measured by asking the
112112
// kernel which pages are mapped. The floor comes from the limit the kernel
113113
// itself enforces, which `nr_prlimit64' above already names.
@@ -203,7 +203,7 @@ enum : okl_long {
203203
nr_close_range = 436, nr_prlimit64 = 261, nr_dup2 = -1, nr_fcntl = 25,
204204
nr_prctl = 167, nr_sched_getaffinity = 123, nr_getppid = 173,
205205
nr_arch_prctl = -1, nr_utimensat = 88, nr_symlinkat = 36, nr_fstatfs = 44,
206-
nr_getrandom = 278,
206+
nr_getrandom = 278, nr_sched_setscheduler = 119,
207207
// openkal 0.15: the region a context stands on is measured by asking the
208208
// kernel which pages are mapped. The floor comes from the limit the kernel
209209
// itself enforces, which `nr_prlimit64' above already names --- and the two

‎src/task.cpp‎

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,41 @@ int start(void (*entry)(void*), void* arg, okl_uptr want, kal_task* out) {
536536

537537
} // namespace
538538

539+
namespace {
540+
541+
// A small number from a file of the kernel's: what /sys answers is one line.
542+
bool read_number(const char* path, okl_long& out) {
543+
const okl_long fd = okl::sys(okl::nr_openat, okl::at_fdcwd, reinterpret_cast<okl_long>(path),
544+
okl::o_rdonly | okl::o_cloexec);
545+
if (okl::failed(fd)) return false;
546+
char text[32];
547+
const okl_long n = okl::sys(okl::nr_read, fd, reinterpret_cast<okl_long>(text), static_cast<okl_long>(sizeof text));
548+
okl::sys(okl::nr_close, fd);
549+
if (okl::failed(n) || n <= 0) return false;
550+
okl_long v = 0;
551+
bool any = false;
552+
for (okl_long i = 0; i < n && text[i] >= '0' && text[i] <= '9'; ++i) { v = v * 10 + (text[i] - '0'); any = true; }
553+
out = v;
554+
return any;
555+
}
556+
557+
// `/sys/devices/system/cpu/cpu<n>/topology/<leaf>'.
558+
bool cpu_topology(unsigned long cpu, const char* leaf, okl_long& out) {
559+
char path[96];
560+
unsigned long at = 0;
561+
for (const char* c = "/sys/devices/system/cpu/cpu"; *c; ++c) path[at++] = *c;
562+
char digits[24];
563+
int d = 0;
564+
do { digits[d++] = static_cast<char>('0' + cpu % 10); cpu /= 10; } while (cpu);
565+
while (d) path[at++] = digits[--d];
566+
for (const char* c = "/topology/"; *c; ++c) path[at++] = *c;
567+
for (const char* c = leaf; *c; ++c) { if (at + 1 >= sizeof path) return false; path[at++] = *c; }
568+
path[at] = 0;
569+
return read_number(path, out);
570+
}
571+
572+
} // namespace
573+
539574
extern "C" {
540575

541576
int kal_task_start(void (*entry)(void*), void* arg, kal_task* out) {
@@ -696,7 +731,53 @@ int kal_task_wake(const kal_u32* word, kal_uintptr count, kal_uintptr* woken) {
696731
// convention, and so does the block this implementation builds. Clause 7.10.
697732
kal_uintptr kal_task_props(void) {
698733
return KAL_TASK_PROP_PREEMPTIVE | KAL_TASK_PROP_PARALLEL
699-
| KAL_TASK_PROP_WAIT_TIMEOUT | KAL_TASK_PROP_THREAD_LOCAL;
734+
| KAL_TASK_PROP_WAIT_TIMEOUT | KAL_TASK_PROP_THREAD_LOCAL
735+
| KAL_TASK_PROP_PRIORITY;
736+
}
737+
738+
// THE CORES OF THE PROCESSORS THIS PROGRAM MAY RUN ON. The kernel states each
739+
// processor's package and core in sysfs on every architecture it supports; two
740+
// processors that share both are one core. A processor whose topology cannot
741+
// be read makes the answer "cannot say" rather than a guess.
742+
kal_uintptr kal_task_cores(void) {
743+
unsigned long mask[128] = { 0 };
744+
const okl_long n = okl::sys(okl::nr_sched_getaffinity, 0, static_cast<okl_long>(sizeof mask),
745+
reinterpret_cast<okl_long>(mask));
746+
if (okl::failed(n) || n <= 0) return 0;
747+
constexpr unsigned kMax = 1024;
748+
okl_long seen[kMax];
749+
unsigned count = 0;
750+
const unsigned long bits = static_cast<unsigned long>(n) * 8;
751+
constexpr unsigned long kWord = 8 * sizeof(unsigned long);
752+
for (unsigned long cpu = 0; cpu < bits; ++cpu) {
753+
if (!((mask[cpu / kWord] >> (cpu % kWord)) & 1ul)) continue;
754+
okl_long package = 0, core = 0;
755+
if (!cpu_topology(cpu, "physical_package_id", package) || !cpu_topology(cpu, "core_id", core)) return 0;
756+
const okl_long key = (package << 32) | core;
757+
bool known = false;
758+
for (unsigned i = 0; i < count && !known; ++i) known = seen[i] == key;
759+
if (known) continue;
760+
if (count == kMax) return 0;
761+
seen[count++] = key;
762+
}
763+
return count;
764+
}
765+
766+
// THE CALLING THREAD'S SCHEDULING POLICY: the kernel's default, its batch
767+
// policy, and its idle policy, which runs only when nothing else wants the
768+
// processor. An unprivileged thread may lower its policy and may be refused
769+
// raising it again (RLIMIT_NICE), which is kal_err_permission.
770+
int kal_task_set_priority(kal_uintptr priority) {
771+
okl_long policy;
772+
switch (priority) {
773+
case KAL_TASK_PRIORITY_DEFAULT: policy = 0; break; // SCHED_OTHER
774+
case KAL_TASK_PRIORITY_LOW: policy = 3; break; // SCHED_BATCH
775+
case KAL_TASK_PRIORITY_BACKGROUND: policy = 5; break; // SCHED_IDLE
776+
default: return kal_err_invalid;
777+
}
778+
int param = 0; // struct sched_param: the static priority, 0 for these policies
779+
const okl_long r = okl::sys(okl::nr_sched_setscheduler, 0, policy, reinterpret_cast<okl_long>(&param));
780+
return okl::failed(r) ? okl::translate(r) : kal_ok;
700781
}
701782

702783
}

0 commit comments

Comments
 (0)