summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2020-09-24 11:44:26 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2020-09-24 11:44:26 +0000
commit03ba8404acefe94916ad776068d4e3f2f585a224 (patch)
treea03aa8d784abb5e77cba57219dca3d36ed049e2d /usr/src
parent2bfe7a031d325ccd2e9758ba6db0e35bcaefa1b3 (diff)
parent6d40a71ead689b14c68d91a5d92845e5f3daa020 (diff)
downloadillumos-joyent-03ba8404acefe94916ad776068d4e3f2f585a224.tar.gz
[illumos-gate merge]
commit 6d40a71ead689b14c68d91a5d92845e5f3daa020 1532 Long-term kernel-resident processes need a way to play fair commit cfd17c15945080ff766acfba4bfbc0ac4d2d31cd 13096 xnf asleep at wheel while freemem smashes into the ground commit baf00aa88d7d535ed115175b04253f5db99a7d0b 13094 systems have more kmem caches than they used to commit 727737b40e05e03cb1b298a96f67258b116ba990 13082 pageout needs a deadman commit 44431c82ebd7ee1d7c240683235e728d70d96cf2 3763 Implement qsort_r(3C) Conflicts: usr/src/uts/common/os/vm_pageout.c
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/common/util/qsort.c74
-rw-r--r--usr/src/head/stdlib.h4
-rw-r--r--usr/src/lib/libc/port/mapfile-vers5
-rw-r--r--usr/src/man/man3c/Makefile3
-rw-r--r--usr/src/man/man3c/qsort.3c188
-rw-r--r--usr/src/pkg/manifests/system-library.man3c.inc1
-rw-r--r--usr/src/pkg/manifests/system-test-libctest.mf4
-rw-r--r--usr/src/test/libc-tests/cfg/symbols/stdlib_h.cfg32
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run1
-rw-r--r--usr/src/test/libc-tests/tests/Makefile1
-rw-r--r--usr/src/test/libc-tests/tests/qsort/Makefile22
-rw-r--r--usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE47
-rw-r--r--usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/test/libc-tests/tests/qsort/antiqsort.c58
-rw-r--r--usr/src/test/libc-tests/tests/qsort/merge.c336
-rw-r--r--usr/src/test/libc-tests/tests/qsort/qsort_test.c790
-rw-r--r--usr/src/uts/common/os/clock.c6
-rw-r--r--usr/src/uts/common/os/kmem.c16
-rw-r--r--usr/src/uts/common/os/vm_pageout.c84
-rw-r--r--usr/src/uts/common/vm/vm_page.c18
-rw-r--r--usr/src/uts/common/xen/io/xnf.c73
-rw-r--r--usr/src/uts/common/xen/io/xnf.h4
22 files changed, 1591 insertions, 177 deletions
diff --git a/usr/src/common/util/qsort.c b/usr/src/common/util/qsort.c
index ce2a596554..78967c0ac6 100644
--- a/usr/src/common/util/qsort.c
+++ b/usr/src/common/util/qsort.c
@@ -22,10 +22,9 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2020 Oxide Computer Company
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#if !defined(_KERNEL) && !defined(_KMDB)
#include "lint.h"
#endif /* !_KERNEL && !_KMDB */
@@ -44,15 +43,32 @@ static void swapp64(uint64_t *r1, uint64_t *r2, size_t cnt);
static void swapi(uint32_t *r1, uint32_t *r2, size_t cnt);
static void swapb(char *r1, char *r2, size_t cnt);
+typedef int (*cmp_f)(const void *, const void *, void *);
+
/*
* choose a median of 3 values
- *
- * note: cstyle specifically prohibits nested conditional operators
- * but this is the only way to do the median of 3 function in-line
*/
-#define med3(a, b, c) (cmp((a), (b)) < 0) \
- ? ((cmp((b), (c)) < 0) ? (b) : (cmp((a), (c)) < 0) ? (c) : (a)) \
- : ((cmp((b), (c)) > 0) ? (b) : (cmp((a), (c)) > 0) ? (c) : (a))
+static __GNU_INLINE char *
+med3(char *a, char *b, char *c, cmp_f cmp, void *arg)
+{
+ if (cmp(a, b, arg) < 0) {
+ if (cmp(b, c, arg) < 0) {
+ return (b);
+ } else if (cmp(a, c, arg) < 0) {
+ return (c);
+ } else {
+ return (a);
+ }
+ } else {
+ if (cmp(b, c, arg) > 0) {
+ return (b);
+ } else if (cmp(a, c, arg) > 0) {
+ return (c);
+ } else {
+ return (a);
+ }
+ }
+}
#define THRESH_L 5 /* threshold for insertion sort */
#define THRESH_M3 20 /* threshold for median of 3 */
@@ -104,12 +120,17 @@ typedef struct {
* 9) The user compare function modifies the data records
*/
+static int
+qsort_r_wrapper(const void *a, const void *b, void *arg)
+{
+ int (*cmp)(const void *, const void *) =
+ (int(*)(const void *, const void *))(uintptr_t)arg;
+ return (cmp(a, b));
+}
+
void
-qsort(
- void *basep,
- size_t nrec,
- size_t rsiz,
- int (*cmp)(const void *, const void *))
+qsort_r(void *basep, size_t nrec, size_t rsiz,
+ cmp_f cmp, void *arg)
{
size_t i; /* temporary variable */
@@ -209,7 +230,8 @@ qsort(
b_par = t_par;
while (b_par > b_lim) {
b_par -= rsiz;
- if ((*cmp)(b_par, b_par + rsiz) <= 0) {
+ if ((*cmp)(b_par, b_par + rsiz, arg) <=
+ 0) {
break;
}
(*swapf)(b_par, b_par + rsiz, loops);
@@ -264,14 +286,16 @@ qsort(
} else if (nrec < THRESH_M9) {
/* use median of 3 */
i = ((nrec - 1) / 2) * rsiz;
- m2 = med3(b_lim, b_lim + i, b_lim + 2 * i);
+ m2 = med3(b_lim, b_lim + i, b_lim + 2 * i, cmp, arg);
} else {
/* approx median of 9 */
i = ((nrec - 1) / 8) * rsiz;
- m1 = med3(b_lim, b_lim + i, b_lim + 2 * i);
- m2 = med3(b_lim + 3 * i, b_lim + 4 * i, b_lim + 5 * i);
- m3 = med3(b_lim + 6 * i, b_lim + 7 * i, b_lim + 8 * i);
- m2 = med3(m1, m2, m3);
+ m1 = med3(b_lim, b_lim + i, b_lim + 2 * i, cmp, arg);
+ m2 = med3(b_lim + 3 * i, b_lim + 4 * i, b_lim + 5 * i,
+ cmp, arg);
+ m3 = med3(b_lim + 6 * i, b_lim + 7 * i, b_lim + 8 * i,
+ cmp, arg);
+ m2 = med3(m1, m2, m3, cmp, arg);
}
/*
@@ -323,7 +347,7 @@ qsort(
if (b_par == m2) {
continue;
}
- cv = cmp(b_par, m2);
+ cv = cmp(b_par, m2, arg);
if (cv > 0) {
break;
}
@@ -342,7 +366,7 @@ qsort(
if (t_par == m2) {
continue;
}
- cv = cmp(t_par, m2);
+ cv = cmp(t_par, m2, arg);
if (cv < 0) {
break;
}
@@ -467,6 +491,14 @@ qsort(
}
}
+void
+qsort(void *basep, size_t nrec, size_t rsiz,
+ int (*cmp)(const void *, const void *))
+{
+ return (qsort_r(basep, nrec, rsiz, qsort_r_wrapper,
+ (void *)(uintptr_t)cmp));
+}
+
/*
* The following swap functions should not create a stack frame
* the SPARC call / return instruction will be executed
diff --git a/usr/src/head/stdlib.h b/usr/src/head/stdlib.h
index 337311a978..37dfbe6a3e 100644
--- a/usr/src/head/stdlib.h
+++ b/usr/src/head/stdlib.h
@@ -311,7 +311,7 @@ extern char *ulltostr(unsigned long long, char *);
#endif /* defined(__EXTENSIONS__) || !defined(_STRICT_STDC) ... */
-/* OpenBSD compatibility functions */
+/* OpenBSD and misc. compatibility functions */
#if !defined(_STRICT_SYMBOLS)
#include <inttypes.h>
@@ -324,6 +324,8 @@ extern void *recallocarray(void *, size_t, size_t, size_t);
extern long long strtonum(const char *, long long, long long, const char **);
extern void *reallocf(void *, size_t);
+extern void qsort_r(void *, size_t, size_t,
+ int (*)(const void *, const void *, void *), void *);
#endif /* !_STRICT_SYBMOLS */
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index cd5543d872..92a58825d4 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -78,6 +78,11 @@ $if _x86 && _ELF64
$add amd64
$endif
+SYMBOL_VERSION ILLUMOS_0.35 {
+ protected:
+ qsort_r;
+} ILLUMOS_0.34;
+
SYMBOL_VERSION ILLUMOS_0.34 {
protected:
futimes;
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index 0387bbd608..193b5e8e59 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -1160,6 +1160,7 @@ MANLINKS= FD_CLR.3c \
qeconvert.3c \
qfconvert.3c \
qgconvert.3c \
+ qsort_r.3c \
quadruple_to_decimal.3c \
rand_r.3c \
rctlblk_get_enforced_value.3c \
@@ -2252,6 +2253,8 @@ pthread_spin_trylock.3c := LINKSRC = pthread_spin_lock.3c
fputs.3c := LINKSRC = puts.3c
+qsort_r.3c := LINKSRC = qsort.3c
+
rand_r.3c := LINKSRC = rand.3c
srand.3c := LINKSRC = rand.3c
diff --git a/usr/src/man/man3c/qsort.3c b/usr/src/man/man3c/qsort.3c
index 133f654330..7bfaf50a5d 100644
--- a/usr/src/man/man3c/qsort.3c
+++ b/usr/src/man/man3c/qsort.3c
@@ -1,57 +1,106 @@
-'\" te
-.\" Copyright 1989 AT&T Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved
-.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
-.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
-.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH QSORT 3C "Dec 6, 2004"
-.SH NAME
-qsort \- quick sort
-.SH SYNOPSIS
-.LP
-.nf
-#include <stdlib.h>
-
-\fBvoid\fR \fBqsort\fR(\fBvoid *\fR\fIbase\fR, \fBsize_t\fR \fInel\fR, \fBsize_t\fR \fIwidth\fR,
- \fBint (*\fR\fIcompar\fR)(const void *, const void *));
-.fi
-
-.SH DESCRIPTION
-.sp
-.LP
-The \fBqsort()\fR function is an implementation of the quick-sort algorithm. It
-sorts a table of data in place. The contents of the table are sorted in
-ascending order according to the user-supplied comparison function.
-.sp
-.LP
-The \fIbase\fR argument points to the element at the base of the table. The
-\fInel\fR argument is the number of elements in the table. The \fIwidth\fR
-argument specifies the size of each element in bytes. The \fIcompar\fR
-argument is the name of the comparison function, which is called with two
+.\"
+.\" The contents of this file are subject to the terms of the
+.\" Common Development and Distribution License (the "License").
+.\" You may not use this file except in compliance with the License.
+.\"
+.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+.\" or http://www.opensolaris.org/os/licensing.
+.\" See the License for the specific language governing permissions
+.\" and limitations under the License.
+.\"
+.\" When distributing Covered Code, include this CDDL HEADER in each
+.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+.\" If applicable, add the following below this CDDL HEADER, with the
+.\" fields enclosed by brackets "[]" replaced with your own identifying
+.\" information: Portions Copyright [yyyy] [name of copyright owner]
+.\"
+.\"
+.\" Copyright 1989 AT&T
+.\" Copyright (c) 2002, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright 2020 Oxide Computer Company
+.\"
+.Dd September 11, 2020
+.Dt QSORT 3C
+.Os
+.Sh NAME
+.Nm qsort ,
+.Nm qsort_r
+.Nd quick sort
+.Sh SYNOPSIS
+.In stdlib.h
+.Ft void
+.Fo qsort
+.Fa "void *base"
+.Fa "size_t nel"
+.Fa "size_t width"
+.Fa "int (*compar)(const void *, const void *)"
+.Fc
+.Fo qsort
+.Fa "void *base"
+.Fa "size_t nel"
+.Fa "size_t width"
+.Fa "int (*compar_arg)(const void *, const void *, void *)"
+.Fa "void *arg"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn qsort
+function is an implementation of the quick-sort algorithm.
+It sorts a table of data in place.
+The contents of the table are sorted in ascending order according to the
+user-supplied comparison function.
+.Pp
+The
+.Fa Ibase
+argument points to the element at the base of the table.
+The
+.Fa nel
+argument is the number of elements in the table.
+The
+.Fa width
+argument specifies the size of each element in bytes.
+The
+.Fa compar
arguments that point to the elements being compared.
-.sp
-.LP
+The comparison function need not compare every byte, so arbitrary data may be
+contained in the elements in addition to the values being compared.
+.Pp
The function must return an integer less than, equal to, or greater than zero
to indicate if the first argument is to be considered less than, equal to, or
greater than the second argument.
-.sp
-.LP
+.Pp
The contents of the table are sorted in ascending order according to the user
supplied comparison function.
-.SH USAGE
-.sp
-.LP
-The \fBqsort()\fR function safely allows concurrent access by multiple threads
+The relative order in the output of two items that compare as equal is
+unpredictable.
+.Pp
+The
+.Fn qsort_r
+function behaves similarly to the
+.Fn qsort
+function, except that its comparison function,
+.Fn compar_arg ,
+takes an extra argument which is the
+.Fn qsort_r
+argument
+.Fa arg .
+This allows one to avoid global data in the comparison function, unlike
+with the
+.Fn qsort
+function.
+.Pp
+The
+.Fn qsort
+and
+.Fn qsort_r
+function safely allows concurrent access by multiple threads
to disjoint data, such as overlapping subtrees or tables.
-.SH EXAMPLES
-.LP
-\fBExample 1 \fRProgram sorts.
-.sp
-.LP
+.Sh EXAMPLES
+.Sy Example 1
+Program sorts.
+.Pp
The following program sorts a simple array:
-
-.sp
-.in +2
-.nf
+.Bd -literal
#include <stdlib.h>
#include <stdio.h>
@@ -84,38 +133,15 @@ main()
(void) printf("\en");
return (0);
}
-.fi
-.in -2
-
-.SH ATTRIBUTES
-.sp
-.LP
-See \fBattributes\fR(5) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE ATTRIBUTE VALUE
-_
-Interface Stability Standard
-_
-MT-Level MT-Safe
-.TE
-
-.SH SEE ALSO
-.sp
-.LP
-\fBsort\fR(1), \fBbsearch\fR(3C), \fBlsearch\fR(3C), \fBstring\fR(3C),
-\fBattributes\fR(5), \fBstandards\fR(5)
-.SH NOTES
-.sp
-.LP
-The comparison function need not compare every byte, so arbitrary data may be
-contained in the elements in addition to the values being compared.
-.sp
-.LP
-The relative order in the output of two items that compare as equal is
-unpredictable.
+.Ed
+.Sh INTERFACE STABILITY
+.Sy Standard
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr sort 1 ,
+.Xr bsearch 3C ,
+.Xr lsearch 3C ,
+.Xr string 3C ,
+.Xr attributes 5 ,
+.Xr standards 5
diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc
index ce6056ce82..100b5db173 100644
--- a/usr/src/pkg/manifests/system-library.man3c.inc
+++ b/usr/src/pkg/manifests/system-library.man3c.inc
@@ -1216,6 +1216,7 @@ link path=usr/share/man/man3c/putwchar.3c target=fputwc.3c
link path=usr/share/man/man3c/qeconvert.3c target=econvert.3c
link path=usr/share/man/man3c/qfconvert.3c target=econvert.3c
link path=usr/share/man/man3c/qgconvert.3c target=econvert.3c
+link path=usr/share/man/man3c/qsort_r.3c target=qsort.3c
link path=usr/share/man/man3c/quadruple_to_decimal.3c \
target=floating_to_decimal.3c
link path=usr/share/man/man3c/rand_r.3c target=rand.3c
diff --git a/usr/src/pkg/manifests/system-test-libctest.mf b/usr/src/pkg/manifests/system-test-libctest.mf
index 71af45a2cc..092bbde222 100644
--- a/usr/src/pkg/manifests/system-test-libctest.mf
+++ b/usr/src/pkg/manifests/system-test-libctest.mf
@@ -30,6 +30,7 @@ dir path=opt/libc-tests/cfg/symbols
dir path=opt/libc-tests/runfiles
dir path=opt/libc-tests/tests
dir path=opt/libc-tests/tests/i18n
+dir path=opt/libc-tests/tests/qsort
dir path=opt/libc-tests/tests/random
dir path=opt/libc-tests/tests/regex
dir path=opt/libc-tests/tests/regex/data
@@ -118,6 +119,9 @@ file path=opt/libc-tests/tests/psignal mode=0555
file path=opt/libc-tests/tests/psignal-5097.32 mode=0555
file path=opt/libc-tests/tests/psignal-5097.64 mode=0555
file path=opt/libc-tests/tests/pthread_attr_get_np mode=0555
+file path=opt/libc-tests/tests/qsort/qsort_test mode=0555
+file path=opt/libc-tests/tests/qsort/qsort_test.$(ARCH) mode=0555
+file path=opt/libc-tests/tests/qsort/qsort_test.$(ARCH64) mode=0555
file path=opt/libc-tests/tests/quick_exit mode=0555
file path=opt/libc-tests/tests/quick_exit_order.32 mode=0555
file path=opt/libc-tests/tests/quick_exit_order.64 mode=0555
diff --git a/usr/src/test/libc-tests/cfg/symbols/stdlib_h.cfg b/usr/src/test/libc-tests/cfg/symbols/stdlib_h.cfg
index 745575068d..37d6ab9376 100644
--- a/usr/src/test/libc-tests/cfg/symbols/stdlib_h.cfg
+++ b/usr/src/test/libc-tests/cfg/symbols/stdlib_h.cfg
@@ -35,7 +35,7 @@ value | NULL | void * | stdlib.h | ALL
# Functions
#
func | aligned_alloc |\
- void * |\
+ void * |\
size_t; size_t |\
stdlib.h |\
-ALL C11
@@ -47,55 +47,55 @@ func | at_quick_exit |\
-ALL C11
func | calloc |\
- void * |\
+ void * |\
size_t; size_t |\
stdlib.h |\
ALL
func | exit |\
- void |\
+ void |\
int |\
stdlib.h |\
ALL
func | free |\
- void |\
+ void |\
void * |\
stdlib.h |\
ALL
func | malloc |\
- void * |\
+ void * |\
size_t |\
stdlib.h |\
ALL
func | mkstemp |\
- int |\
+ int |\
char * |\
stdlib.h |\
C90 C99 SUSv1+
func | mkostemp |\
- int |\
+ int |\
char *; int |\
stdlib.h |\
-ALL
func | mkstemps |\
- int |\
+ int |\
char *; int |\
stdlib.h |\
C90 C99
func | mkostemps |\
- int |\
+ int |\
char *; int; int |\
stdlib.h |\
-ALL
func | mkdtemp |\
- char * |\
+ char * |\
char * |\
stdlib.h |\
-ALL SUSv4+
@@ -105,3 +105,15 @@ func | quick_exit |\
int |\
stdlib.h |\
-ALL C11
+
+func | qsort |\
+ void |\
+ void *; size_t; size_t; int (*)(const void *, const void *) |\
+ stdlib.h |\
+ ALL
+
+func | qsort_r |\
+ void |\
+ void *; size_t; size_t; int (*)(const void *, const void *, void *), void * |\
+ stdlib.h |\
+ -ALL
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index e8323a549b..6ccb7c794f 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -40,6 +40,7 @@ outputdir = /var/tmp/test_results
[/opt/libc-tests/tests/wcsncasecmp-7350.64]
[/opt/libc-tests/tests/i18n/bindtextdomain_test]
+[/opt/libc-tests/tests/qsort/qsort_test]
[/opt/libc-tests/tests/random/getrandom]
[/opt/libc-tests/tests/random/getentropy]
diff --git a/usr/src/test/libc-tests/tests/Makefile b/usr/src/test/libc-tests/tests/Makefile
index fcb903edeb..016b2a0dab 100644
--- a/usr/src/test/libc-tests/tests/Makefile
+++ b/usr/src/test/libc-tests/tests/Makefile
@@ -23,6 +23,7 @@ SUBDIRS = \
newlocale \
nl_langinfo \
priv_gettext \
+ qsort \
random \
regex \
select \
diff --git a/usr/src/test/libc-tests/tests/qsort/Makefile b/usr/src/test/libc-tests/tests/qsort/Makefile
new file mode 100644
index 0000000000..0fcc2d2aba
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/qsort/Makefile
@@ -0,0 +1,22 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2020 Oxide Computer Company
+#
+
+TESTSUBDIR = qsort
+PROG = qsort_test
+ARCHPROG = qsort_test
+OBJS_OVERRIDE=1
+OBJS = qsort_test.o merge.o antiqsort.o
+
+include ../Makefile.com
diff --git a/usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE b/usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..fed2d8bb0b
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Peter McIlroy.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
diff --git a/usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE.descrip b/usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..3d6d2ccd52
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/qsort/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+QUICKSORT TEST SUITE
diff --git a/usr/src/test/libc-tests/tests/qsort/antiqsort.c b/usr/src/test/libc-tests/tests/qsort/antiqsort.c
new file mode 100644
index 0000000000..cb3b7b2525
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/qsort/antiqsort.c
@@ -0,0 +1,58 @@
+/*
+ * A Killer Adversary for Quicksort
+ * M. D. MCILROY
+ * http://www.cs.dartmouth.edu/~doug/mdmspe.pdf
+ *
+ * For comparison:
+ * Bentley & McIlroy: 4096 items, 1645585 compares
+ * random pivot: 4096 items, 8388649 compares
+ * introsort: 4096 items, 151580 compares
+ * heapsort: 4096 items, 48233 compares
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static int *val; /* item values */
+static int ncmp; /* number of comparisons */
+static int nsolid; /* number of solid items */
+static int candidate; /* pivot candidate */
+static int gas; /* gas value */
+
+#define freeze(x) (val[(x)] = nsolid++)
+
+static int
+cmp(const void *px, const void *py)
+{
+ const int x = *(const int *)px;
+ const int y = *(const int *)py;
+
+ ncmp++;
+ if (val[x] == gas && val[y] == gas) {
+ if (x == candidate)
+ freeze(x);
+ else
+ freeze(y);
+ }
+ if (val[x] == gas)
+ candidate = x;
+ else if (val[y] == gas)
+ candidate = y;
+ return val[x] > val[y] ? 1 : val[x] < val[y] ? -1 : 0;
+}
+
+int
+antiqsort(int n, int *a, int *ptr)
+{
+ int i;
+
+ val = a;
+ gas = n - 1;
+ nsolid = ncmp = candidate = 0;
+ for (i = 0; i < n; i++) {
+ ptr[i] = i;
+ val[i] = gas;
+ }
+ qsort(ptr, n, sizeof(*ptr), cmp);
+ return ncmp;
+}
diff --git a/usr/src/test/libc-tests/tests/qsort/merge.c b/usr/src/test/libc-tests/tests/qsort/merge.c
new file mode 100644
index 0000000000..d60317ce08
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/qsort/merge.c
@@ -0,0 +1,336 @@
+/* $OpenBSD: merge.c,v 1.10 2015/06/21 03:20:56 millert Exp $ */
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Peter McIlroy.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+/*
+ * Hybrid exponential search/linear search merge sort with hybrid
+ * natural/pairwise first pass. Requires about .3% more comparisons
+ * for random data than LSMS with pairwise first pass alone.
+ * It works for objects as small as two bytes.
+ */
+
+#define NATURAL
+#define THRESHOLD 16 /* Best choice for natural merge cut-off. */
+
+/* #define NATURAL to get hybrid natural merge.
+ * (The default is pairwise merging.)
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void setup(u_char *, u_char *, size_t, size_t, int (*)());
+static void insertionsort(u_char *, size_t, size_t, int (*)());
+
+#define ISIZE sizeof(int)
+#define PSIZE sizeof(u_char *)
+#define ICOPY_LIST(src, dst, last) \
+ do \
+ *(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE; \
+ while(src < last)
+#define ICOPY_ELT(src, dst, i) \
+ do \
+ *(int*) dst = *(int*) src, src += ISIZE, dst += ISIZE; \
+ while (i -= ISIZE)
+
+#define CCOPY_LIST(src, dst, last) \
+ do \
+ *dst++ = *src++; \
+ while (src < last)
+#define CCOPY_ELT(src, dst, i) \
+ do \
+ *dst++ = *src++; \
+ while (i -= 1)
+
+/*
+ * Find the next possible pointer head. (Trickery for forcing an array
+ * to do double duty as a linked list when objects do not align with word
+ * boundaries.
+ */
+/* Assumption: PSIZE is a power of 2. */
+#define EVAL(p) (u_char **) \
+ ((u_char *)0 + \
+ (((u_char *)p + PSIZE - 1 - (u_char *) 0) & ~(PSIZE - 1)))
+
+/*
+ * Arguments are as for qsort.
+ */
+int
+mergesort(void *base, size_t nmemb, size_t size,
+ int (*cmp)(const void *, const void *))
+{
+ int i, sense;
+ int big, iflag;
+ u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2;
+ u_char *list2, *list1, *p2, *p, *last, **p1;
+
+ if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (nmemb == 0)
+ return (0);
+
+ /*
+ * XXX
+ * Stupid subtraction for the Cray.
+ */
+ iflag = 0;
+ if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE))
+ iflag = 1;
+
+ if ((list2 = malloc(nmemb * size + PSIZE)) == NULL)
+ return (-1);
+
+ list1 = base;
+ setup(list1, list2, nmemb, size, cmp);
+ last = list2 + nmemb * size;
+ i = big = 0;
+ while (*EVAL(list2) != last) {
+ l2 = list1;
+ p1 = EVAL(list1);
+ for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) {
+ p2 = *EVAL(p2);
+ f1 = l2;
+ f2 = l1 = list1 + (p2 - list2);
+ if (p2 != last)
+ p2 = *EVAL(p2);
+ l2 = list1 + (p2 - list2);
+ while (f1 < l1 && f2 < l2) {
+ if ((*cmp)(f1, f2) <= 0) {
+ q = f2;
+ b = f1, t = l1;
+ sense = -1;
+ } else {
+ q = f1;
+ b = f2, t = l2;
+ sense = 0;
+ }
+ if (!big) { /* here i = 0 */
+ while ((b += size) < t && cmp(q, b) >sense)
+ if (++i == 6) {
+ big = 1;
+ goto EXPONENTIAL;
+ }
+ } else {
+EXPONENTIAL: for (i = size; ; i <<= 1)
+ if ((p = (b + i)) >= t) {
+ if ((p = t - size) > b &&
+ (*cmp)(q, p) <= sense)
+ t = p;
+ else
+ b = p;
+ break;
+ } else if ((*cmp)(q, p) <= sense) {
+ t = p;
+ if (i == size)
+ big = 0;
+ goto FASTCASE;
+ } else
+ b = p;
+ while (t > b+size) {
+ i = (((t - b) / size) >> 1) * size;
+ if ((*cmp)(q, p = b + i) <= sense)
+ t = p;
+ else
+ b = p;
+ }
+ goto COPY;
+FASTCASE: while (i > size)
+ if ((*cmp)(q,
+ p = b + (i >>= 1)) <= sense)
+ t = p;
+ else
+ b = p;
+COPY: b = t;
+ }
+ i = size;
+ if (q == f1) {
+ if (iflag) {
+ ICOPY_LIST(f2, tp2, b);
+ ICOPY_ELT(f1, tp2, i);
+ } else {
+ CCOPY_LIST(f2, tp2, b);
+ CCOPY_ELT(f1, tp2, i);
+ }
+ } else {
+ if (iflag) {
+ ICOPY_LIST(f1, tp2, b);
+ ICOPY_ELT(f2, tp2, i);
+ } else {
+ CCOPY_LIST(f1, tp2, b);
+ CCOPY_ELT(f2, tp2, i);
+ }
+ }
+ }
+ if (f2 < l2) {
+ if (iflag)
+ ICOPY_LIST(f2, tp2, l2);
+ else
+ CCOPY_LIST(f2, tp2, l2);
+ } else if (f1 < l1) {
+ if (iflag)
+ ICOPY_LIST(f1, tp2, l1);
+ else
+ CCOPY_LIST(f1, tp2, l1);
+ }
+ *p1 = l2;
+ }
+ tp2 = list1; /* swap list1, list2 */
+ list1 = list2;
+ list2 = tp2;
+ last = list2 + nmemb*size;
+ }
+ if (base == list2) {
+ memmove(list2, list1, nmemb*size);
+ list2 = list1;
+ }
+ free(list2);
+ return (0);
+}
+
+#define swap(a, b) { \
+ s = b; \
+ i = size; \
+ do { \
+ tmp = *a; *a++ = *s; *s++ = tmp; \
+ } while (--i); \
+ a -= size; \
+ }
+#define reverse(bot, top) { \
+ s = top; \
+ do { \
+ i = size; \
+ do { \
+ tmp = *bot; *bot++ = *s; *s++ = tmp; \
+ } while (--i); \
+ s -= size2; \
+ } while(bot < s); \
+}
+
+/*
+ * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of
+ * increasing order, list2 in a corresponding linked list. Checks for runs
+ * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL
+ * is defined. Otherwise simple pairwise merging is used.)
+ */
+void
+setup(u_char *list1, u_char *list2, size_t n, size_t size,
+ int (*cmp)(const void *, const void *))
+{
+ int i, length, size2, sense;
+ u_char tmp, *f1, *f2, *s, *l2, *last, *p2;
+
+ size2 = size*2;
+ if (n <= 5) {
+ insertionsort(list1, n, size, cmp);
+ *EVAL(list2) = (u_char*) list2 + n*size;
+ return;
+ }
+ /*
+ * Avoid running pointers out of bounds; limit n to evens
+ * for simplicity.
+ */
+ i = 4 + (n & 1);
+ insertionsort(list1 + (n - i) * size, i, size, cmp);
+ last = list1 + size * (n - i);
+ *EVAL(list2 + (last - list1)) = list2 + n * size;
+
+#ifdef NATURAL
+ p2 = list2;
+ f1 = list1;
+ sense = (cmp(f1, f1 + size) > 0);
+ for (; f1 < last; sense = !sense) {
+ length = 2;
+ /* Find pairs with same sense. */
+ for (f2 = f1 + size2; f2 < last; f2 += size2) {
+ if ((cmp(f2, f2+ size) > 0) != sense)
+ break;
+ length += 2;
+ }
+ if (length < THRESHOLD) { /* Pairwise merge */
+ do {
+ p2 = *EVAL(p2) = f1 + size2 - list1 + list2;
+ if (sense > 0)
+ swap (f1, f1 + size);
+ } while ((f1 += size2) < f2);
+ } else { /* Natural merge */
+ l2 = f2;
+ for (f2 = f1 + size2; f2 < l2; f2 += size2) {
+ if ((cmp(f2-size, f2) > 0) != sense) {
+ p2 = *EVAL(p2) = f2 - list1 + list2;
+ if (sense > 0)
+ reverse(f1, f2-size);
+ f1 = f2;
+ }
+ }
+ if (sense > 0)
+ reverse (f1, f2-size);
+ f1 = f2;
+ if (f2 < last || cmp(f2 - size, f2) > 0)
+ p2 = *EVAL(p2) = f2 - list1 + list2;
+ else
+ p2 = *EVAL(p2) = list2 + n*size;
+ }
+ }
+#else /* pairwise merge only. */
+ for (f1 = list1, p2 = list2; f1 < last; f1 += size2) {
+ p2 = *EVAL(p2) = p2 + size2;
+ if (cmp (f1, f1 + size) > 0)
+ swap(f1, f1 + size);
+ }
+#endif /* NATURAL */
+}
+
+/*
+ * This is to avoid out-of-bounds addresses in sorting the
+ * last 4 elements.
+ */
+static void
+insertionsort(u_char *a, size_t n, size_t size,
+ int (*cmp)(const void *, const void *))
+{
+ u_char *ai, *s, *t, *u, tmp;
+ int i;
+
+ for (ai = a+size; --n >= 1; ai += size)
+ for (t = ai; t > a; t -= size) {
+ u = t - size;
+ if (cmp(u, t) <= 0)
+ break;
+ swap(u, t);
+ }
+}
diff --git a/usr/src/test/libc-tests/tests/qsort/qsort_test.c b/usr/src/test/libc-tests/tests/qsort/qsort_test.c
new file mode 100644
index 0000000000..fb0f5f8e08
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/qsort/qsort_test.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright (c) 2017 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/time.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+#include <time.h>
+#include <unistd.h>
+#include <err.h>
+
+/*
+ * Test program based on Bentley & McIlroy's "Engineering a Sort Function".
+ * Uses mergesort(3) to check the results.
+ *
+ * Additional "killer" input taken from:
+ * David R. Musser's "Introspective Sorting and Selection Algorithms"
+ * http://calmerthanyouare.org/2014/06/11/algorithmic-complexity-attacks-and-libc-qsort.html
+ * M. D. McIlroy's "A Killer Adversary for Quicksort"
+ */
+
+#define timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (0)
+
+extern int mergesort(void *, size_t, size_t,
+ int (*)(const void *, const void *));
+
+/*
+ * TODO:
+ * test with unaligned elements (char[]?)
+ */
+struct test_distribution {
+ const char *name;
+ void (*fill)(void *x, int n, int m);
+ int (*test)(struct test_distribution *d, int n, void *x, void *y, void *z);
+ int (*cmp)(const void *v1, const void *v2);
+ int (*cmp_checked)(const void *v1, const void *v2);
+};
+
+#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
+#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
+
+static size_t compares;
+static size_t max_compares;
+static size_t abrt_compares;
+static sigjmp_buf cmpjmp;
+static bool dump_table, timing, verbose;
+
+extern int antiqsort(int n, int *a, int *ptr);
+
+static int
+cmp_i(const void *v1, const void *v2)
+{
+ const int a = *(const int *)v1;
+ const int b = *(const int *)v2;
+
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+
+static int
+cmp_checked_i(const void *v1, const void *v2)
+{
+ const int a = *(const int *)v1;
+ const int b = *(const int *)v2;
+
+ compares++;
+ if (compares > abrt_compares)
+ siglongjmp(cmpjmp, 1);
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+
+static int
+cmp_ll(const void *v1, const void *v2)
+{
+ const long long a = *(const long long *)v1;
+ const long long b = *(const long long *)v2;
+
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+
+static int
+cmp_checked_ll(const void *v1, const void *v2)
+{
+ const long long a = *(const long long *)v1;
+ const long long b = *(const long long *)v2;
+
+ compares++;
+ if (compares > abrt_compares)
+ siglongjmp(cmpjmp, 1);
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+
+static int
+cmp_d(const void *v1, const void *v2)
+{
+ const double a = *(const double *)v1;
+ const double b = *(const double *)v2;
+
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+
+static int
+cmp_checked_d(const void *v1, const void *v2)
+{
+ const double a = *(const double *)v1;
+ const double b = *(const double *)v2;
+
+ compares++;
+ if (compares > abrt_compares)
+ siglongjmp(cmpjmp, 1);
+ return a > b ? 1 : a < b ? -1 : 0;
+}
+
+static int
+check_result(char *sub, size_t es, char *got, char *expected, struct test_distribution *d,
+ int m, int n)
+{
+ int i;
+
+ if (verbose || compares > max_compares) {
+ if (sub != NULL) {
+ if (m != 0) {
+ warnx("%s (%s): m: %d, n: %d, %zu compares, "
+ "max %zu(%zu)", d->name, sub, m, n,
+ compares, max_compares, abrt_compares);
+ } else {
+ warnx("%s (%s): n: %d, %zu compares, "
+ "max %zu(%zu)", d->name, sub, n,
+ compares, max_compares, abrt_compares);
+ }
+ } else {
+ if (m != 0) {
+ warnx("%s: m: %d, n: %d, %zu compares, "
+ "max %zu(%zu)", d->name, m, n,
+ compares, max_compares, abrt_compares);
+ } else {
+ warnx("%s: n: %d, %zu compares, "
+ "max %zu(%zu)", d->name, n,
+ compares, max_compares, abrt_compares);
+ }
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ if (memcmp(got, expected, es) != 0)
+ break;
+ got += es;
+ expected += es;
+ }
+ if (i == n)
+ return 0;
+
+ if (sub != NULL) {
+ warnx("%s (%s): failure at %d, m: %d, n: %d",
+ d->name, sub, i, m, n);
+ } else {
+ warnx("%s: failure at %d, m: %d, n: %d",
+ d->name, i, m, n);
+ }
+ return 1;
+}
+
+#define FILL_SAWTOOTH(x, n, m) do { \
+ int i; \
+ \
+ for (i = 0; i < n; i++) \
+ x[i] = i % m; \
+} while (0)
+
+static void
+fill_sawtooth_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_SAWTOOTH(x, n, m);
+}
+
+static void
+fill_sawtooth_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_SAWTOOTH(x, n, m);
+}
+
+static void
+fill_sawtooth_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_SAWTOOTH(x, n, m);
+}
+
+#define FILL_RANDOM(x, n, m) do { \
+ int i; \
+ \
+ for (i = 0; i < n; i++) \
+ x[i] = arc4random_uniform(m); \
+} while (0)
+
+
+static void
+fill_random_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_RANDOM(x, n, m);
+}
+
+static void
+fill_random_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_RANDOM(x, n, m);
+}
+
+static void
+fill_random_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_RANDOM(x, n, m);
+}
+
+#define FILL_STAGGER(x, n, m) do { \
+ int i; \
+ \
+ for (i = 0; i < n; i++) \
+ x[i] = (i * m + i) % n; \
+} while (0)
+
+
+static void
+fill_stagger_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_STAGGER(x, n, m);
+}
+
+static void
+fill_stagger_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_STAGGER(x, n, m);
+}
+
+static void
+fill_stagger_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_STAGGER(x, n, m);
+}
+
+#define FILL_PLATEAU(x, n, m) do { \
+ int i; \
+ \
+ for (i = 0; i < n; i++) \
+ x[i] = MINIMUM(i, m); \
+} while (0)
+
+static void
+fill_plateau_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_PLATEAU(x, n, m);
+}
+
+static void
+fill_plateau_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_PLATEAU(x, n, m);
+}
+
+static void
+fill_plateau_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_PLATEAU(x, n, m);
+}
+
+#define FILL_SHUFFLE(x, n, m) do { \
+ int i, j, k; \
+ \
+ for (i = 0, j = 0, k = 1; i < n; i++) \
+ x[i] = arc4random_uniform(m) ? (j += 2) : (k += 2); \
+} while (0)
+
+static void
+fill_shuffle_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_SHUFFLE(x, n, m);
+}
+
+static void
+fill_shuffle_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_SHUFFLE(x, n, m);
+}
+
+static void
+fill_shuffle_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_SHUFFLE(x, n, m);
+}
+
+#define FILL_BSD_KILLER(x, n, m) do { \
+ int i, k; \
+ \
+ /* 4.4BSD insertion sort optimization killer, Mats Linander */ \
+ k = n / 2; \
+ for (i = 0; i < n; i++) { \
+ if (i < k) \
+ x[i] = k - i; \
+ else if (i > k) \
+ x[i] = n + k + 1 - i; \
+ else \
+ x[i] = k + 1; \
+ } \
+} while (0)
+
+static void
+fill_bsd_killer_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_BSD_KILLER(x, n, m);
+
+}
+
+static void
+fill_bsd_killer_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_BSD_KILLER(x, n, m);
+
+}
+
+static void
+fill_bsd_killer_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_BSD_KILLER(x, n, m);
+
+}
+
+#define FILL_MED3_KILLER(x, n, m) do { \
+ int i, k; \
+ \
+ /* \
+ * Median of 3 killer, as seen in David R Musser's \
+ * "Introspective Sorting and Selection Algorithms" \
+ */ \
+ if (n & 1) { \
+ /* odd size, store last at end and make even. */ \
+ x[n - 1] = n; \
+ n--; \
+ } \
+ k = n / 2; \
+ for (i = 0; i < n; i++) { \
+ if (i & 1) { \
+ x[i] = k + x[i - 1]; \
+ } else { \
+ x[i] = i + 1; \
+ } \
+ } \
+} while (0)
+
+static void
+fill_med3_killer_i(void *v, int n, int m)
+{
+ int *x = v;
+ FILL_MED3_KILLER(x, n, m);
+}
+
+static void
+fill_med3_killer_ll(void *v, int n, int m)
+{
+ long long *x = v;
+ FILL_MED3_KILLER(x, n, m);
+}
+
+static void
+fill_med3_killer_double(void *v, int n, int m)
+{
+ double *x = v;
+ FILL_MED3_KILLER(x, n, m);
+}
+
+static void
+print_timing(struct test_distribution *d, char *sub, int m, int n, double elapsed)
+{
+ if (sub != NULL) {
+ if (m != 0) {
+ warnx("%s (%s): m: %d, n: %d, %f seconds",
+ d->name, sub, m, n, elapsed);
+ } else {
+ warnx("%s (%s): n: %d, %f seconds",
+ d->name, sub, n, elapsed);
+ }
+ } else {
+ if (m != 0) {
+ warnx("%s: m: %d, n: %d, %f seconds",
+ d->name, m, n, elapsed);
+ } else {
+ warnx("%s: n: %d, %f seconds",
+ d->name, n, elapsed);
+ }
+ }
+}
+
+static int
+do_test(struct test_distribution *d, char *sub, int m, int n, size_t es, void *y, void *z)
+{
+ int ret = 0;
+ struct timespec before, after;
+
+ compares = 0;
+ if (sigsetjmp(cmpjmp, 1) != 0) {
+ if (sub != NULL) {
+ warnx("%s (%s): qsort aborted after %zu compares, m: %d, n: %d",
+ d->name, sub, compares, m, n);
+ } else {
+ warnx("%s: qsort aborted after %zu compares, m: %d, n: %d",
+ d->name, compares, m, n);
+ }
+ ret = 1;
+ } else {
+ if (timing)
+ (void) clock_gettime(CLOCK_MONOTONIC, &before);
+ qsort(y, n, es, d->cmp_checked);
+ if (timing) {
+ double elapsed;
+ (void) clock_gettime(CLOCK_MONOTONIC, &after);
+ timespecsub(&after, &before, &after);
+ elapsed = after.tv_sec + after.tv_nsec / 1000000000.0;
+ print_timing(d, sub, m, n, elapsed);
+ }
+ if (check_result(sub, es, y, z, d, m, n) != 0)
+ ret = 1;
+ }
+ return ret;
+}
+
+#define TEST_PERTURBED(d, n, x, y, z) do { \
+ int i, j, m; \
+ const int es = sizeof(x[0]); \
+ \
+ for (m = 1; m < 2 * n; m *= 2) { \
+ /* Fill in x[n] modified by m */ \
+ d->fill(x, n, m); \
+ \
+ /* Test on copy of x[] */ \
+ for (i = 0; i < n; i++) \
+ y[i] = z[i] = x[i]; \
+ if (mergesort(z, n, es, d->cmp) != 0) \
+ err(1, NULL); \
+ if (do_test(d, "copy", m, n, es, y, z) != 0) \
+ ret = 1; \
+ \
+ /* Test on reversed copy of x[] */ \
+ for (i = 0, j = n - 1; i < n; i++, j--) \
+ y[i] = z[i] = x[j]; \
+ if (mergesort(z, n, es, d->cmp) != 0) \
+ err(1, NULL); \
+ if (do_test(d, "reversed", m, n, es, y, z) != 0) \
+ ret = 1; \
+ \
+ /* Test with front half of x[] reversed */ \
+ for (i = 0, j = (n / 2) - 1; i < n / 2; i++, j--) \
+ y[i] = z[i] = x[j]; \
+ for (; i < n; i++) \
+ y[i] = z[i] = x[i]; \
+ if (mergesort(z, n, es, d->cmp) != 0) \
+ err(1, NULL); \
+ if (do_test(d, "front reversed", m, n, es, y, z) != 0) \
+ ret = 1; \
+ \
+ /* Test with back half of x[] reversed */ \
+ for (i = 0; i < n / 2; i++) \
+ y[i] = z[i] = x[i]; \
+ for (j = n - 1; i < n; i++, j--) \
+ y[i] = z[i] = x[j]; \
+ if (mergesort(z, n, es, d->cmp) != 0) \
+ err(1, NULL); \
+ if (do_test(d, "back reversed", m, n, es, y, z) != 0) \
+ ret = 1; \
+ \
+ /* Test on sorted copy of x[] */ \
+ if (mergesort(x, n, es, d->cmp) != 0) \
+ err(1, NULL); \
+ for (i = 0; i < n; i++) \
+ y[i] = x[i]; \
+ if (do_test(d, "sorted", m, n, es, y, x) != 0) \
+ ret = 1; \
+ \
+ /* Test with i%5 added to x[i] (dither) */ \
+ for (i = 0, j = n - 1; i < n; i++, j--) \
+ y[i] = z[i] = x[j] + (i % 5); \
+ if (mergesort(z, n, es, d->cmp) != 0) \
+ err(1, NULL); \
+ if (do_test(d, "dithered", m, n, es, y, z) != 0) \
+ ret = 1; \
+ } \
+} while (0)
+
+static int
+test_perturbed_i(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ int *x = vx;
+ int *y = vx;
+ int *z = vz;
+ int ret = 0;
+
+ TEST_PERTURBED(d, n, x, y, z);
+ return ret;
+}
+
+static int
+test_perturbed_ll(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ long long *x = vx;
+ long long *y = vx;
+ long long *z = vz;
+ int ret = 0;
+
+ TEST_PERTURBED(d, n, x, y, z);
+ return ret;
+}
+
+static int
+test_perturbed_double(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ double *x = vx;
+ double *y = vx;
+ double *z = vz;
+ int ret = 0;
+
+ TEST_PERTURBED(d, n, x, y, z);
+ return ret;
+}
+
+/*
+ * Like TEST_PERTURBED but because the input is tailored to cause
+ * quicksort to go quadratic we don't perturb the input.
+ */
+#define TEST_SIMPLE(d, n, x, y, z) do { \
+ int i; \
+ \
+ /* Fill in x[n] */ \
+ d->fill(x, n, 0); \
+ \
+ /* Make a copy of x[] */ \
+ for (i = 0; i < n; i++) \
+ y[i] = z[i] = x[i]; \
+ if (mergesort(z, n, sizeof(z[0]), d->cmp) != 0) \
+ err(1, NULL); \
+ if (do_test(d, NULL, 0, n, sizeof(x[0]), y, z) != 0) \
+ ret = 1; \
+} while (0)
+
+static int
+test_simple_i(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ int *x = vx;
+ int *y = vx;
+ int *z = vz;
+ int ret = 0;
+
+ TEST_SIMPLE(d, n, x, y, z);
+ return ret;
+}
+
+static int
+test_simple_ll(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ long long *x = vx;
+ long long *y = vx;
+ long long *z = vz;
+ int ret = 0;
+
+ TEST_SIMPLE(d, n, x, y, z);
+ return ret;
+}
+
+static int
+test_simple_double(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ double *x = vx;
+ double *y = vx;
+ double *z = vz;
+ int ret = 0;
+
+ TEST_SIMPLE(d, n, x, y, z);
+ return ret;
+}
+
+/*
+ * Use D. McIlroy's "Killer Adversary for Quicksort"
+ * We don't compare the results in this case.
+ */
+static int
+test_antiqsort(struct test_distribution *d, int n, void *vx, void *vy, void *vz)
+{
+ struct timespec before, after;
+ int *x = vx;
+ int *y = vx;
+ int i, ret = 0;
+
+ /*
+ * We expect antiqsort to generate > 1.5 * nlgn compares.
+ * If introspection is not used, it will be > 10 * nlgn compares.
+ */
+ if (timing)
+ (void) clock_gettime(CLOCK_MONOTONIC, &before);
+ i = antiqsort(n, x, y);
+ if (timing)
+ (void) clock_gettime(CLOCK_MONOTONIC, &after);
+ if (i > abrt_compares)
+ ret = 1;
+ if (dump_table) {
+ printf("/* %d items, %d compares */\n", n, i);
+ printf("static const int table_%d[] = {", n);
+ for (i = 0; i < n - 1; i++) {
+ if ((i % 12) == 0)
+ printf("\n\t");
+ printf("%4d, ", x[i]);
+ }
+ printf("%4d\n};\n", x[i]);
+ } else {
+ if (timing) {
+ double elapsed;
+ timespecsub(&after, &before, &after);
+ elapsed = after.tv_sec + after.tv_nsec / 1000000000.0;
+ print_timing(d, NULL, 0, n, elapsed);
+ }
+ if (verbose || ret != 0) {
+ warnx("%s: n: %d, %d compares, max %zu(%zu)",
+ d->name, n, i, max_compares, abrt_compares);
+ }
+ }
+
+ return ret;
+}
+
+static struct test_distribution distributions[] = {
+ { "sawtooth_i", fill_sawtooth_i, test_perturbed_i, cmp_i, cmp_checked_i },
+ { "sawtooth_ll", fill_sawtooth_ll, test_perturbed_ll, cmp_ll, cmp_checked_ll },
+ { "sawtooth_d", fill_sawtooth_double, test_perturbed_double, cmp_d, cmp_checked_d },
+ { "random_i", fill_random_i, test_perturbed_i, cmp_i, cmp_checked_i },
+ { "random_ll", fill_random_ll, test_perturbed_ll, cmp_ll, cmp_checked_ll },
+ { "random_d", fill_random_double, test_perturbed_double, cmp_d, cmp_checked_d },
+ { "stagger_i", fill_stagger_i, test_perturbed_i, cmp_i, cmp_checked_i },
+ { "stagger_ll", fill_stagger_ll, test_perturbed_ll, cmp_ll, cmp_checked_ll },
+ { "stagger_d", fill_stagger_double, test_perturbed_double, cmp_d, cmp_checked_d },
+ { "plateau_i", fill_plateau_i, test_perturbed_i, cmp_i, cmp_checked_i },
+ { "plateau_ll", fill_plateau_ll, test_perturbed_ll, cmp_ll, cmp_checked_ll },
+ { "plateau_d", fill_plateau_double, test_perturbed_double, cmp_d, cmp_checked_d },
+ { "shuffle_i", fill_shuffle_i, test_perturbed_i, cmp_i, cmp_checked_i },
+ { "shuffle_ll", fill_shuffle_ll, test_perturbed_ll, cmp_ll, cmp_checked_ll },
+ { "shuffle_d", fill_shuffle_double, test_perturbed_double, cmp_d, cmp_checked_d },
+ { "bsd_killer_i", fill_bsd_killer_i, test_simple_i, cmp_i, cmp_checked_i },
+ { "bsd_killer_ll", fill_bsd_killer_ll, test_simple_ll, cmp_ll, cmp_checked_ll },
+ { "bsd_killer_d", fill_bsd_killer_double, test_simple_double, cmp_d, cmp_checked_d },
+ { "med3_killer_i", fill_med3_killer_i, test_simple_i, cmp_i, cmp_checked_i },
+ { "med3_killer_ll", fill_med3_killer_ll, test_simple_ll, cmp_ll, cmp_checked_ll },
+ { "med3_killer_d", fill_med3_killer_double, test_simple_double, cmp_d, cmp_checked_d },
+ { "antiqsort", NULL, test_antiqsort, cmp_i, cmp_checked_i },
+ { NULL }
+};
+
+static int
+run_tests(int n, const char *name)
+{
+ void *x, *y, *z;
+ int i, nlgn = 0;
+ int ret = 0;
+ size_t es;
+ struct test_distribution *d;
+
+ /*
+ * We expect A*n*lg(n) compares where A is between 1 and 2.
+ * For A > 1.5, print a warning.
+ * For A > 10 abort the test since qsort has probably gone quadratic.
+ */
+ for (i = n - 1; i > 0; i >>= 1)
+ nlgn++;
+ nlgn *= n;
+ max_compares = nlgn * 1.5;
+ abrt_compares = nlgn * 10;
+
+ /* Allocate enough space to store ints or doubles. */
+ es = MAXIMUM(sizeof(int), sizeof(double));
+ x = reallocarray(NULL, n, es);
+ y = reallocarray(NULL, n, es);
+ z = reallocarray(NULL, n, es);
+ if (x == NULL || y == NULL || z == NULL)
+ err(1, NULL);
+
+ for (d = distributions; d->name != NULL; d++) {
+ if (name != NULL && strncmp(name, d->name, strlen(name)) != 0)
+ continue;
+ if (d->test(d, n, x, y, z) != 0)
+ ret = 1;
+ }
+
+ free(x);
+ free(y);
+ free(z);
+ return ret;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: qsort_test [-dvt] [-n test_name] [num ...]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *nums[] = { "100", "1023", "1024", "1025", "4095", "4096", "4097" };
+ struct test_distribution *d;
+ int ch, n, ret = 0;
+ char *name = NULL;
+
+ while ((ch = getopt(argc, argv, "dn:tv")) != -1) {
+ switch (ch) {
+ case 'd':
+ dump_table = true;
+ break;
+ case 'n':
+ for (d = distributions; d->name != NULL; d++) {
+ if (strncmp(optarg, d->name, strlen(optarg)) == 0)
+ break;
+ }
+ if (d->name == NULL)
+ errx(1, "unknown test %s", optarg);
+ name = optarg;
+ break;
+ case 't':
+ timing = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0) {
+ argv = nums;
+ argc = sizeof(nums) / sizeof(nums[0]);
+ }
+
+ while (argc > 0) {
+ n = atoi(*argv);
+ if (run_tests(n, name) != 0)
+ ret = 1;
+ argc--;
+ argv++;
+ }
+
+ return ret;
+}
diff --git a/usr/src/uts/common/os/clock.c b/usr/src/uts/common/os/clock.c
index 75c3b000db..93f12d7b96 100644
--- a/usr/src/uts/common/os/clock.c
+++ b/usr/src/uts/common/os/clock.c
@@ -318,7 +318,9 @@ time_t boot_time = 0; /* Boot time in seconds since 1970 */
cyclic_id_t clock_cyclic; /* clock()'s cyclic_id */
cyclic_id_t deadman_cyclic; /* deadman()'s cyclic_id */
-extern void clock_tick_schedule(int);
+extern void clock_tick_schedule(int);
+extern void set_freemem(void);
+extern void pageout_deadman(void);
static int lgrp_ticks; /* counter to schedule lgrp load calcs */
@@ -400,7 +402,6 @@ clock(void)
uint_t w_io;
cpu_t *cp;
cpupart_t *cpupart;
- extern void set_freemem();
void (*funcp)();
int32_t ltemp;
int64_t lltemp;
@@ -477,6 +478,7 @@ clock(void)
if (one_sec) {
loadavg_update();
deadman_counter++;
+ pageout_deadman();
}
/*
diff --git a/usr/src/uts/common/os/kmem.c b/usr/src/uts/common/os/kmem.c
index d12928acc3..ec7d118275 100644
--- a/usr/src/uts/common/os/kmem.c
+++ b/usr/src/uts/common/os/kmem.c
@@ -24,6 +24,7 @@
* Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2018, Joyent, Inc.
+ * Copyright 2020 Oxide Computer Company
*/
/*
@@ -4530,8 +4531,21 @@ void
kmem_thread_init(void)
{
kmem_move_init();
+
+ /*
+ * This taskq is used for various kmem maintenance functions, including
+ * kmem_reap(). When maintenance is required on every cache,
+ * kmem_cache_applyall() dispatches one task per cache onto this queue.
+ *
+ * In the case of kmem_reap(), the system may be under increasingly
+ * dire memory pressure and may not be able to allocate a new task
+ * entry. The count of entries to prepopulate (below) should cover at
+ * least as many caches as we generally expect to exist on the system
+ * so that they may all be scheduled for reaping under those
+ * conditions.
+ */
kmem_taskq = taskq_create_instance("kmem_taskq", 0, 1, minclsyspri,
- 300, INT_MAX, TASKQ_PREPOPULATE);
+ 600, INT_MAX, TASKQ_PREPOPULATE);
}
void
diff --git a/usr/src/uts/common/os/vm_pageout.c b/usr/src/uts/common/os/vm_pageout.c
index f5ee76a2cb..2a93cd9061 100644
--- a/usr/src/uts/common/os/vm_pageout.c
+++ b/usr/src/uts/common/os/vm_pageout.c
@@ -18,14 +18,19 @@
*
* CDDL HEADER END
*/
+
+/*
+ * Copyright 2020 Oxide Computer Company
+ */
+
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2018 Joyent, Inc.
*/
-/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
@@ -60,6 +65,7 @@
#include <sys/mem_cage.h>
#include <sys/time.h>
#include <sys/zone.h>
+#include <sys/stdbool.h>
#include <vm/hat.h>
#include <vm/as.h>
@@ -592,6 +598,23 @@ static struct async_reqs *push_list; /* pending reqs */
static kmutex_t push_lock; /* protects req pool */
static kcondvar_t push_cv;
+/*
+ * If pageout() is stuck on a single push for this many seconds,
+ * pageout_deadman() will assume the system has hit a memory deadlock. If set
+ * to 0, the deadman will have no effect.
+ *
+ * Note that we are only looking for stalls in the calls that pageout() makes
+ * to VOP_PUTPAGE(). These calls are merely asynchronous requests for paging
+ * I/O, which should not take long unless the underlying strategy call blocks
+ * indefinitely for memory. The actual I/O request happens (or fails) later.
+ */
+uint_t pageout_deadman_seconds = 90;
+
+static uint_t pageout_stucktime = 0;
+static bool pageout_pushing = false;
+static uint64_t pageout_pushcount = 0;
+static uint64_t pageout_pushcount_seen = 0;
+
static int async_list_size = 256; /* number of async request structs */
static void pageout_scanner(void *);
@@ -902,6 +925,7 @@ pageout()
}
push_list = arg->a_next;
arg->a_next = NULL;
+ pageout_pushing = true;
mutex_exit(&push_lock);
DTRACE_PROBE(pageout__push);
@@ -914,6 +938,8 @@ pageout()
VN_RELE(arg->a_vp);
mutex_enter(&push_lock);
+ pageout_pushing = false;
+ pageout_pushcount++;
arg->a_next = req_freelist; /* back on freelist */
req_freelist = arg;
push_list_size--;
@@ -1184,6 +1210,58 @@ loop:
}
/*
+ * The pageout deadman is run once per second by clock().
+ */
+void
+pageout_deadman(void)
+{
+ if (panicstr != NULL) {
+ /*
+ * There is no pageout after panic.
+ */
+ return;
+ }
+
+ if (pageout_deadman_seconds == 0) {
+ /*
+ * The deadman is not enabled.
+ */
+ return;
+ }
+
+ if (!pageout_pushing) {
+ goto reset;
+ }
+
+ /*
+ * We are pushing a page. Check to see if it is the same call we saw
+ * last time we looked:
+ */
+ if (pageout_pushcount != pageout_pushcount_seen) {
+ /*
+ * It is a different call from the last check, so we are not
+ * stuck.
+ */
+ goto reset;
+ }
+
+ if (++pageout_stucktime >= pageout_deadman_seconds) {
+ panic("pageout_deadman: stuck pushing the same page for %d "
+ "seconds (freemem is %lu)", pageout_deadman_seconds,
+ freemem);
+ }
+
+ return;
+
+reset:
+ /*
+ * Reset our tracking state to reflect that we are not stuck:
+ */
+ pageout_stucktime = 0;
+ pageout_pushcount_seen = pageout_pushcount;
+}
+
+/*
* Look at the page at hand. If it is locked (e.g., for physical i/o),
* system (u., page table) or free, then leave it alone. Otherwise,
* if we are running the front hand, turn off the page's reference bit.
@@ -1206,7 +1284,7 @@ checkpage(struct page *pp, int whichhand)
/*
* Skip pages:
- * - associated with the kernel vnode since
+ * - associated with the kernel vnode since
* they are always "exclusively" locked.
* - that are free
* - that are shared more than po_share'd times
diff --git a/usr/src/uts/common/vm/vm_page.c b/usr/src/uts/common/vm/vm_page.c
index abccf82057..c2123db013 100644
--- a/usr/src/uts/common/vm/vm_page.c
+++ b/usr/src/uts/common/vm/vm_page.c
@@ -25,8 +25,8 @@
* Copyright 2018 Joyent, Inc.
*/
-/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
+/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
@@ -173,8 +173,8 @@ struct pcf {
kmutex_t pcf_lock; /* protects the structure */
uint_t pcf_count; /* page count */
uint_t pcf_wait; /* number of waiters */
- uint_t pcf_block; /* pcgs flag to page_free() */
- uint_t pcf_reserve; /* pages freed after pcf_block set */
+ uint_t pcf_block; /* pcgs flag to page_free() */
+ uint_t pcf_reserve; /* pages freed after pcf_block set */
uint_t pcf_fill[10]; /* to line up on the caches */
};
@@ -1353,7 +1353,7 @@ wakeup_pcgs(void)
* clock() on each TICK.
*/
void
-set_freemem()
+set_freemem(void)
{
struct pcf *p;
ulong_t t;
@@ -3922,8 +3922,8 @@ page_pp_unlock(
/*
* This routine reserves availrmem for npages;
- * flags: KM_NOSLEEP or KM_SLEEP
- * returns 1 on success or 0 on failure
+ * flags: KM_NOSLEEP or KM_SLEEP
+ * returns 1 on success or 0 on failure
*/
int
page_resv(pgcnt_t npages, uint_t flags)
@@ -3980,7 +3980,7 @@ void
page_pp_useclaim(
page_t *opp, /* original page frame losing lock */
page_t *npp, /* new page frame gaining lock */
- uint_t write_perm) /* set if vpage has PROT_WRITE */
+ uint_t write_perm) /* set if vpage has PROT_WRITE */
{
int payback = 0;
int nidx, oidx;
@@ -4734,7 +4734,7 @@ group_page_unlock(page_t *pp)
/*
* returns
- * 0 : on success and *nrelocp is number of relocated PAGESIZE pages
+ * 0 : on success and *nrelocp is number of relocated PAGESIZE pages
* ERANGE : this is not a base page
* EBUSY : failure to get locks on the page/pages
* ENOMEM : failure to obtain replacement pages
diff --git a/usr/src/uts/common/xen/io/xnf.c b/usr/src/uts/common/xen/io/xnf.c
index 2a53cc23e2..1d4bf4ef44 100644
--- a/usr/src/uts/common/xen/io/xnf.c
+++ b/usr/src/uts/common/xen/io/xnf.c
@@ -152,15 +152,6 @@
#include <io/xnf.h>
-#if defined(DEBUG) || defined(__lint)
-#define XNF_DEBUG
-#endif
-
-#ifdef XNF_DEBUG
-int xnf_debug = 0;
-xnf_t *xnf_debug_instance = NULL;
-#endif
-
/*
* On a 32 bit PAE system physical and machine addresses are larger
* than 32 bits. ddi_btop() on such systems take an unsigned long
@@ -564,9 +555,14 @@ xnf_data_txbuf_free_chain(xnf_t *xnfp, xnf_txbuf_t *txp)
}
static xnf_txbuf_t *
-xnf_data_txbuf_alloc(xnf_t *xnfp)
+xnf_data_txbuf_alloc(xnf_t *xnfp, int flag)
{
- xnf_txbuf_t *txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, KM_SLEEP);
+ xnf_txbuf_t *txp;
+
+ if ((txp = kmem_cache_alloc(xnfp->xnf_tx_buf_cache, flag)) == NULL) {
+ return (NULL);
+ }
+
txp->tx_type = TX_DATA;
txp->tx_next = NULL;
txp->tx_prev = NULL;
@@ -992,12 +988,6 @@ xnf_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
int err;
char cachename[32];
-#ifdef XNF_DEBUG
- if (xnf_debug & XNF_DEBUG_DDI)
- printf("xnf%d: attach(0x%p)\n", ddi_get_instance(devinfo),
- (void *)devinfo);
-#endif
-
switch (cmd) {
case DDI_RESUME:
xnfp = ddi_get_driver_private(devinfo);
@@ -1156,11 +1146,6 @@ xnf_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
"Ethernet controller");
#endif
-#ifdef XNF_DEBUG
- if (xnf_debug_instance == NULL)
- xnf_debug_instance = xnfp;
-#endif
-
return (DDI_SUCCESS);
failure_5:
@@ -1209,11 +1194,6 @@ xnf_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
{
xnf_t *xnfp; /* Our private device info */
-#ifdef XNF_DEBUG
- if (xnf_debug & XNF_DEBUG_DDI)
- printf("xnf_detach(0x%p)\n", (void *)devinfo);
-#endif
-
xnfp = ddi_get_driver_private(devinfo);
switch (cmd) {
@@ -1547,9 +1527,9 @@ xnf_tx_get_lookaside(xnf_t *xnfp, mblk_t *mp, size_t *plen)
xnf_buf_t *bd;
caddr_t bp;
- bd = xnf_buf_get(xnfp, KM_SLEEP, B_TRUE);
- if (bd == NULL)
+ if ((bd = xnf_buf_get(xnfp, KM_NOSLEEP, B_TRUE)) == NULL) {
return (NULL);
+ }
bp = bd->buf;
while (mp != NULL) {
@@ -1751,9 +1731,13 @@ xnf_tx_push_packet(xnf_t *xnfp, xnf_txbuf_t *head)
static xnf_txbuf_t *
xnf_mblk_copy(xnf_t *xnfp, mblk_t *mp)
{
- xnf_txbuf_t *txp = xnf_data_txbuf_alloc(xnfp);
+ xnf_txbuf_t *txp;
size_t length;
+ if ((txp = xnf_data_txbuf_alloc(xnfp, KM_NOSLEEP)) == NULL) {
+ return (NULL);
+ }
+
txp->tx_bdesc = xnf_tx_get_lookaside(xnfp, mp, &length);
if (txp->tx_bdesc == NULL) {
xnf_data_txbuf_free(xnfp, txp);
@@ -1786,7 +1770,9 @@ xnf_mblk_map(xnf_t *xnfp, mblk_t *mp, int *countp)
if (MBLKL(ml) == 0)
continue;
- txp = xnf_data_txbuf_alloc(xnfp);
+ if ((txp = xnf_data_txbuf_alloc(xnfp, KM_NOSLEEP)) == NULL) {
+ goto error;
+ }
if (head == NULL) {
head = txp;
@@ -1825,7 +1811,10 @@ xnf_mblk_map(xnf_t *xnfp, mblk_t *mp, int *countp)
goto error;
}
if (dma_cookie_prev != NULL) {
- txp = xnf_data_txbuf_alloc(xnfp);
+ if ((txp = xnf_data_txbuf_alloc(xnfp,
+ KM_NOSLEEP)) == NULL) {
+ goto error;
+ }
ASSERT(tail != NULL);
TXBUF_SETNEXT(tail, txp);
txp->tx_head = head;
@@ -2016,6 +2005,12 @@ pulledup:
* Defragment packet if it spans too many pages.
*/
mblk_t *newmp = msgpullup(mp, -1);
+ if (newmp == NULL) {
+ dev_err(xnfp->xnf_devinfo, CE_WARN,
+ "msgpullup() failed");
+ goto drop;
+ }
+
freemsg(mp);
mp = newmp;
xnfp->xnf_stat_tx_pullup++;
@@ -2166,12 +2161,6 @@ xnf_start(void *arg)
{
xnf_t *xnfp = arg;
-#ifdef XNF_DEBUG
- if (xnf_debug & XNF_DEBUG_TRACE)
- printf("xnf%d start(0x%p)\n",
- ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
-#endif
-
mutex_enter(&xnfp->xnf_rxlock);
mutex_enter(&xnfp->xnf_txlock);
@@ -2190,12 +2179,6 @@ xnf_stop(void *arg)
{
xnf_t *xnfp = arg;
-#ifdef XNF_DEBUG
- if (xnf_debug & XNF_DEBUG_TRACE)
- printf("xnf%d stop(0x%p)\n",
- ddi_get_instance(xnfp->xnf_devinfo), (void *)xnfp);
-#endif
-
mutex_enter(&xnfp->xnf_rxlock);
mutex_enter(&xnfp->xnf_txlock);
@@ -2571,7 +2554,7 @@ xnf_rx_collect(xnf_t *xnfp)
static int
xnf_alloc_dma_resources(xnf_t *xnfp)
{
- dev_info_t *devinfo = xnfp->xnf_devinfo;
+ dev_info_t *devinfo = xnfp->xnf_devinfo;
size_t len;
ddi_dma_cookie_t dma_cookie;
uint_t ncookies;
diff --git a/usr/src/uts/common/xen/io/xnf.h b/usr/src/uts/common/xen/io/xnf.h
index 63ce31020f..0c6f987ddb 100644
--- a/usr/src/uts/common/xen/io/xnf.h
+++ b/usr/src/uts/common/xen/io/xnf.h
@@ -50,10 +50,6 @@ extern "C" {
#define XNF_MAXPKT 16384
#define XNF_FRAMESIZE 1514 /* frame size including MAC header */
-/* DEBUG flags */
-#define XNF_DEBUG_DDI 0x01
-#define XNF_DEBUG_TRACE 0x02
-
/*
* Based on XEN_NETIF_NR_SLOTS_MIN in Linux. Packets that span more pages
* than this must be defragmented or dropped.