diff options
-rw-r--r-- | regress/sys/kern/extent/extest.awk | 9 | ||||
-rw-r--r-- | regress/sys/kern/extent/extest.exp | 12 | ||||
-rw-r--r-- | regress/sys/kern/extent/tests | 18 | ||||
-rw-r--r-- | sys/kern/subr_extent.c | 31 |
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) |