diff options
author | Peter Grehan <grehan@FreeBSD.org> | 2005-05-19 07:21:46 +0000 |
---|---|---|
committer | Peter Grehan <grehan@FreeBSD.org> | 2005-05-19 07:21:46 +0000 |
commit | a9f9f6ce08b3d9999660a821dca882c4e11479a8 (patch) | |
tree | a224c63f5e700511607dc73f80604579cc8a9f97 /sys/boot | |
parent | 1aececdb5a8e2cacd9d0b7912b145bff57f4f7e9 (diff) | |
download | src-a9f9f6ce08b3d9999660a821dca882c4e11479a8.tar.gz src-a9f9f6ce08b3d9999660a821dca882c4e11479a8.zip |
Change ofw_readin/ofw_copyin to map the entire region before
copying, rather than a page at a time. This was creating far
too many single-page mappings, and eventually OFW overflowed
some internal data structure and refused to map any more.
The new algorithm creates far less mappings and fixed a bug
where multiple mappings for the same page would be created.
'Twas known this was a problem, but only became urgent when the
install CD's mfs_root grew large enough to cause the overflow.
Notes
Notes:
svn path=/head/; revision=146368
Diffstat (limited to 'sys/boot')
-rw-r--r-- | sys/boot/ofw/libofw/ofw_copy.c | 98 |
1 files changed, 74 insertions, 24 deletions
diff --git a/sys/boot/ofw/libofw/ofw_copy.c b/sys/boot/ofw/libofw/ofw_copy.c index ba8c91f700cc..2196c67cd884 100644 --- a/sys/boot/ofw/libofw/ofw_copy.c +++ b/sys/boot/ofw/libofw/ofw_copy.c @@ -42,35 +42,79 @@ __FBSDID("$FreeBSD$"); #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) -ssize_t -ofw_copyin(const void *src, vm_offset_t dest, const size_t len) +static int +ofw_mapmem(vm_offset_t dest, const size_t len) { - void *destp, *addr; - size_t dlen; - size_t resid; - - destp = (void *)(dest & ~PAGE_MASK); - resid = dest & PAGE_MASK; - dlen = roundup(len + resid, PAGE_SIZE); - - if (OF_call_method("claim", memory, 3, 1, destp, dlen, 0, &addr) - == -1) { - printf("ofw_copyin: physical claim failed\n"); - return (0); + void *destp, *addr; + size_t dlen; + size_t resid; + size_t nlen; + static vm_offset_t last_dest = 0; + static size_t last_len = 0; + + nlen = len; + /* + * Check to see if this region fits in a prior mapping. + * Allocations are generally sequential, so only check + * the last one. + */ + if (dest >= last_dest && + (dest + len) <= (last_dest + last_len)) { + return (0); } - if (OF_call_method("claim", mmu, 3, 1, destp, dlen, 0, &addr) == -1) { - printf("ofw_copyin: virtual claim failed\n"); - return (0); + /* + * Trim area covered by existing mapping, if any + */ + if (dest < (last_dest + last_len)) { + nlen -= (last_dest + last_len) - dest; + dest = last_dest + last_len; } - if (OF_call_method("map", mmu, 4, 0, destp, destp, dlen, 0) == -1) { - printf("ofw_copyin: map failed\n"); - return (0); - } + destp = (void *)(dest & ~PAGE_MASK); + resid = dest & PAGE_MASK; + + /* + * To avoid repeated mappings on small allocations, + * never map anything less than 16 pages at a time + */ + if ((nlen + resid) < PAGE_SIZE*8) { + dlen = PAGE_SIZE*8; + } else + dlen = roundup(nlen + resid, PAGE_SIZE); + + if (OF_call_method("claim", memory, 3, 1, destp, dlen, 0, &addr) + == -1) { + printf("ofw_mapmem: physical claim failed\n"); + return (ENOMEM); + } + + if (OF_call_method("claim", mmu, 3, 1, destp, dlen, 0, &addr) == -1) { + printf("ofw_mapmem: virtual claim failed\n"); + return (ENOMEM); + } + + if (OF_call_method("map", mmu, 4, 0, destp, destp, dlen, 0) == -1) { + printf("ofw_mapmem: map failed\n"); + return (ENOMEM); + } + + last_dest = (vm_offset_t) destp; + last_len = dlen; + + return (0); +} - bcopy(src, (void *)dest, len); - return(len); +ssize_t +ofw_copyin(const void *src, vm_offset_t dest, const size_t len) +{ + if (ofw_mapmem(dest, len)) { + printf("ofw_copyin: map error\n"); + return (0); + } + + bcopy(src, (void *)dest, len); + return(len); } ssize_t @@ -97,6 +141,12 @@ ofw_readin(const int fd, vm_offset_t dest, const size_t len) return(0); } + if (ofw_mapmem(dest, len)) { + printf("ofw_readin: map error\n"); + free(buf); + return (0); + } + for (resid = len; resid > 0; resid -= got, p += got) { get = min(chunk, resid); got = read(fd, buf, get); @@ -107,7 +157,7 @@ ofw_readin(const int fd, vm_offset_t dest, const size_t len) break; } - ofw_copyin(buf, p, got); + bcopy(buf, (void *)p, got); } free(buf); |