From fcd83e98ec23a28b546877a0b93f0025e233c6a9 Mon Sep 17 00:00:00 2001 From: grehan Date: Thu, 19 May 2005 07:21:46 +0000 Subject: 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. --- sys/boot/ofw/libofw/ofw_copy.c | 98 +++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 24 deletions(-) (limited to 'sys/boot/ofw') diff --git a/sys/boot/ofw/libofw/ofw_copy.c b/sys/boot/ofw/libofw/ofw_copy.c index ba8c91f..2196c67 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); -- cgit v1.1