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
+}