Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ for Linux, written on the kernel's own system-call interface.

```toml
[dependencies]
openkal = "0.17.0"
openkal = "0.19.0"

[target.'cfg(os = "linux")'.dependencies]
openkal-linux = "0.19.0"
openkal-linux = "0.20.0"
```

## Why it does not use a C library
Expand Down
4 changes: 2 additions & 2 deletions mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
namespace = "mcpplibs"
name = "openkal-linux"
version = "0.19.0"
version = "0.20.0"
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."
license = "Apache-2.0"

Expand Down Expand Up @@ -64,7 +64,7 @@ provides-interfaces = [
]

[dependencies]
openkal = "0.18.0"
openkal = "0.19.0"

# The package contributes definitions and no modules. The interface it
# implements is declared by the specification package, which this package
Expand Down
4 changes: 2 additions & 2 deletions src/sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ enum : okl_long {
nr_renameat = 264, nr_readlinkat = 267, nr_dup3 = 292, nr_execveat = 322,
nr_close_range = 436, nr_prlimit64 = 302,
nr_dup2 = 33, nr_utimensat = 280, nr_symlinkat = 266, nr_fstatfs = 138,
nr_getrandom = 318,
nr_getrandom = 318, nr_sched_setscheduler = 144,
// openkal 0.15: the region a context stands on is measured by asking the
// kernel which pages are mapped. The floor comes from the limit the kernel
// itself enforces, which `nr_prlimit64' above already names.
Expand Down Expand Up @@ -203,7 +203,7 @@ enum : okl_long {
nr_close_range = 436, nr_prlimit64 = 261, nr_dup2 = -1, nr_fcntl = 25,
nr_prctl = 167, nr_sched_getaffinity = 123, nr_getppid = 173,
nr_arch_prctl = -1, nr_utimensat = 88, nr_symlinkat = 36, nr_fstatfs = 44,
nr_getrandom = 278,
nr_getrandom = 278, nr_sched_setscheduler = 119,
// openkal 0.15: the region a context stands on is measured by asking the
// kernel which pages are mapped. The floor comes from the limit the kernel
// itself enforces, which `nr_prlimit64' above already names --- and the two
Expand Down
83 changes: 82 additions & 1 deletion src/task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,41 @@ int start(void (*entry)(void*), void* arg, okl_uptr want, kal_task* out) {

} // namespace

namespace {

// A small number from a file of the kernel's: what /sys answers is one line.
bool read_number(const char* path, okl_long& out) {
const okl_long fd = okl::sys(okl::nr_openat, okl::at_fdcwd, reinterpret_cast<okl_long>(path),
okl::o_rdonly | okl::o_cloexec);
if (okl::failed(fd)) return false;
char text[32];
const okl_long n = okl::sys(okl::nr_read, fd, reinterpret_cast<okl_long>(text), static_cast<okl_long>(sizeof text));
okl::sys(okl::nr_close, fd);
if (okl::failed(n) || n <= 0) return false;
okl_long v = 0;
bool any = false;
for (okl_long i = 0; i < n && text[i] >= '0' && text[i] <= '9'; ++i) { v = v * 10 + (text[i] - '0'); any = true; }
out = v;
return any;
}

// `/sys/devices/system/cpu/cpu<n>/topology/<leaf>'.
bool cpu_topology(unsigned long cpu, const char* leaf, okl_long& out) {
char path[96];
unsigned long at = 0;
for (const char* c = "/sys/devices/system/cpu/cpu"; *c; ++c) path[at++] = *c;
char digits[24];
int d = 0;
do { digits[d++] = static_cast<char>('0' + cpu % 10); cpu /= 10; } while (cpu);
while (d) path[at++] = digits[--d];
for (const char* c = "/topology/"; *c; ++c) path[at++] = *c;
for (const char* c = leaf; *c; ++c) { if (at + 1 >= sizeof path) return false; path[at++] = *c; }
path[at] = 0;
return read_number(path, out);
}

} // namespace

extern "C" {

int kal_task_start(void (*entry)(void*), void* arg, kal_task* out) {
Expand Down Expand Up @@ -696,7 +731,53 @@ int kal_task_wake(const kal_u32* word, kal_uintptr count, kal_uintptr* woken) {
// convention, and so does the block this implementation builds. Clause 7.10.
kal_uintptr kal_task_props(void) {
return KAL_TASK_PROP_PREEMPTIVE | KAL_TASK_PROP_PARALLEL
| KAL_TASK_PROP_WAIT_TIMEOUT | KAL_TASK_PROP_THREAD_LOCAL;
| KAL_TASK_PROP_WAIT_TIMEOUT | KAL_TASK_PROP_THREAD_LOCAL
| KAL_TASK_PROP_PRIORITY;
}

// THE CORES OF THE PROCESSORS THIS PROGRAM MAY RUN ON. The kernel states each
// processor's package and core in sysfs on every architecture it supports; two
// processors that share both are one core. A processor whose topology cannot
// be read makes the answer "cannot say" rather than a guess.
kal_uintptr kal_task_cores(void) {
unsigned long mask[128] = { 0 };
const okl_long n = okl::sys(okl::nr_sched_getaffinity, 0, static_cast<okl_long>(sizeof mask),
reinterpret_cast<okl_long>(mask));
if (okl::failed(n) || n <= 0) return 0;
constexpr unsigned kMax = 1024;
okl_long seen[kMax];
unsigned count = 0;
const unsigned long bits = static_cast<unsigned long>(n) * 8;
constexpr unsigned long kWord = 8 * sizeof(unsigned long);
for (unsigned long cpu = 0; cpu < bits; ++cpu) {
if (!((mask[cpu / kWord] >> (cpu % kWord)) & 1ul)) continue;
okl_long package = 0, core = 0;
if (!cpu_topology(cpu, "physical_package_id", package) || !cpu_topology(cpu, "core_id", core)) return 0;
const okl_long key = (package << 32) | core;
bool known = false;
for (unsigned i = 0; i < count && !known; ++i) known = seen[i] == key;
if (known) continue;
if (count == kMax) return 0;
seen[count++] = key;
}
return count;
}

// THE CALLING THREAD'S SCHEDULING POLICY: the kernel's default, its batch
// policy, and its idle policy, which runs only when nothing else wants the
// processor. An unprivileged thread may lower its policy and may be refused
// raising it again (RLIMIT_NICE), which is kal_err_permission.
int kal_task_set_priority(kal_uintptr priority) {
okl_long policy;
switch (priority) {
case KAL_TASK_PRIORITY_DEFAULT: policy = 0; break; // SCHED_OTHER
case KAL_TASK_PRIORITY_LOW: policy = 3; break; // SCHED_BATCH
case KAL_TASK_PRIORITY_BACKGROUND: policy = 5; break; // SCHED_IDLE
default: return kal_err_invalid;
}
int param = 0; // struct sched_param: the static priority, 0 for these policies
const okl_long r = okl::sys(okl::nr_sched_setscheduler, 0, policy, reinterpret_cast<okl_long>(&param));
return okl::failed(r) ? okl::translate(r) : kal_ok;
}

}
Loading