git: e45c7bc6115e - main - linux: implement sched_getattr()

From: Enji Cooper <ngie_at_FreeBSD.org>
Date: Mon, 19 Oct 2026 20:27:42 UTC
The branch main has been updated by ngie:

URL: https://cgit.FreeBSD.org/src/commit/?id=e45c7bc6115ef3cd9567a397e1351c608c0269a7

commit e45c7bc6115ef3cd9567a397e1351c608c0269a7
Author:     Enji Cooper <ngie@FreeBSD.org>
AuthorDate: 2026-10-10 18:18:23 +0000
Commit:     Enji Cooper <ngie@FreeBSD.org>
CommitDate: 2026-10-19 20:27:24 +0000

    linux: implement sched_getattr()
    
    This change adds the necessary glue to translate Linux scheduler policy
    and priority from FreeBSD so this information is available to Linux
    applications.
    
    Please note that this change does not fill in the `.sched_nice` property: this
    will be handled in a follow up commit (see bug 299617 for more details).
    
    This unbreaks using the Linux version of Chromium on FreeBSD.
    
    PR:     297468
    Signed-off-by:  Joao Bonifacio <joaoboni017@gmail.com>
    Pull Request: https://github.com/freebsd/freebsd-src/pull/2370
    Reviewed by:    olce
    Differential Revision:  https://reviews.freebsd.org/D60214
---
 sys/compat/linux/linux_dummy.c |   1 -
 sys/compat/linux/linux_misc.c  | 112 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 112 insertions(+), 1 deletion(-)

diff --git a/sys/compat/linux/linux_dummy.c b/sys/compat/linux/linux_dummy.c
index dfb1c90e9abf..873e2cef3879 100644
--- a/sys/compat/linux/linux_dummy.c
+++ b/sys/compat/linux/linux_dummy.c
@@ -97,7 +97,6 @@ DUMMY(process_vm_writev);
 /* Linux 3.8: */
 DUMMY(finit_module);
 DUMMY(sched_setattr);
-DUMMY(sched_getattr);
 /* Linux 3.18: */
 DUMMY(bpf);
 /* Linux 3.19: */
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 65c9044dd3bd..e9441a31e8ac 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -29,6 +29,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
 #include <sys/param.h>
 #include <sys/fcntl.h>
 #include <sys/jail.h>
@@ -116,6 +117,41 @@ struct l_pselect6arg {
 	l_size_t	ss_len;
 };
 
+/**
+ * `SCHED_ATTR_SIZE_VER0` represents the size of the initial published structure.
+ *
+ * See `linux/kernel/sched/syscalls.c` for more details.
+ */
+#define LINUX_SCHED_ATTR_SIZE_VER0 48
+/**
+ * Linux struct sched_attr
+ */
+struct linux_sched_attr {
+	l_uint 		size;
+	l_uint 		sched_policy;
+	uint64_t 	sched_flags;
+	int32_t  	sched_nice;
+	l_uint 		sched_priority;
+
+	uint64_t 	sched_runtime;
+	uint64_t 	sched_deadline;
+	uint64_t 	sched_period;
+
+	l_uint 		sched_util_min;
+	l_uint 		sched_util_max;
+};
+/**
+ * `.size` must be checked at runtime as well in the syscall to match the
+ * Linux implementation.
+ *
+ * This `_Static_assert(..)` is a sanity check to ensure that the structure
+ * meets the minimum expected bar.
+ */
+_Static_assert(sizeof(struct linux_sched_attr) >= LINUX_SCHED_ATTR_SIZE_VER0,
+    "struct linux_sched_attr size");
+
+static int	bsd_sched_policy_to_linux_sched_policy(int);
+
 static int	linux_utimensat_lts_to_ts(struct l_timespec *,
 			struct timespec *);
 #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
@@ -1350,6 +1386,82 @@ linux_sched_getscheduler(struct thread *td,
 	return (error);
 }
 
+static int
+bsd_sched_policy_to_linux_sched_policy(int bsd_policy)
+{
+
+	switch (bsd_policy) {
+	case SCHED_FIFO:
+		return (LINUX_SCHED_FIFO);
+	case SCHED_RR:
+		return (LINUX_SCHED_RR);
+	case SCHED_OTHER:
+		/* FALLTHROUGH */
+	default:
+		return (LINUX_SCHED_OTHER);
+	}
+}
+
+int
+linux_sched_getattr(struct thread *td, struct linux_sched_getattr_args *args)
+{
+	struct linux_sched_attr lattr;
+	struct sched_param bsd;
+	struct thread *tdt;
+	int error, policy;
+
+	if (args->attr == NULL || args->pid < 0 || args->flags != 0 ||
+	    args->size < LINUX_SCHED_ATTR_SIZE_VER0 || args->size > PAGE_SIZE)
+		return (EINVAL);
+
+	tdt = linux_tdfind(td, args->pid, -1);
+	if (tdt == NULL)
+		return (ESRCH);
+
+	error = kern_sched_getscheduler(td, tdt, &policy);
+	if (error != 0) {
+		PROC_UNLOCK(tdt->td_proc);
+		return (error);
+	}
+
+	error = kern_sched_getparam(td, tdt, &bsd);
+	PROC_UNLOCK(tdt->td_proc);
+	if (error != 0)
+		return (error);
+
+	memset(&lattr, 0, sizeof(lattr));
+	lattr.size = MIN(args->size, sizeof(lattr));
+	lattr.sched_policy = bsd_sched_policy_to_linux_sched_policy(policy);
+	/*
+	 * TODO(PR#299617,ngie): fill in `.sched_nice` with policies like
+	 * `LINUX_SCHED_NORMAL`, etc.
+	 */
+
+	if (linux_map_sched_prio) {
+		switch (lattr.sched_policy) {
+		case LINUX_SCHED_OTHER:
+			lattr.sched_priority = 0;
+			break;
+		case LINUX_SCHED_FIFO:
+		case LINUX_SCHED_RR:
+			/**
+			 * Map [0, RTP_PRIO_MAX - RTP_PRIO_MIN] to
+			 * [1, LINUX_MAX_RT_PRIO - 1] (rounding up).
+			 */
+			lattr.sched_priority =
+			    (bsd.sched_priority *
+				    (LINUX_MAX_RT_PRIO - 1) +
+				(RTP_PRIO_MAX - RTP_PRIO_MIN - 1)) /
+				(RTP_PRIO_MAX - RTP_PRIO_MIN) + 1;
+			break;
+		}
+	} else {
+		lattr.sched_priority = bsd.sched_priority;
+	}
+
+	return (copyout(&lattr, args->attr, lattr.size));
+}
+
 int
 linux_sched_get_priority_max(struct thread *td,
     struct linux_sched_get_priority_max_args *args)