summaryrefslogtreecommitdiffstats
path: root/sys/kern/subr_rman.c
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2004-08-05 15:48:18 +0000
committerjhb <jhb@FreeBSD.org>2004-08-05 15:48:18 +0000
commit73d1afd6fde41cc69bc50752883e40b6fd763b5c (patch)
tree6fe7e7446b86a01ce428ac82cb84b80fe14a397b /sys/kern/subr_rman.c
parente2cd05d74450e2b1beaa74a28d5c74ba98e0d938 (diff)
downloadFreeBSD-src-73d1afd6fde41cc69bc50752883e40b6fd763b5c.zip
FreeBSD-src-73d1afd6fde41cc69bc50752883e40b6fd763b5c.tar.gz
Fix the code in rman that merges adjacent unallocated resources to use a
better check for 'adjacent'. The old code assumed that if two resources were adjacent in the linked list that they were also adjacent range wise. This is not true when a resource manager has to manage disparate regions. For example, the current interrupt code on i386/amd64 will instruct irq_rman to manage two disjoint regions: 0-1 and 3-15 for the non-APIC case. If IRQs 1 and 3 were allocated and then released, the old code would coalesce across the 1 to 3 boundary because the resources were adjacent in the linked list thus adding 2 to the area of resources that irq_rman managed as a side effect. The fix adds extra checks so that adjacent unallocated resources are only merged with the resource being freed if the start and end values of the resources also match up. The patch also consolidates the checks for adjacent resources being allocated.
Diffstat (limited to 'sys/kern/subr_rman.c')
-rw-r--r--sys/kern/subr_rman.c17
1 files changed, 12 insertions, 5 deletions
diff --git a/sys/kern/subr_rman.c b/sys/kern/subr_rman.c
index 62b576d..28fb7b9 100644
--- a/sys/kern/subr_rman.c
+++ b/sys/kern/subr_rman.c
@@ -539,13 +539,20 @@ int_rman_release_resource(struct rman *rm, struct resource *r)
/*
* Look at the adjacent resources in the list and see if our
- * segment can be merged with any of them.
+ * segment can be merged with any of them. If either of the
+ * resources is allocated or is not exactly adjacent then they
+ * cannot be merged with our segment.
*/
s = TAILQ_PREV(r, resource_head, r_link);
+ if (s != NULL && ((s->r_flags & RF_ALLOCATED) != 0 ||
+ s->r_end + 1 != r->r_start))
+ s = NULL;
t = TAILQ_NEXT(r, r_link);
+ if (t != NULL && ((t->r_flags & RF_ALLOCATED) != 0 ||
+ r->r_end + 1 != t->r_start))
+ t = NULL;
- if (s != NULL && (s->r_flags & RF_ALLOCATED) == 0
- && t != NULL && (t->r_flags & RF_ALLOCATED) == 0) {
+ if (s != NULL && t != NULL) {
/*
* Merge all three segments.
*/
@@ -553,13 +560,13 @@ int_rman_release_resource(struct rman *rm, struct resource *r)
TAILQ_REMOVE(&rm->rm_list, r, r_link);
TAILQ_REMOVE(&rm->rm_list, t, r_link);
free(t, M_RMAN);
- } else if (s != NULL && (s->r_flags & RF_ALLOCATED) == 0) {
+ } else if (s != NULL) {
/*
* Merge previous segment with ours.
*/
s->r_end = r->r_end;
TAILQ_REMOVE(&rm->rm_list, r, r_link);
- } else if (t != NULL && (t->r_flags & RF_ALLOCATED) == 0) {
+ } else if (t != NULL) {
/*
* Merge next segment with ours.
*/
OpenPOWER on IntegriCloud