PERFORCE change 230037 for review
John Baldwin
jhb at FreeBSD.org
Fri Jun 21 21:40:05 UTC 2013
http://p4web.freebsd.org/@@230037?ac=10
Change 230037 by jhb at jhb_jhbbsd on 2013/06/21 21:39:25
Ok, the last idea of just reserving the alias ranges won't work
if we have nested bridges as the nested bridges need to be able
to allocate their windows. So, bite the bullet and fix this the
right way by only allocating the non-alias ranges from the parent.
This means a window can now have N resources and requires special
handling everywhere we frob the window. Thankfully, we get
lucky (I believe) and don't have to change the logic in
pcib_grow_window() where we pick front and back windows because
the granularity of I/O windows ensures you will always get 4 new
non-alias ranges each step of window growth. In theory this will
even properly handle a 32-bit capable I/O window that has the ISA
enable bit set in that it uses small resources for < 64k and one
regular resource for everything >= 64k.
This is just the first draft, not compile tested. :)
Affected files ...
.. //depot/projects/pci/sys/dev/pci/pci_pci.c#30 edit
.. //depot/projects/pci/sys/dev/pci/pcib_private.h#22 edit
Differences ...
==== //depot/projects/pci/sys/dev/pci/pci_pci.c#30 (text+ko) ====
@@ -241,52 +241,140 @@
}
static void
-pcib_alloc_isa_ranges(struct pcib_softc *sc, u_long start, u_long end)
+pcib_add_window_resources(struct pcib_window *w, struct resource **res,
+ int count)
+{
+ struct resource **newarray;
+ int error, i;
+
+ newarray = malloc(sizeof(struct resource *) * (w->count + count),
+ M_DEVBUF, M_WAITOK);
+ if (w->res != NULL)
+ bcopy(w->res, newarray, sizeof(struct resource *) * w->count);
+ bcopy(res, newarray + w->count, sizeof(struct resource *) * count);
+ free(w->res, M_DEVBUF);
+ w->res = newarray;
+ w->count += count;
+
+ for (i = 0; i < count; i++) {
+ error = rman_manage_region(&w->rman, rman_get_start(res[i]),
+ rman_get_end(res[i]));
+ if (error)
+ panic("Failed to add resource to rman");
+ }
+}
+
+typedef void (*nonisa_callback)(u_long start, u_long end, void *arg);
+
+static void
+pcib_walk_nonisa_ranges(u_long start, u_long end, nonisa_callback *cb,
+ void *arg)
{
- struct resource *res;
u_long next_end;
- if (!(sc->bridgectl & PCIB_BCR_ISA_ENABLE))
- return (0);
-
/* ISA aliases are only in the lower 64KB of I/O space. */
- if (end > 65535)
- end = 65535;
-
- /* XXX */
- device_printf(sc->dev, "Reserving ISA aliases for %#lx-%#lx\n", start,
- end);
- while (start <= end) {
+ while (start <= MIN(end, 65535)) {
/*
- * Find the first address that aliases to 0x0100 and
- * reserve the space from that up to the alias with
- * 0x03ff. As a special case, addresses in the range
- * 0x000 - 0x0ff should also be reserved since those
- * are used for various system I/O devices in ISA
- * systems.
+ * If start is within an ISA alias range, move up to
+ * the start of the next non-alias range. As a
+ * special case, addresses in the range 0x000 - 0x0ff
+ * should also be skipped since those are used for
+ * various system I/O devices in ISA systems.
*/
- if (start >= 0x100 && (start & 0x300) == 0) {
- start &= ~0xfful;
- start |= 0x100;
+ if (start < 0x100 || (start & 0x300) != 0) {
+ start &= ~0x3fful;
+ start += 0x400;
}
- next_end = MIN(start | 0x3ff, end);
- //if (bootverbose)
- device_printf(sc->dev,
- "Reserving ISA alias %#lx-%#lx\n", start, next_end);
- res = rman_reserve_resource(&sc->io.rman, start, next_end,
- next_end - start + 1, 0, sc->dev);
- if (res == NULL)
- device_printf(sc->dev,
- "Failed to reserve ISA alias %#lx-%#lx\n", start,
- next_end);
+ next_end = MIN(start | 0xff, end);
+ cb(start, end, arg);
start = next_end + 1;
}
+
+ if (start <= end)
+ cb(start, end, arg);
+}
+
+static void
+count_ranges(u_long start, u_long end, void *arg)
+{
+ int *countp;
+
+ countp = arg;
+ *countp++;
+}
+
+struct alloc_state {
+ struct resource **res;
+ struct pcib_softc *sc;
+ int count, error;
+};
+
+static void
+alloc_ranges(u_long start, u_long end, void *arg)
+{
+ struct alloc_state *as;
+ struct pcib_window *w;
+ int rid;
+
+ as = arg;
+ if (as->error != 0)
+ return;
+
+ w = &as->sc->io;
+ rid = w->reg;
+ /* XXX */
+ if (bootverbose || 1)
+ device_printf(as->sc->dev,
+ "Allocating non-ISA range %#lx-%#lx\n", start, end);
+ as->res[as->count] = bus_alloc_resource(as->sc->dev, SYS_RES_IOPORT,
+ &rid, start, end, end - start + 1, 0);
+ if (as->res[as->count] == NULL)
+ as->error = ENXIO;
+ else
+ as->count++;
+}
+
+static int
+pcib_alloc_nonisa_ranges(struct pcib_softc *sc, u_long start, u_long end)
+{
+ struct alloc_state as;
+ int i, new_count;
+
+ /* XXX */
+ device_printf(sc->dev, "Allocating non-ISA ranges of %#lx-%#lx\n",
+ start, end);
+
+ /* First, see how many ranges we need. */
+ new_count = 0;
+ pcib_walk_nonisa_ranges(start, end, count_ranges, &new_count);
+
+ /* Second, allocate the ranges. */
+ as.res = malloc(sizeof(struct resource *) * new_count, M_DEVBUF,
+ M_WAITOK);
+ as.sc = sc;
+ as.count = 0;
+ as.error = 0;
+ pcib_walk_nonisa_ranges(start, end, alloc_ranges &as);
+ if (as.error != 0) {
+ for (i = 0; i < as.count; i++)
+ bus_release_resource(sc->dev, SYS_RES_IOPORT,
+ sc->io.reg, as.res[i]);
+ free(as.res, M_DEVBUF);
+ return (as.error);
+ }
+ KASSERT(as.count == newcount, ("%s: count mismatch", __func__));
+
+ /* Third, add the ranges to the window. */
+ pcib_add_window_resources(&sc->io, as.res, as.count);
+ free(as.res, M_DEVBUF);
+ return (0);
}
static void
pcib_alloc_window(struct pcib_softc *sc, struct pcib_window *w, int type,
int flags, pci_addr_t max_address)
{
+ struct resource *res;
char buf[64];
int error, rid;
@@ -311,9 +399,15 @@
"initial %s window has too many bits, ignoring\n", w->name);
return;
}
- rid = w->reg;
- w->res = bus_alloc_resource(sc->dev, type, &rid, w->base, w->limit,
- w->limit - w->base + 1, flags);
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE)
+ (void)pcib_alloc_nonisa_ranges(sc, w->base, w->limit);
+ else {
+ rid = w->reg;
+ res = bus_alloc_resource(sc->dev, type, &rid, w->base, w->limit,
+ w->limit - w->base + 1, flags);
+ if (res != NULL)
+ pcib_add_window_resources(w, &res, 1);
+ }
if (w->res == NULL) {
device_printf(sc->dev,
"failed to allocate initial %s window: %#jx-%#jx\n",
@@ -324,11 +418,6 @@
return;
}
pcib_activate_window(sc, type);
-
- error = rman_manage_region(&w->rman, rman_get_start(w->res),
- rman_get_end(w->res));
- if (error)
- panic("Failed to initialize rman with resource");
}
/*
@@ -392,8 +481,6 @@
max = 0xffff;
}
pcib_alloc_window(sc, &sc->io, SYS_RES_IOPORT, 0, max);
- pcib_alloc_isa_ranges(sc, sc->io.base, sc->io.base +
- sc->io.limit - 1);
}
/* Read the existing memory window. */
@@ -937,9 +1024,184 @@
return (res);
}
+/* Allocate a fresh resource range for an unconfigured window. */
+static int
+pcib_alloc_new_window(struct pcib_softc *sc, struct pcib_window *w, int type,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct resource *res;
+ u_long base, limit;
+ int error, rid;
+
+ /*
+ * If this is an I/O window on a bridge with ISA enable set
+ * and the start address is below 64k, then try to allocate an
+ * initial window of 0x1000 bytes long starting at address
+ * 0xf000 and walking down. Note that if the original request
+ * was larger than the non-aliased range size of 0x100 our
+ * caller would have raised the start address up to 64k
+ * already.
+ */
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE &&
+ start < 65536) {
+ for (base = 0xf000; base >= 0; base -= 0x1000) {
+ limit = base + 0xfff;
+
+ /*
+ * Skip ranges that wouldn't work for the
+ * original request. Note that the actual
+ * window that overlaps are the non-alias
+ * ranges within [base, limit], so this isn't
+ * quite a simple comparison.
+ */
+ if (start + count > limit - 0x400)
+ continue;
+ if (base == 0) {
+ /*
+ * The first open region for the window at
+ * 0 is 0x400-0x4ff.
+ */
+ if (end - count < 0x400)
+ continue;
+ } else {
+ if (end - count < base)
+ continue;
+ }
+
+ if (pcib_alloc_nonisa_ranges(sc, base, limit) == 0) {
+ w->base = base;
+ w->limit = limit;
+ return (0);
+ }
+ }
+ }
+
+ if (RF_ALIGNMENT(flags) < w->step) {
+ flags &= ~RF_ALIGNMENT_MASK;
+ flags |= RF_ALIGNMENT_LOG2(w->step);
+ }
+ start &= ~wmask;
+ end |= wmask;
+ count = roundup2(count, 1ul << w->step);
+ rid = w->reg;
+ res = bus_alloc_resource(sc->dev, type, &rid, start, end, count,
+ flags & ~RF_ACTIVE);
+ if (res == NULL) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "failed to allocate initial %s window (%#lx-%#lx,%#lx)\n",
+ w->name, start, end, count);
+ return (ENXIO);
+ }
+ if (bootverbose)
+ device_printf(sc->dev,
+ "allocated initial %s window of %#lx-%#lx\n",
+ w->name, rman_get_start(res), rman_get_end(res));
+ pcib_add_window_resources(w, &res, 1);
+ pcib_activate_window(sc, type);
+ w->base = rman_get_start(res);
+ w->limit = rman_get_end(res);
+ return (0);
+}
+
+/* Try to expand an existing window to the requested base and limit. */
+static int
+pcib_expand_window(struct pcib_softc *sc, struct pcib_window *w, int type,
+ u_long base, u_long limit)
+{
+ struct resource *res;
+ int error, i, force_64k_base;
+
+ KASSERT(base <= w->base && limit >= w->limit,
+ ("attempting to shrink window"));
+
+ /*
+ * XXX: pcib_grow_window() doesn't try to do this anyway and
+ * the error handling for all the edge cases would be tedious.
+ */
+ KASSERT(limit == w->limit || base == w->base,
+ ("attempting to grow both ends of a window"));
+
+ /*
+ * Yet more special handling for requests to expand an I/O
+ * window behind an ISA-enabled bridge. Since I/O windows
+ * have to grow in 0x1000 increments and the end of the 0xffff
+ * range is an alias, growing a window below 64k will always
+ * result in allocating new resources and never adjusting an
+ * existing resource.
+ */
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE &&
+ limit <= 65535 || (base <= 65535 && base != w->base)) {
+ KASSERT(limit == w->limit || limit <= 65535,
+ ("attempting to grow both ends across 64k ISA alias"));
+
+ if (base != w->base)
+ error = pcib_alloc_nonisa_ranges(sc, base, w->base - 1);
+ else
+ error = pcib_alloc_nonisa_ranges(sc, w->limit + 1,
+ limit);
+ if (error == 0) {
+ w->base = base;
+ w->limit = limit;
+ }
+ return (error);
+ }
+
+ /*
+ * Find the existing resource to adjust. Usually there is only one,
+ * but for an ISA-enabled bridge we might be growing the I/O window
+ * above 64k and need to find the existing resource that maps all
+ * of the area above 64k.
+ */
+ for (i = 0; i < w->count; i++) {
+ res = w->res[i];
+ if (rman_get_end(res) == w->limit)
+ break;
+ }
+ KASSERT(i != w->count, ("did not find existing resource"));
+
+ /*
+ * Usually the resource we found should match the window's
+ * existing range. The one exception is the ISA-enabled case
+ * mentioned above in which case the resource should start at
+ * 64k.
+ */
+ if (type == SYS_RES_IOPORT && sc->bridgectl & PCIB_BCR_ISA_ENABLE &&
+ w->base <= 65535) {
+ KASSERT(rman_get_start(res) == 65536,
+ ("existing resource mismatch"));
+ force_64k_base = 1;
+ } else {
+ KASSERT(w->base == rman_get_start(res),
+ ("existing resource mismatch"));
+ force_64k_base = 0;
+ }
+
+ error = bus_adjust_resource(sc->dev, type, res, force_64k_base ?
+ rman_get_start(res) : base, limit);
+ if (error)
+ return (error);
+
+ /* Add the newly allocated region to the resource manager. */
+ if (w->base != base) {
+ error = rman_manage_region(&w->rman, base, w->base - 1);
+ w->base = base;
+ } else {
+ error = rman_manage_region(&w->rman, w->limit + 1, limit);
+ w->limit = rman_get_end(w->res);
+ }
+ if (error) {
+ if (bootverbose)
+ device_printf(sc->dev,
+ "failed to expand %s resource manager\n", w->name);
+ (void)bus_adjust_resource(sc->dev, type, res, force_64k_base ?
+ rman_get_start(res) : w->base, w->limit);
+ }
+ return (error);
+}
+
/*
* Attempt to grow a window to make room for a given resource request.
- * The 'step' parameter is log_2 of the desired I/O window's alignment.
*/
static int
pcib_grow_window(struct pcib_softc *sc, struct pcib_window *w, int type,
@@ -971,40 +1233,10 @@
* aligned space for this resource.
*/
if (w->res == NULL) {
- if (RF_ALIGNMENT(flags) < w->step) {
- flags &= ~RF_ALIGNMENT_MASK;
- flags |= RF_ALIGNMENT_LOG2(w->step);
- }
- start &= ~wmask;
- end |= wmask;
- count = roundup2(count, 1ul << w->step);
- rid = w->reg;
- w->res = bus_alloc_resource(sc->dev, type, &rid, start, end,
- count, flags & ~RF_ACTIVE);
- if (w->res == NULL) {
- if (bootverbose)
- device_printf(sc->dev,
- "failed to allocate initial %s window (%#lx-%#lx,%#lx)\n",
- w->name, start, end, count);
- return (ENXIO);
- }
- if (bootverbose)
- device_printf(sc->dev,
- "allocated initial %s window of %#lx-%#lx\n",
- w->name, rman_get_start(w->res),
- rman_get_end(w->res));
- error = rman_manage_region(&w->rman, rman_get_start(w->res),
- rman_get_end(w->res));
- if (error) {
- if (bootverbose)
- device_printf(sc->dev,
- "failed to add initial %s window to rman\n",
- w->name);
- bus_release_resource(sc->dev, type, w->reg, w->res);
- w->res = NULL;
+ error = pcib_alloc_new_window(sc, w, type, start, end, count,
+ flags);
+ if (error)
return (error);
- }
- pcib_activate_window(sc, type);
goto updatewin;
}
@@ -1027,10 +1259,10 @@
"attempting to grow %s window for (%#lx-%#lx,%#lx)\n",
w->name, start, end, count);
align = 1ul << RF_ALIGNMENT(flags);
- if (start < rman_get_start(w->res)) {
+ if (start < w->base) {
if (rman_first_free_region(&w->rman, &start_free, &end_free) !=
- 0 || start_free != rman_get_start(w->res))
- end_free = rman_get_start(w->res);
+ 0 || start_free != w->base)
+ end_free = w->base;
if (end_free > end)
end_free = end + 1;
@@ -1051,15 +1283,15 @@
printf("\tfront candidate range: %#lx-%#lx\n",
front, end_free);
front &= ~wmask;
- front = rman_get_start(w->res) - front;
+ front = w->base - front;
} else
front = 0;
} else
front = 0;
- if (end > rman_get_end(w->res)) {
+ if (end > w->limit) {
if (rman_last_free_region(&w->rman, &start_free, &end_free) !=
- 0 || end_free != rman_get_end(w->res))
- start_free = rman_get_end(w->res) + 1;
+ 0 || end_free != w->limit)
+ start_free = w->limit + 1;
if (start_free < start)
start_free = start;
@@ -1079,7 +1311,7 @@
printf("\tback candidate range: %#lx-%#lx\n",
start_free, back);
back |= wmask;
- back -= rman_get_end(w->res);
+ back -= w->limit;
} else
back = 0;
} else
@@ -1092,16 +1324,14 @@
error = ENOSPC;
while (front != 0 || back != 0) {
if (front != 0 && (front <= back || back == 0)) {
- error = bus_adjust_resource(sc->dev, type, w->res,
- rman_get_start(w->res) - front,
- rman_get_end(w->res));
+ error = pcib_expand_window(sc, w, type, w->base - front,
+ w->limit);
if (error == 0)
break;
front = 0;
} else {
- error = bus_adjust_resource(sc->dev, type, w->res,
- rman_get_start(w->res),
- rman_get_end(w->res) + back);
+ error = pcib_expand_window(sc, w, type, w->base,
+ w->limit + back);
if (error == 0)
break;
back = 0;
@@ -1112,37 +1342,10 @@
return (error);
if (bootverbose)
device_printf(sc->dev, "grew %s window to %#lx-%#lx\n",
- w->name, rman_get_start(w->res), rman_get_end(w->res));
-
- /* Add the newly allocated region to the resource manager. */
- if (w->base != rman_get_start(w->res)) {
- KASSERT(w->limit == rman_get_end(w->res), ("both ends moved"));
- error = rman_manage_region(&w->rman, rman_get_start(w->res),
- w->base - 1);
- if (type == SYS_RES_IOPORT)
- pcib_alloc_isa_ranges(sc, rman_get_start(w->res),
- w->base - 1);
- } else {
- KASSERT(w->limit != rman_get_end(w->res),
- ("neither end moved"));
- error = rman_manage_region(&w->rman, w->limit + 1,
- rman_get_end(w->res));
- if (type == SYS_RES_IOPORT)
- pcib_alloc_isa_ranges(sc, w->limit + 1,
- rman_get_end(w->res));
- }
- if (error) {
- if (bootverbose)
- device_printf(sc->dev,
- "failed to expand %s resource manager\n", w->name);
- bus_adjust_resource(sc->dev, type, w->res, w->base, w->limit);
- return (error);
- }
+ w->name, w->base, w->limit);
updatewin:
- /* Save the new window. */
- w->base = rman_get_start(w->res);
- w->limit = rman_get_end(w->res);
+ /* Write the new window. */
KASSERT((w->base & wmask) == 0, ("start address is not aligned"));
KASSERT((w->limit & wmask) == wmask, ("end address is not aligned"));
pcib_write_windows(sc, w->mask);
==== //depot/projects/pci/sys/dev/pci/pcib_private.h#22 (text+ko) ====
@@ -73,7 +73,8 @@
pci_addr_t base; /* base address */
pci_addr_t limit; /* topmost address */
struct rman rman;
- struct resource *res;
+ struct resource **res;
+ int count; /* size of 'res' array */
int reg; /* resource id from parent */
int valid;
int mask; /* WIN_* bitmask of this window */
More information about the p4-projects
mailing list