From 896ceebabd15f831c9a9fc3a9add565292e97c26 Mon Sep 17 00:00:00 2001 From: jhb Date: Mon, 11 Sep 2006 19:31:52 +0000 Subject: - Fix rman_manage_region() to be a lot more intelligent. It now checks for overlaps, but more importantly, it collapses adjacent free regions. This is needed to cope with BIOSen that split up ports for system devices (like IPMI controllers) across multiple system resource entries. - Now that rman_manage_region() is not so dumb, remove extra logic in the x86 nexus drivers to populate the IRQ rman that manually coalesced the regions. MFC after: 1 week --- sys/amd64/amd64/nexus.c | 14 +++----------- sys/i386/i386/nexus.c | 14 +++----------- sys/kern/subr_rman.c | 44 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/sys/amd64/amd64/nexus.c b/sys/amd64/amd64/nexus.c index f143d5e..478106c 100644 --- a/sys/amd64/amd64/nexus.c +++ b/sys/amd64/amd64/nexus.c @@ -140,7 +140,7 @@ DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); static int nexus_probe(device_t dev) { - int irq, last; + int irq; device_quiet(dev); /* suppress attach message for neatness */ @@ -173,18 +173,10 @@ nexus_probe(device_t dev) * We search for regions of existing IRQs and add those to the IRQ * resource manager. */ - last = -1; for (irq = 0; irq < NUM_IO_INTS; irq++) - if (intr_lookup_source(irq) != NULL) { - if (last == -1) - last = irq; - } else if (last != -1) { - if (rman_manage_region(&irq_rman, last, irq - 1) != 0) + if (intr_lookup_source(irq) != NULL) + if (rman_manage_region(&irq_rman, irq, irq) != 0) panic("nexus_probe irq_rman add"); - last = -1; - } - if (last != -1 && rman_manage_region(&irq_rman, last, irq - 1) != 0) - panic("nexus_probe irq_rman add"); /* * ISA DMA on PCI systems is implemented in the ISA part of each diff --git a/sys/i386/i386/nexus.c b/sys/i386/i386/nexus.c index 9def53d..6e1e281 100644 --- a/sys/i386/i386/nexus.c +++ b/sys/i386/i386/nexus.c @@ -144,7 +144,7 @@ DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); static int nexus_probe(device_t dev) { - int irq, last; + int irq; device_quiet(dev); /* suppress attach message for neatness */ @@ -177,18 +177,10 @@ nexus_probe(device_t dev) * We search for regions of existing IRQs and add those to the IRQ * resource manager. */ - last = -1; for (irq = 0; irq < NUM_IO_INTS; irq++) - if (intr_lookup_source(irq) != NULL) { - if (last == -1) - last = irq; - } else if (last != -1) { - if (rman_manage_region(&irq_rman, last, irq - 1) != 0) + if (intr_lookup_source(irq) != NULL) + if (rman_manage_region(&irq_rman, irq, irq) != 0) panic("nexus_probe irq_rman add"); - last = -1; - } - if (last != -1 && rman_manage_region(&irq_rman, last, irq - 1) != 0) - panic("nexus_probe irq_rman add"); /* * ISA DMA on PCI systems is implemented in the ISA part of each diff --git a/sys/kern/subr_rman.c b/sys/kern/subr_rman.c index c180eba..f59327a 100644 --- a/sys/kern/subr_rman.c +++ b/sys/kern/subr_rman.c @@ -155,7 +155,7 @@ rman_init(struct rman *rm) int rman_manage_region(struct rman *rm, u_long start, u_long end) { - struct resource_i *r, *s; + struct resource_i *r, *s, *t; DPRINTF(("rman_manage_region: <%s> request: start %#lx, end %#lx\n", rm->rm_descr, start, end)); @@ -167,15 +167,53 @@ rman_manage_region(struct rman *rm, u_long start, u_long end) r->r_rm = rm; mtx_lock(rm->rm_mtx); + + /* Skip entries before us. */ for (s = TAILQ_FIRST(&rm->rm_list); - s && s->r_end < r->r_start; + s && s->r_end + 1 < r->r_start; s = TAILQ_NEXT(s, r_link)) ; + /* If we ran off the end of the list, insert at the tail. */ if (s == NULL) { TAILQ_INSERT_TAIL(&rm->rm_list, r, r_link); } else { - TAILQ_INSERT_BEFORE(s, r, r_link); + /* Check for any overlap with the current region. */ + if (r->r_start <= s->r_end && r->r_end >= s->r_start) + return EBUSY; + + /* Check for any overlap with the next region. */ + t = TAILQ_NEXT(s, r_link); + if (t && r->r_start <= t->r_end && r->r_end >= t->r_start) + return EBUSY; + + /* + * See if this region can be merged with the next region. If + * not, clear the pointer. + */ + if (t && (r->r_end + 1 != t->r_start || t->r_flags != 0)) + t = NULL; + + /* See if we can merge with the current region. */ + if (s->r_end + 1 == r->r_start && s->r_flags == 0) { + /* Can we merge all 3 regions? */ + if (t != NULL) { + s->r_end = t->r_end; + TAILQ_REMOVE(&rm->rm_list, t, r_link); + free(r, M_RMAN); + free(t, M_RMAN); + } else { + s->r_end = r->r_end; + free(r, M_RMAN); + } + } else { + /* Can we merge with just the next region? */ + if (t != NULL) { + t->r_start = r->r_start; + free(r, M_RMAN); + } else + TAILQ_INSERT_BEFORE(s, r, r_link); + } } mtx_unlock(rm->rm_mtx); -- cgit v1.1