Add sched_getattr()/sched_setattr().
Still a questionable ABI (because the struct changes size over time without changing its name), but it's in glibc 2.41, so my assumption is that _not_ having it is likely to be more disruptive than the ABI issues going forward. (Hopefully no-one was planning on passing these structs between separately compiled libraries anyway!)
Bug: http://b/183240349
Change-Id: I80c39900b70af7e84913e547f38f656efa3e16ec
diff --git a/docs/status.md b/docs/status.md
index e7111d9..034f115 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -59,6 +59,9 @@
Current libc symbols: https://android.googlesource.com/platform/bionic/+/main/libc/libc.map.txt
+New libc functions in API level 37:
+ * New system call wrappers: `sched_getattr()`/`sched_setattr()` (`<sched.h>`).
+
New libc functions in API level 36:
* `qsort_r`, `sig2str`/`str2sig` (POSIX Issue 8 additions).
* GNU/BSD extension `lchmod`.
diff --git a/libc/SECCOMP_ALLOWLIST_COMMON.TXT b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
index 1d58475..5594910 100644
--- a/libc/SECCOMP_ALLOWLIST_COMMON.TXT
+++ b/libc/SECCOMP_ALLOWLIST_COMMON.TXT
@@ -49,9 +49,6 @@
int io_submit(aio_context_t, long, iocb**) all
int io_getevents(aio_context_t, long, long, io_event*, timespec*) all
int io_cancel(aio_context_t, iocb*, io_event*) all
-# Since Linux 3.14, not in glibc.
-int sched_getattr(pid_t, sched_attr*, unsigned) all
-int sched_setattr(pid_t, sched_attr*, unsigned, unsigned) all
# Since Linux 3.19, not in glibc (and not really needed to implement fexecve).
int execveat(int, const char*, char* const*, char* const*, int) all
# Since Linux 4.3, not in glibc. Probed for and conditionally used by ART.
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index 8c5572e..1506e13 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -293,19 +293,18 @@
int __sendmmsg:socketcall:20(int, struct mmsghdr*, unsigned int, int) x86
# scheduler & real-time
-int sched_setscheduler(pid_t pid, int policy, const struct sched_param* param) all
-int sched_getscheduler(pid_t pid) all
-int sched_yield(void) all
-int sched_setparam(pid_t pid, const struct sched_param* param) all
-int sched_getparam(pid_t pid, struct sched_param* param) all
-int sched_get_priority_max(int policy) all
-int sched_get_priority_min(int policy) all
-int sched_rr_get_interval(pid_t pid, struct timespec* interval) all
-int sched_setaffinity(pid_t pid, size_t setsize, const cpu_set_t* set) all
-int setns(int, int) all
-int unshare(int) all
-int __sched_getaffinity:sched_getaffinity(pid_t pid, size_t setsize, cpu_set_t* set) all
-int __getcpu:getcpu(unsigned*, unsigned*, void*) all
+int sched_get_priority_max(int policy) all
+int sched_get_priority_min(int policy) all
+int __sched_getaffinity:sched_getaffinity(pid_t, size_t, cpu_set_t*) all
+int sched_getattr(pid_t, sched_attr*, unsigned, unsigned) all
+int sched_getparam(pid_t, sched_param*) all
+int sched_getscheduler(pid_t) all
+int sched_rr_get_interval(pid_t, timespec*) all
+int sched_setaffinity(pid_t, size_t, const cpu_set_t*) all
+int sched_setattr(pid_t, sched_attr*, unsigned) all
+int sched_setparam(pid_t, const sched_param*) all
+int sched_setscheduler(pid_t, int, const sched_param*) all
+int sched_yield(void) all
# other
int uname(struct utsname*) all
@@ -317,6 +316,11 @@
int sysinfo(struct sysinfo*) all
int personality(unsigned long) all
+int setns(int, int) all
+int unshare(int) all
+
+int __getcpu:getcpu(unsigned*, unsigned*, void*) all
+
int bpf(int, union bpf_attr *, unsigned int) all
ssize_t tee(int, int, size_t, unsigned int) all
diff --git a/libc/include/sched.h b/libc/include/sched.h
index 58dd9a6..c68ebf0 100644
--- a/libc/include/sched.h
+++ b/libc/include/sched.h
@@ -37,6 +37,7 @@
#include <bits/timespec.h>
#include <linux/sched.h>
+#include <linux/sched/types.h>
__BEGIN_DECLS
@@ -236,6 +237,22 @@
int sched_getaffinity(pid_t __pid, size_t __set_size, cpu_set_t* _Nonnull __set);
/**
+ * [sched_setattr(2)](https://man7.org/linux/man-pages/man2/sched_setattr.2.html)
+ * sets the scheduling attributes for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int sched_setattr(pid_t __pid, struct sched_attr* _Nonnull __attr, unsigned __flags) __INTRODUCED_IN(37);
+
+/**
+ * [sched_getattr(2)](https://man7.org/linux/man-pages/man2/sched_getattr.2.html)
+ * gets the scheduling attributes for the given thread.
+ *
+ * Returns 0 on success and returns -1 and sets `errno` on failure.
+ */
+int sched_getattr(pid_t __pid, struct sched_attr* _Nonnull __attr, unsigned __size, unsigned __flags) __INTRODUCED_IN(37);
+
+/**
* [CPU_ZERO](https://man7.org/linux/man-pages/man3/CPU_ZERO.3.html) clears all
* bits in a static CPU set.
*/
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 86dcc39..3e9f850 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1619,6 +1619,12 @@
str2sig;
} LIBC_V;
+LIBC_37 { # introduced=37
+ global:
+ sched_getattr;
+ sched_setattr;
+} LIBC_36;
+
LIBC_PRIVATE {
global:
__accept4; # arm x86
diff --git a/tests/sched_test.cpp b/tests/sched_test.cpp
index 448fae9..4009e1d 100644
--- a/tests/sched_test.cpp
+++ b/tests/sched_test.cpp
@@ -165,7 +165,6 @@
}
}
-
TEST(sched, cpu_alloc_small) {
cpu_set_t* set = CPU_ALLOC(17);
size_t size = CPU_ALLOC_SIZE(17);
@@ -313,7 +312,7 @@
#pragma clang diagnostic pop
}
-TEST(pthread, sched_getaffinity) {
+TEST(sched, sched_getaffinity) {
cpu_set_t set;
CPU_ZERO(&set);
ASSERT_EQ(0, sched_getaffinity(getpid(), sizeof(set), &set));
@@ -329,7 +328,7 @@
#pragma clang diagnostic pop
}
-TEST(pthread, sched_setaffinity) {
+TEST(sched, sched_setaffinity) {
cpu_set_t set;
CPU_ZERO(&set);
ASSERT_EQ(0, sched_getaffinity(getpid(), sizeof(set), &set));
@@ -337,3 +336,25 @@
// but it ought to be safe to ask for the same affinity you already have.
ASSERT_EQ(0, sched_setaffinity(getpid(), sizeof(set), &set));
}
+
+TEST(sched, sched_getattr) {
+#if defined(__BIONIC__)
+ struct sched_attr sa;
+ ASSERT_EQ(0, sched_getattr(getpid(), &sa, sizeof(sa), 0));
+#else
+ GTEST_SKIP() << "our glibc is too old";
+#endif
+}
+
+TEST(sched, sched_setattr_failure) {
+#if defined(__BIONIC__)
+ // Trivial test of the errno-preserving/returning behavior.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
+ ASSERT_EQ(-1, sched_setattr(getpid(), nullptr, 0));
+ ASSERT_ERRNO(EINVAL);
+#pragma clang diagnostic pop
+#else
+ GTEST_SKIP() << "our glibc is too old";
+#endif
+}