summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--regress/sys/kern/extent/extest.awk9
-rw-r--r--regress/sys/kern/extent/extest.exp12
-rw-r--r--regress/sys/kern/extent/tests18
-rw-r--r--sys/kern/subr_extent.c31
4 files changed, 61 insertions, 9 deletions
diff --git a/regress/sys/kern/extent/extest.awk b/regress/sys/kern/extent/extest.awk
index e803e93267d..889f8656aad 100644
--- a/regress/sys/kern/extent/extest.awk
+++ b/regress/sys/kern/extent/extest.awk
@@ -1,4 +1,4 @@
-# $OpenBSD: extest.awk,v 1.2 2009/04/10 20:57:04 kettenis Exp $
+# $OpenBSD: extest.awk,v 1.3 2019/09/11 12:30:34 kettenis Exp $
# $NetBSD: extest.awk,v 1.6 2002/02/21 03:59:25 mrg Exp $
BEGIN {
@@ -67,7 +67,12 @@ $1 == "alloc_subregion" {
}
$1 == "free" {
- printf("error = extent_free(ex, %s, %s, 0);\n", $2, $3)
+ if ($4 == "") {
+ flags = "0";
+ } else {
+ flags = $4;
+ }
+ printf("error = extent_free(ex, %s, %s, %s);\n", $2, $3, flags)
printf("if (error)\n\tprintf(\"error: %%s\\n\", strerror(error));\n")
}
diff --git a/regress/sys/kern/extent/extest.exp b/regress/sys/kern/extent/extest.exp
index 4e4f6a5a462..cd7eb70e91a 100644
--- a/regress/sys/kern/extent/extest.exp
+++ b/regress/sys/kern/extent/extest.exp
@@ -1,4 +1,4 @@
-# $OpenBSD: extest.exp,v 1.4 2009/10/13 20:53:40 miod Exp $
+# $OpenBSD: extest.exp,v 1.5 2019/09/11 12:30:34 kettenis Exp $
# $NetBSD: extest.exp,v 1.9 2005/03/15 18:27:23 bouyer Exp $
# real output must start in line 5
@@ -92,3 +92,13 @@ extent `test16' (0x0 - 0xffffffff), flags = 0x0
output for test17
extent `test17' (0x0 - 0xffffffffffffffff), flags = 0x0
0x0 - 0xffffffffffffffff
+output for test18
+extent `test18' (0x0 - 0xffff), flags = 0x0
+ 0x0 - 0xcff
+ 0xf000 - 0xffff
+output for test19
+extent `test19' (0x0 - 0xffff), flags = 0x0
+ 0x0 - 0xcff
+output for test20
+extent `test20' (0x0 - 0xffff), flags = 0x0
+ 0xf000 - 0xffff
diff --git a/regress/sys/kern/extent/tests b/regress/sys/kern/extent/tests
index 9f85853aade..5b2fb93f9ac 100644
--- a/regress/sys/kern/extent/tests
+++ b/regress/sys/kern/extent/tests
@@ -1,4 +1,4 @@
-# $OpenBSD: tests,v 1.5 2009/10/13 20:53:40 miod Exp $
+# $OpenBSD: tests,v 1.6 2019/09/11 12:30:35 kettenis Exp $
# $NetBSD: tests,v 1.9 2005/03/15 18:27:23 bouyer Exp $
#fill up an extent, should coalesce into one allocation
@@ -136,3 +136,19 @@ print
extent test17 0x00000000 -1L EX_FILLED
alloc_region 0 0x4000 EX_CONFLICTOK
print
+
+# Check freeing overkapping regions from a filled extent
+extent test18 0x0000 0xffff EX_FILLED
+free 0x164e 0x2
+free 0x0d00 0xe300 EX_CONFLICTOK
+print
+
+extent test19 0x0000 0xffff EX_FILLED
+free 0x164e 0x2
+free 0x0d00 0xf300 EX_CONFLICTOK
+print
+
+extent test20 0x0000 0xffff EX_FILLED
+free 0x164e 0x2
+free 0x0000 0xf000 EX_CONFLICTOK
+print
diff --git a/sys/kern/subr_extent.c b/sys/kern/subr_extent.c
index 09132e69dd5..43e4aa20357 100644
--- a/sys/kern/subr_extent.c
+++ b/sys/kern/subr_extent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_extent.c,v 1.61 2019/08/28 22:22:43 kettenis Exp $ */
+/* $OpenBSD: subr_extent.c,v 1.62 2019/09/11 12:30:34 kettenis Exp $ */
/* $NetBSD: subr_extent.c,v 1.7 1996/11/21 18:46:34 cgd Exp $ */
/*-
@@ -962,6 +962,7 @@ int
extent_free(struct extent *ex, u_long start, u_long size, int flags)
{
struct extent_region *rp, *nrp = NULL;
+ struct extent_region *tmp;
u_long end = start + (size - 1);
int exflags;
int error = 0;
@@ -1019,8 +1020,12 @@ extent_free(struct extent *ex, u_long start, u_long size, int flags)
*
* Cases 2, 3, and 4 require that the EXF_NOCOALESCE flag
* is not set.
+ *
+ * If the EX_CONFLICTOK flag is set, partially overlapping
+ * regions are allowed. This is handled in cases 1a, 2a and
+ * 3a below.
*/
- LIST_FOREACH(rp, &ex->ex_regions, er_link) {
+ LIST_FOREACH_SAFE(rp, &ex->ex_regions, er_link, tmp) {
/*
* Save ourselves some comparisons; does the current
* region end before chunk to be freed begins? If so,
@@ -1080,12 +1085,28 @@ extent_free(struct extent *ex, u_long start, u_long size, int flags)
nrp = NULL;
goto done;
}
+
+ if ((flags & EX_CONFLICTOK) == 0)
+ continue;
+
+ /* Case 1a. */
+ if ((start <= rp->er_start && end >= rp->er_end)) {
+ LIST_REMOVE(rp, er_link);
+ extent_free_region_descriptor(ex, rp);
+ continue;
+ }
+
+ /* Case 2a. */
+ if ((start <= rp->er_start) && (end >= rp->er_start))
+ rp->er_start = (end + 1);
+
+ /* Case 3a. */
+ if ((start <= rp->er_end) && (end >= rp->er_end))
+ rp->er_end = (start - 1);
}
- if (flags & EX_CONFLICTOK) {
- error = EINVAL;
+ if (flags & EX_CONFLICTOK)
goto done;
- }
/* Region not found, or request otherwise invalid. */
#if defined(DIAGNOSTIC) || defined(DDB)