git: af3c78886fd8 - main - Alter the prototype of qsort_r(3) to match POSIX, which adopted the glibc-based interface.

From: Xin LI <delphij_at_FreeBSD.org>
Date: Fri, 30 Sep 2022 22:30:52 UTC
The branch main has been updated by delphij:

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

commit af3c78886fd8d4ca5eebdbe581a459a6f6d29d6a
Author:     Ed Schouten <ed@FreeBSD.org>
AuthorDate: 2022-09-30 22:26:30 +0000
Commit:     Xin LI <delphij@FreeBSD.org>
CommitDate: 2022-09-30 22:26:30 +0000

    Alter the prototype of qsort_r(3) to match POSIX, which adopted the
    glibc-based interface.
    
    Unfortunately, the glibc maintainers, despite knowing the existence
    of the FreeBSD qsort_r(3) interface in 2004 and refused to add the
    same interface to glibc based on grounds of the lack of standardization
    and portability concerns, has decided it was a good idea to introduce
    their own qsort_r(3) interface in 2007 as a GNU extension with a
    slightly different and incompatible interface.
    
    With the adoption of their interface as POSIX standard, let's switch
    to the same prototype, there is no need to remain incompatible.
    
    C++ and C applications written for the historical FreeBSD interface
    get source level compatibility when building in C++ mode, or when
    building with a C compiler with C11 generics support, provided that
    the caller passes a fifth parameter of qsort_r() that exactly matches
    the historical FreeBSD comparator function pointer type and does not
    redefine the historical qsort_r(3) prototype in their source code.
    
    Symbol versioning is used to keep old binaries working.
    
    MFC:                    never
    Relnotes:               yes
    Reviewed by:            cem, imp, hps, pauamma
    Differential revision:  https://reviews.freebsd.org/D17083
---
 include/stdlib.h                              | 38 ++++++++++-
 lib/libc/gen/scandir-compat11.c               |  8 +--
 lib/libc/gen/scandir.c                        |  6 +-
 lib/libc/stdlib/Makefile.inc                  |  4 +-
 lib/libc/stdlib/Symbol.map                    |  2 +-
 lib/libc/stdlib/qsort.3                       | 12 +++-
 lib/libc/stdlib/qsort.c                       | 16 ++++-
 lib/libc/stdlib/qsort_r.c                     | 11 ----
 lib/libc/stdlib/qsort_r_compat.c              | 21 ++++++
 lib/libc/tests/stdlib/Makefile                |  1 +
 lib/libc/tests/stdlib/qsort_r_compat_test.c   | 92 +++++++++++++++++++++++++++
 lib/libc/tests/stdlib/qsort_r_test.c          |  6 +-
 lib/libproc/proc_sym.c                        |  4 +-
 sys/compat/linuxkpi/common/src/linux_compat.c |  4 +-
 sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c    |  9 ++-
 sys/dev/drm2/drm_linux_list_sort.c            |  4 +-
 sys/libkern/qsort.c                           | 10 +--
 sys/netgraph/ng_ppp.c                         |  8 +--
 sys/sys/libkern.h                             |  4 +-
 19 files changed, 207 insertions(+), 53 deletions(-)

diff --git a/include/stdlib.h b/include/stdlib.h
index bf1a612190ee..754e8f5f5fd4 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -311,8 +311,8 @@ int	 mergesort_b(void *, size_t, size_t, int (^)(const void *, const void *));
 int	 mkostemp(char *, int);
 int	 mkostemps(char *, int, int);
 int	 mkostempsat(int, char *, int, int);
-void	 qsort_r(void *, size_t, size_t, void *,
-	    int (*)(void *, const void *, const void *));
+void	 qsort_r(void *, size_t, size_t,
+	    int (*)(const void *, const void *, void *), void *);
 int	 radixsort(const unsigned char **, int, const unsigned char *,
 	    unsigned);
 void	*reallocarray(void *, size_t, size_t) __result_use_check
@@ -332,6 +332,40 @@ __int64_t
 __uint64_t
 	 strtouq(const char *, char **, int);
 
+/*
+ * In FreeBSD 14, the prototype of qsort_r() was modified to comply with
+ * POSIX.  The standardized qsort_r()'s order of last two parameters was
+ * changed, and the comparator function is now taking thunk as its last
+ * parameter, and both are different from the ones expected by the historical
+ * FreeBSD qsort_r() interface.
+ *
+ * Apply a workaround where we explicitly link against the historical
+ * interface, qsort_r@FBSD_1.0, in case when qsort_r() is called with
+ * the last parameter with a function pointer that exactly matches the
+ * historical FreeBSD qsort_r() comparator signature, so applications
+ * written for the historical interface can continue to work without
+ * modification.
+ */
+#if defined(__generic) || defined(__cplusplus)
+void __qsort_r_compat(void *, size_t, size_t, void *,
+	    int (*)(void *, const void *, const void *));
+__sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0);
+#endif
+#if defined(__generic) && !defined(__cplusplus)
+#define	qsort_r(base, nel, width, arg4, arg5)				\
+    __generic(arg5, int (*)(void *, const void *, const void *),	\
+        __qsort_r_compat, qsort_r)(base, nel, width, arg4, arg5)
+#elif defined(__cplusplus)
+__END_DECLS
+extern "C++" {
+static inline void qsort_r(void *base, size_t nmemb, size_t size,
+    void *thunk, int (*compar)(void *, const void *, const void *)) {
+	__qsort_r_compat(base, nmemb, size, thunk, compar);
+}
+}
+__BEGIN_DECLS
+#endif
+
 extern char *suboptarg;			/* getsubopt(3) external variable */
 #endif /* __BSD_VISIBLE */
 
diff --git a/lib/libc/gen/scandir-compat11.c b/lib/libc/gen/scandir-compat11.c
index e6af1929a4d2..fe8d34d4b7d6 100644
--- a/lib/libc/gen/scandir-compat11.c
+++ b/lib/libc/gen/scandir-compat11.c
@@ -58,8 +58,8 @@ __FBSDID("$FreeBSD$");
 
 #define	SELECT(x)	select(x)
 
-static int freebsd11_scandir_thunk_cmp(void *thunk, const void *p1,
-    const void *p2);
+static int freebsd11_scandir_thunk_cmp(const void *p1, const void *p2,
+    void *thunk);
 
 int
 freebsd11_scandir(const char *dirname, struct freebsd11_dirent ***namelist,
@@ -116,7 +116,7 @@ freebsd11_scandir(const char *dirname, struct freebsd11_dirent ***namelist,
 	closedir(dirp);
 	if (numitems && dcomp != NULL)
 		qsort_r(names, numitems, sizeof(struct freebsd11_dirent *),
-		    &dcomp, freebsd11_scandir_thunk_cmp);
+		    freebsd11_scandir_thunk_cmp, &dcomp);
 	*namelist = names;
 	return (numitems);
 
@@ -141,7 +141,7 @@ freebsd11_alphasort(const struct freebsd11_dirent **d1,
 }
 
 static int
-freebsd11_scandir_thunk_cmp(void *thunk, const void *p1, const void *p2)
+freebsd11_scandir_thunk_cmp(const void *p1, const void *p2, void *thunk)
 {
 	int (*dc)(const struct freebsd11_dirent **, const struct
 	    freebsd11_dirent **);
diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c
index 496b1ddc29db..4f40678513dd 100644
--- a/lib/libc/gen/scandir.c
+++ b/lib/libc/gen/scandir.c
@@ -63,7 +63,7 @@ typedef DECLARE_BLOCK(int, select_block, const struct dirent *);
 typedef DECLARE_BLOCK(int, dcomp_block, const struct dirent **,
     const struct dirent **);
 #else
-static int scandir_thunk_cmp(void *thunk, const void *p1, const void *p2);
+static int scandir_thunk_cmp(const void *p1, const void *p2, void *thunk);
 #endif
 
 static int
@@ -123,7 +123,7 @@ scandir_dirp(DIR *dirp, struct dirent ***namelist,
 		qsort_b(names, numitems, sizeof(struct dirent *), (void*)dcomp);
 #else
 		qsort_r(names, numitems, sizeof(struct dirent *),
-		    &dcomp, scandir_thunk_cmp);
+		    scandir_thunk_cmp, &dcomp);
 #endif
 	*namelist = names;
 	return (numitems);
@@ -199,7 +199,7 @@ versionsort(const struct dirent **d1, const struct dirent **d2)
 }
 
 static int
-scandir_thunk_cmp(void *thunk, const void *p1, const void *p2)
+scandir_thunk_cmp(const void *p1, const void *p2, void *thunk)
 {
 	int (*dc)(const struct dirent **, const struct dirent **);
 
diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc
index a658fd78e862..8ace2c051b82 100644
--- a/lib/libc/stdlib/Makefile.inc
+++ b/lib/libc/stdlib/Makefile.inc
@@ -11,8 +11,8 @@ MISRCS+=C99_Exit.c a64l.c abort.c abs.c atexit.c atof.c atoi.c atol.c atoll.c \
 	getsubopt.c hcreate.c hcreate_r.c hdestroy_r.c heapsort.c heapsort_b.c \
 	hsearch_r.c imaxabs.c imaxdiv.c \
 	insque.c l64a.c labs.c ldiv.c llabs.c lldiv.c lsearch.c \
-	merge.c mergesort_b.c ptsname.c qsort.c qsort_r.c qsort_s.c \
-	quick_exit.c radixsort.c rand.c \
+	merge.c mergesort_b.c ptsname.c qsort.c qsort_r.c qsort_r_compat.c \
+	qsort_s.c quick_exit.c radixsort.c rand.c \
 	random.c reallocarray.c reallocf.c realpath.c remque.c \
 	set_constraint_handler_s.c strfmon.c strtoimax.c \
 	strtol.c strtold.c strtoll.c strtoq.c strtoul.c strtonum.c strtoull.c \
diff --git a/lib/libc/stdlib/Symbol.map b/lib/libc/stdlib/Symbol.map
index 6524c6097b96..7e0b141a21b6 100644
--- a/lib/libc/stdlib/Symbol.map
+++ b/lib/libc/stdlib/Symbol.map
@@ -49,7 +49,6 @@ FBSD_1.0 {
 	lfind;
 	mergesort;
 	putenv;
-	qsort_r;
 	qsort;
 	radixsort;
 	sradixsort;
@@ -130,6 +129,7 @@ FBSD_1.6 {
 
 FBSD_1.7 {
 	clearenv;
+	qsort_r;
 };
 
 FBSDprivate_1.0 {
diff --git a/lib/libc/stdlib/qsort.3 b/lib/libc/stdlib/qsort.3
index 606185f9baee..6449849fc490 100644
--- a/lib/libc/stdlib/qsort.3
+++ b/lib/libc/stdlib/qsort.3
@@ -32,7 +32,7 @@
 .\"     @(#)qsort.3	8.1 (Berkeley) 6/4/93
 .\" $FreeBSD$
 .\"
-.Dd January 20, 2020
+.Dd September 30, 2022
 .Dt QSORT 3
 .Os
 .Sh NAME
@@ -67,8 +67,8 @@
 .Fa "void *base"
 .Fa "size_t nmemb"
 .Fa "size_t size"
+.Fa "int \*[lp]*compar\*[rp]\*[lp]const void *, const void *, void *\*[rp]"
 .Fa "void *thunk"
-.Fa "int \*[lp]*compar\*[rp]\*[lp]void *, const void *, const void *\*[rp]"
 .Fc
 .Ft int
 .Fo heapsort
@@ -157,7 +157,7 @@ function behaves identically to
 .Fn qsort ,
 except that it takes an additional argument,
 .Fa thunk ,
-which is passed unchanged as the first argument to function pointed to
+which is passed unchanged as the last argument to function pointed to
 .Fa compar .
 This allows the comparison function to access additional
 data without using global variables, and thus
@@ -436,3 +436,9 @@ K.3.6.3.2.
 The variants of these functions that take blocks as arguments first appeared in
 Mac OS X.
 This implementation was created by David Chisnall.
+.Pp
+In
+.Fx 14.0 ,
+the prototype of
+.Fn qsort_r
+was updated to match POSIX.
diff --git a/lib/libc/stdlib/qsort.c b/lib/libc/stdlib/qsort.c
index 0b99c04507d3..11a3c5508143 100644
--- a/lib/libc/stdlib/qsort.c
+++ b/lib/libc/stdlib/qsort.c
@@ -42,6 +42,8 @@ __FBSDID("$FreeBSD$");
 #include "libc_private.h"
 
 #if defined(I_AM_QSORT_R)
+typedef int		 cmp_t(const void *, const void *, void *);
+#elif defined(I_AM_QSORT_R_COMPAT)
 typedef int		 cmp_t(void *, const void *, const void *);
 #elif defined(I_AM_QSORT_S)
 typedef int		 cmp_t(const void *, const void *, void *);
@@ -72,6 +74,8 @@ swapfunc(char *a, char *b, size_t es)
 	if ((n) > 0) swapfunc(a, b, n)
 
 #if defined(I_AM_QSORT_R)
+#define	CMP(t, x, y) (cmp((x), (y), (t)))
+#elif defined(I_AM_QSORT_R_COMPAT)
 #define	CMP(t, x, y) (cmp((t), (x), (y)))
 #elif defined(I_AM_QSORT_S)
 #define	CMP(t, x, y) (cmp((x), (y), (t)))
@@ -81,7 +85,7 @@ swapfunc(char *a, char *b, size_t es)
 
 static inline char *
 med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk
-#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_S)
+#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_R_COMPAT) && !defined(I_AM_QSORT_S)
 __unused
 #endif
 )
@@ -97,6 +101,8 @@ __unused
  */
 #if defined(I_AM_QSORT_R)
 #define local_qsort local_qsort_r
+#elif defined(I_AM_QSORT_R_COMPAT)
+#define local_qsort local_qsort_r_compat
 #elif defined(I_AM_QSORT_S)
 #define local_qsort local_qsort_s
 #endif
@@ -211,10 +217,16 @@ loop:
 
 #if defined(I_AM_QSORT_R)
 void
-qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp)
+(qsort_r)(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk)
 {
 	local_qsort_r(a, n, es, cmp, thunk);
 }
+#elif defined(I_AM_QSORT_R_COMPAT)
+void
+__qsort_r_compat(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp)
+{
+	local_qsort_r_compat(a, n, es, cmp, thunk);
+}
 #elif defined(I_AM_QSORT_S)
 errno_t
 qsort_s(void *a, rsize_t n, rsize_t es, cmp_t *cmp, void *thunk)
diff --git a/lib/libc/stdlib/qsort_r.c b/lib/libc/stdlib/qsort_r.c
index f489d31c2335..d86873604f59 100644
--- a/lib/libc/stdlib/qsort_r.c
+++ b/lib/libc/stdlib/qsort_r.c
@@ -4,16 +4,5 @@
  *
  * $FreeBSD$
  */
-#include "block_abi.h"
 #define I_AM_QSORT_R
 #include "qsort.c"
-
-typedef DECLARE_BLOCK(int, qsort_block, const void *, const void *);
-
-void
-qsort_b(void *base, size_t nel, size_t width, qsort_block compar)
-{
-	qsort_r(base, nel, width, compar,
-		(int (*)(void *, const void *, const void *))
-		GET_BLOCK_FUNCTION(compar));
-}
diff --git a/lib/libc/stdlib/qsort_r_compat.c b/lib/libc/stdlib/qsort_r_compat.c
new file mode 100644
index 000000000000..239a5f307ceb
--- /dev/null
+++ b/lib/libc/stdlib/qsort_r_compat.c
@@ -0,0 +1,21 @@
+/*
+ * This file is in the public domain.  Originally written by Garrett
+ * A. Wollman.
+ *
+ * $FreeBSD$
+ */
+#include "block_abi.h"
+#define I_AM_QSORT_R_COMPAT
+#include "qsort.c"
+
+typedef DECLARE_BLOCK(int, qsort_block, const void *, const void *);
+
+void
+qsort_b(void *base, size_t nel, size_t width, qsort_block compar)
+{
+	__qsort_r_compat(base, nel, width, compar,
+		(int (*)(void *, const void *, const void *))
+		GET_BLOCK_FUNCTION(compar));
+}
+
+__sym_compat(qsort_r, __qsort_r_compat, FBSD_1.0);
diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile
index 47841a92ba32..df0ecc66b067 100644
--- a/lib/libc/tests/stdlib/Makefile
+++ b/lib/libc/tests/stdlib/Makefile
@@ -8,6 +8,7 @@ ATF_TESTS_C+=		heapsort_test
 ATF_TESTS_C+=		mergesort_test
 ATF_TESTS_C+=		qsort_test
 ATF_TESTS_C+=		qsort_b_test
+ATF_TESTS_C+=		qsort_r_compat_test
 ATF_TESTS_C+=		qsort_r_test
 ATF_TESTS_C+=		qsort_s_test
 ATF_TESTS_C+=		set_constraint_handler_s_test
diff --git a/lib/libc/tests/stdlib/qsort_r_compat_test.c b/lib/libc/tests/stdlib/qsort_r_compat_test.c
new file mode 100644
index 000000000000..84fd1b116b82
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_r_compat_test.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (C) 2020 Edward Tomasz Napierala <trasz@FreeBSD.org>
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for historical qsort_r(3) routine.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+#define	THUNK 42
+
+static int
+sorthelp_r(void *thunk, const void *a, const void *b)
+{
+	const int *oa, *ob;
+
+	ATF_REQUIRE_EQ(*(int *)thunk, THUNK);
+
+	oa = a;
+	ob = b;
+	/* Don't use "return *oa - *ob" since it's easy to cause overflow! */
+	if (*oa > *ob)
+		return (1);
+	if (*oa < *ob)
+		return (-1);
+	return (0);
+}
+
+ATF_TC_WITHOUT_HEAD(qsort_r_compat_test);
+ATF_TC_BODY(qsort_r_compat_test, tc)
+{
+	int testvector[IVEC_LEN];
+	int sresvector[IVEC_LEN];
+	int i, j;
+	int thunk = THUNK;
+
+	for (j = 2; j < IVEC_LEN; j++) {
+		/* Populate test vectors */
+		for (i = 0; i < j; i++)
+			testvector[i] = sresvector[i] = initvector[i];
+
+		/* Sort using qsort_r(3) */
+		qsort_r(testvector, j, sizeof(testvector[0]), &thunk,
+		    sorthelp_r);
+		/* Sort using reference slow sorting routine */
+		ssort(sresvector, j);
+
+		/* Compare results */
+		for (i = 0; i < j; i++)
+			ATF_CHECK_MSG(testvector[i] == sresvector[i],
+			    "item at index %d didn't match: %d != %d",
+			    i, testvector[i], sresvector[i]);
+	}
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+	ATF_TP_ADD_TC(tp, qsort_r_compat_test);
+
+	return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_r_test.c b/lib/libc/tests/stdlib/qsort_r_test.c
index c27e6d92d587..c55563eaea8a 100644
--- a/lib/libc/tests/stdlib/qsort_r_test.c
+++ b/lib/libc/tests/stdlib/qsort_r_test.c
@@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$");
 #define	THUNK 42
 
 static int
-sorthelp_r(void *thunk, const void *a, const void *b)
+sorthelp_r(const void *a, const void *b, void *thunk)
 {
 	const int *oa, *ob;
 
@@ -70,8 +70,8 @@ ATF_TC_BODY(qsort_r_test, tc)
 			testvector[i] = sresvector[i] = initvector[i];
 
 		/* Sort using qsort_r(3) */
-		qsort_r(testvector, j, sizeof(testvector[0]), &thunk,
-		    sorthelp_r);
+		qsort_r(testvector, j, sizeof(testvector[0]), sorthelp_r,
+		    &thunk);
 		/* Sort using reference slow sorting routine */
 		ssort(sresvector, j);
 
diff --git a/lib/libproc/proc_sym.c b/lib/libproc/proc_sym.c
index 27b0fc15a2ab..fdcf133d8457 100644
--- a/lib/libproc/proc_sym.c
+++ b/lib/libproc/proc_sym.c
@@ -105,7 +105,7 @@ struct symsort_thunk {
 };
 
 static int
-symvalcmp(void *_thunk, const void *a1, const void *a2)
+symvalcmp(const void *a1, const void *a2, void *_thunk)
 {
 	GElf_Sym sym1, sym2;
 	struct symsort_thunk *thunk;
@@ -192,7 +192,7 @@ load_symtab(Elf *e, struct symtab *symtab, u_long sh_type)
 
 	thunk.e = e;
 	thunk.symtab = symtab;
-	qsort_r(symtab->index, nsyms, sizeof(u_int), &thunk, symvalcmp);
+	qsort_r(symtab->index, nsyms, sizeof(u_int), symvalcmp, &thunk);
 
 	return (0);
 }
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index 6f1def64fa0f..8c5d2e4ecd16 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -2562,7 +2562,7 @@ struct list_sort_thunk {
 };
 
 static inline int
-linux_le_cmp(void *priv, const void *d1, const void *d2)
+linux_le_cmp(const void *d1, const void *d2, void *priv)
 {
 	struct list_head *le1, *le2;
 	struct list_sort_thunk *thunk;
@@ -2590,7 +2590,7 @@ list_sort(void *priv, struct list_head *head, int (*cmp)(void *priv,
 		ar[i++] = le;
 	thunk.cmp = cmp;
 	thunk.priv = priv;
-	qsort_r(ar, count, sizeof(struct list_head *), &thunk, linux_le_cmp);
+	qsort_r(ar, count, sizeof(struct list_head *), linux_le_cmp, &thunk);
 	INIT_LIST_HEAD(head);
 	for (i = 0; i < count; i++)
 		list_add_tail(ar[i], head);
diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c b/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
index 730b9d51da45..fd05648147f5 100644
--- a/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
+++ b/sys/dev/bhnd/nvram/bhnd_nvram_store_subr.c
@@ -59,8 +59,7 @@ __FBSDID("$FreeBSD$");
 
 #include "bhnd_nvram_storevar.h"
 
-static int			 bhnd_nvstore_idx_cmp(void *ctx,
-				     const void *lhs, const void *rhs);
+static int bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx);
 
 /**
  * Allocate and initialize a new path instance.
@@ -198,7 +197,7 @@ bhnd_nvstore_index_append(struct bhnd_nvram_store *sc,
 
 /* sort function for bhnd_nvstore_index_prepare() */
 static int
-bhnd_nvstore_idx_cmp(void *ctx, const void *lhs, const void *rhs)
+bhnd_nvstore_idx_cmp(const void *lhs, const void *rhs, void *ctx)
 {
 	struct bhnd_nvram_store	*sc;
 	void			*l_cookiep, *r_cookiep;
@@ -259,8 +258,8 @@ bhnd_nvstore_index_prepare(struct bhnd_nvram_store *sc,
 	BHND_NVSTORE_LOCK_ASSERT(sc, MA_OWNED);
 
 	/* Sort the index table */
-	qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]), sc,
-	    bhnd_nvstore_idx_cmp);
+	qsort_r(index->cookiep, index->count, sizeof(index->cookiep[0]),
+	    bhnd_nvstore_idx_cmp, sc);
 
 	return (0);
 }
diff --git a/sys/dev/drm2/drm_linux_list_sort.c b/sys/dev/drm2/drm_linux_list_sort.c
index f9a64154c796..04cdf28180c8 100644
--- a/sys/dev/drm2/drm_linux_list_sort.c
+++ b/sys/dev/drm2/drm_linux_list_sort.c
@@ -37,7 +37,7 @@ struct drm_list_sort_thunk {
 };
 
 static int
-drm_le_cmp(void *priv, const void *d1, const void *d2)
+drm_le_cmp(const void *d1, const void *d2, void *priv)
 {
 	struct list_head *le1, *le2;
 	struct drm_list_sort_thunk *thunk;
@@ -68,7 +68,7 @@ drm_list_sort(void *priv, struct list_head *head, int (*cmp)(void *priv,
 		ar[i++] = le;
 	thunk.cmp = cmp;
 	thunk.priv = priv;
-	qsort_r(ar, count, sizeof(struct list_head *), &thunk, drm_le_cmp);
+	qsort_r(ar, count, sizeof(struct list_head *), drm_le_cmp, &thunk);
 	INIT_LIST_HEAD(head);
 	for (i = 0; i < count; i++)
 		list_add_tail(ar[i], head);
diff --git a/sys/libkern/qsort.c b/sys/libkern/qsort.c
index 7602127a59d6..66ca826e265c 100644
--- a/sys/libkern/qsort.c
+++ b/sys/libkern/qsort.c
@@ -36,7 +36,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/libkern.h>
 
 #ifdef I_AM_QSORT_R
-typedef int		 cmp_t(void *, const void *, const void *);
+typedef int		 cmp_t(const void *, const void *, void *);
 #else
 typedef int		 cmp_t(const void *, const void *);
 #endif
@@ -88,7 +88,7 @@ swapfunc(char *a, char *b, size_t n, int swaptype_long, int swaptype_int)
 	if ((n) > 0) swapfunc(a, b, n, swaptype_long, swaptype_int)
 
 #ifdef I_AM_QSORT_R
-#define	CMP(t, x, y) (cmp((t), (x), (y)))
+#define	CMP(t, x, y) (cmp((x), (y), (t)))
 #else
 #define	CMP(t, x, y) (cmp((x), (y)))
 #endif
@@ -107,7 +107,7 @@ __unused
 
 #ifdef I_AM_QSORT_R
 void
-qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp)
+(qsort_r)(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk)
 #else
 #define	thunk NULL
 void
@@ -192,7 +192,7 @@ loop:	SWAPINIT(long, a, es);
 		/* Recurse on left partition, then iterate on right partition */
 		if (d1 > es) {
 #ifdef I_AM_QSORT_R
-			qsort_r(a, d1 / es, es, thunk, cmp);
+			qsort_r(a, d1 / es, es, cmp, thunk);
 #else
 			qsort(a, d1 / es, es, cmp);
 #endif
@@ -208,7 +208,7 @@ loop:	SWAPINIT(long, a, es);
 		/* Recurse on right partition, then iterate on left partition */
 		if (d2 > es) {
 #ifdef I_AM_QSORT_R
-			qsort_r(pn - d2, d2 / es, es, thunk, cmp);
+			qsort_r(pn - d2, d2 / es, es, cmp, thunk);
 #else
 			qsort(pn - d2, d2 / es, es, cmp);
 #endif
diff --git a/sys/netgraph/ng_ppp.c b/sys/netgraph/ng_ppp.c
index 6c61bcda0ae3..aafed858a26b 100644
--- a/sys/netgraph/ng_ppp.c
+++ b/sys/netgraph/ng_ppp.c
@@ -322,7 +322,7 @@ static void	ng_ppp_frag_timeout(node_p node, hook_p hook, void *arg1,
 static void	ng_ppp_frag_checkstale(node_p node);
 static void	ng_ppp_frag_reset(node_p node);
 static void	ng_ppp_mp_strategy(node_p node, int len, int *distrib);
-static int	ng_ppp_intcmp(void *latency, const void *v1, const void *v2);
+static int	ng_ppp_intcmp(const void *v1, const void *v2, void *latency);
 static struct mbuf *ng_ppp_addproto(struct mbuf *m, uint16_t proto, int compOK);
 static struct mbuf *ng_ppp_cutproto(struct mbuf *m, uint16_t *proto);
 static struct mbuf *ng_ppp_prepend(struct mbuf *m, const void *buf, int len);
@@ -2316,8 +2316,8 @@ ng_ppp_mp_strategy(node_p node, int len, int *distrib)
 	}
 
 	/* Sort active links by latency */
-	qsort_r(sortByLatency,
-	    priv->numActiveLinks, sizeof(*sortByLatency), latency, ng_ppp_intcmp);
+	qsort_r(sortByLatency, priv->numActiveLinks, sizeof(*sortByLatency),
+	    ng_ppp_intcmp, latency);
 
 	/* Find the interval we need (add links in sortByLatency[] order) */
 	for (numFragments = 1;
@@ -2401,7 +2401,7 @@ ng_ppp_mp_strategy(node_p node, int len, int *distrib)
  * Compare two integers
  */
 static int
-ng_ppp_intcmp(void *latency, const void *v1, const void *v2)
+ng_ppp_intcmp(const void *v1, const void *v2, void *latency)
 {
 	const int index1 = *((const int *) v1);
 	const int index2 = *((const int *) v2);
diff --git a/sys/sys/libkern.h b/sys/sys/libkern.h
index 41844fa7490e..8adeeb499984 100644
--- a/sys/sys/libkern.h
+++ b/sys/sys/libkern.h
@@ -163,8 +163,8 @@ void	*memcchr(const void *s, int c, size_t n);
 void	*memmem(const void *l, size_t l_len, const void *s, size_t s_len);
 void	 qsort(void *base, size_t nmemb, size_t size,
 	    int (*compar)(const void *, const void *));
-void	 qsort_r(void *base, size_t nmemb, size_t size, void *thunk,
-	    int (*compar)(void *, const void *, const void *));
+void	 qsort_r(void *base, size_t nmemb, size_t size,
+	    int (*compar)(const void *, const void *, void *), void *thunk);
 u_long	 random(void);
 int	 scanc(u_int, const u_char *, const u_char *, int);
 int	 strcasecmp(const char *, const char *);