Map only part of the generated pack file at any point in time.
authorShawn O. Pearce <spearce@spearce.org>
Sun, 27 Aug 2006 09:53:48 +0000 (05:53 -0400)
committerShawn O. Pearce <spearce@spearce.org>
Sun, 14 Jan 2007 07:15:08 +0000 (02:15 -0500)
When generating a very large pack file (for example close to 1 GB
in size) it may be impossible for the kernel to find a contiguous
free range within a 32 bit address space for the mapping to be
located at.  This is especially problematic on large imports where
there is a lot of malloc activity occuring within the same process
and the malloc'd regions may straddle the previously mapped regions,
thereby creating large holes in the address space.

So instead we map only 128 MB of the pack at any given time.
This will likely increase the number of times the file gets mapped
(with additional system time required to update the page tables
more frequently) but will allow the program to handle packs up to
4 GB in size.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
fast-import.c

index 309f4d353bb32e33f5e98980afcda48ebd6c41a1..f3376c60ef42d8a6fb5acf1868db9f4d7c512d44 100644 (file)
@@ -215,8 +215,10 @@ static struct atom_str **atom_table;
 static int pack_fd;
 static unsigned long pack_size;
 static unsigned char pack_sha1[20];
-static void* pack_base;
-static size_t pack_mlen;
+static unsigned char* pack_base;
+static unsigned long pack_moff;
+static unsigned long pack_mlen = 128*1024*1024;
+static unsigned long page_size;
 
 /* Table of objects we've written. */
 static unsigned int object_entry_alloc = 1000;
@@ -676,23 +678,26 @@ static int store_object(
        return 0;
 }
 
-static void* map_pack(unsigned long offset)
+static unsigned char* map_pack(unsigned long offset, unsigned int *left)
 {
        if (offset >= pack_size)
                die("object offset outside of pack file");
-       if (offset >= pack_mlen) {
+       if (!pack_base
+                       || offset < pack_moff
+                       || (offset + 20) >= (pack_moff + pack_mlen)) {
                if (pack_base)
                        munmap(pack_base, pack_mlen);
-               /* round out how much we map to 16 MB units */
-               pack_mlen = pack_size;
-               if (pack_mlen & ((1 << 24) - 1))
-                       pack_mlen = ((pack_mlen >> 24) + 1) << 24;
-               pack_base = mmap(NULL,pack_mlen,PROT_READ,MAP_SHARED,pack_fd,0);
+               pack_moff = (offset / page_size) * page_size;
+               pack_base = mmap(NULL,pack_mlen,PROT_READ,MAP_SHARED,
+                       pack_fd,pack_moff);
                if (pack_base == MAP_FAILED)
                        die("Failed to map generated pack: %s", strerror(errno));
                remap_count++;
        }
-       return (char*)pack_base + offset;
+       offset -= pack_moff;
+       if (left)
+               *left = pack_mlen - offset;
+       return pack_base + offset;
 }
 
 static unsigned long unpack_object_header(unsigned long offset,
@@ -703,12 +708,12 @@ static unsigned long unpack_object_header(unsigned long offset,
        unsigned char c;
        unsigned long size;
 
-       c = *(unsigned char*)map_pack(offset++);
+       c = *map_pack(offset++, NULL);
        *type = (c >> 4) & 7;
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
-               c = *(unsigned char*)map_pack(offset++);
+               c = *map_pack(offset++, NULL);
                size += (c & 0x7f) << shift;
                shift += 7;
        }
@@ -725,8 +730,7 @@ static void *unpack_non_delta_entry(unsigned long o, unsigned long sz)
        result[sz] = 0;
 
        memset(&stream, 0, sizeof(stream));
-       stream.next_in = map_pack(o);
-       stream.avail_in = pack_mlen - o;
+       stream.next_in = map_pack(o, &stream.avail_in);
        stream.next_out = result;
        stream.avail_out = sz;
 
@@ -735,13 +739,12 @@ static void *unpack_non_delta_entry(unsigned long o, unsigned long sz)
                int st = inflate(&stream, Z_FINISH);
                if (st == Z_STREAM_END)
                        break;
-               if (st == Z_OK) {
-                       o = stream.next_in - (unsigned char*)pack_base;
-                       stream.next_in = map_pack(o);
-                       stream.avail_in = pack_mlen - o;
+               if (st == Z_OK || st == Z_BUF_ERROR) {
+                       o = stream.next_in - pack_base + pack_moff;
+                       stream.next_in = map_pack(o, &stream.avail_in);
                        continue;
                }
-               die("Error from zlib during inflate.");
+               die("Error %i from zlib during inflate.", st);
        }
        inflateEnd(&stream);
        if (stream.total_out != sz)
@@ -760,7 +763,7 @@ static void *unpack_delta_entry(unsigned long offset,
        void *delta_data, *base, *result;
        unsigned long base_size, result_size;
 
-       base_sha1 = (unsigned char*)map_pack(offset + 20) - 20;
+       base_sha1 = map_pack(offset, NULL);
        base_oe = find_object(base_sha1);
        if (!base_oe)
                die("I'm broken; I can't find a base I know must be here.");
@@ -1615,6 +1618,7 @@ int main(int argc, const char **argv)
 
        setup_ident();
        git_config(git_default_config);
+       page_size = getpagesize();
 
        for (i = 1; i < argc; i++) {
                const char *a = argv[i];