As those of you who watch CVS will be aware Wayne has been making progress in reducing memory requirements of rsync. Much of what he has done has been the product of discussions between he and myself that started a month ago with John Van Essen. Most recently Wayne has changed how the file_struct and its associated data are allocated, eliminating the string areas. Most of these changes have been small and relatively low impact although combining the allocation of the file_struct with the strings does impact the memory management of file_struct. Attached is a patch that implements the next step. It alters flist memory management and introduces a MM pool layer that reduces malloc overhead and allows destructors to actually release memory to the OS. The patch adds a couple of new files so use patch -p1 and rerun ./configure after patching. -- ________________________________________________________________ J.W. Schultz Pegasystems Technologies email address: jw@pegasys.ws Remember Cernan and Schmitt -------------- next part -------------- diff -rupNP --exclude-from cvs/.ignore cvs/Makefile.in pool2/Makefile.in --- cvs/Makefile.in Tue Feb 3 16:34:24 2004 +++ pool2/Makefile.in Tue Feb 3 16:33:00 2004 @@ -27,7 +27,7 @@ VERSION=@VERSION@ HEADERS=byteorder.h config.h errcode.h proto.h rsync.h LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \ - lib/permstring.o @LIBOBJS@ + lib/permstring.o lib/pool_alloc.o @LIBOBJS@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \ zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \ zlib/zutil.o zlib/adler32.o diff -rupNP --exclude-from cvs/.ignore cvs/backup.c pool2/backup.c --- cvs/backup.c Wed Feb 4 03:49:36 2004 +++ pool2/backup.c Wed Feb 4 22:11:26 2004 @@ -189,6 +189,7 @@ static int keep_backup(char *fname) backup_dir[--backup_dir_len] = '\0'; if (verbose > 0) rprintf(FINFO, "backup_dir is %s\n", backup_dir); + initialised = 1; } @@ -199,7 +200,7 @@ static int keep_backup(char *fname) if (do_stat(fname, &st)) return 1; #endif - file = make_file(fname, NO_EXCLUDES); + file = make_file(fname, NULL, NO_EXCLUDES); /* the file could have disappeared */ if (!file) return 1; @@ -282,7 +283,7 @@ static int keep_backup(char *fname) } } set_perms(keep_name, file, NULL, 0); - free_file(file, FREE_STRUCT); + free(file); if (verbose > 1) rprintf(FINFO, "keep_backup %s -> %s\n", fname, keep_name); diff -rupNP --exclude-from cvs/.ignore cvs/batch.c pool2/batch.c --- cvs/batch.c Thu Feb 5 20:47:31 2004 +++ pool2/batch.c Thu Feb 5 20:51:14 2004 @@ -136,9 +136,7 @@ struct file_list *create_flist_from_batc exit_cleanup(1); } - batch_flist = new(struct file_list); - if (!batch_flist) - out_of_memory("create_flist_from_batch"); + batch_flist = flist_new(WITH_HLINK, "create_flist_from_batch"); save_read = stats.total_read; save_pv = protocol_version; @@ -153,9 +151,9 @@ struct file_list *create_flist_from_batc for (i = 0; (flags = read_byte(f)) != 0; i++) { if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS)) flags |= read_byte(f) << 8; - receive_file_entry(&batch_flist->files[i], flags, f); + receive_file_entry(&batch_flist->files[i], flags, batch_flist, f); } - receive_file_entry(NULL, 0, 0); /* Signal that we're done. */ + receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */ protocol_version = save_pv; stats.total_read = save_read; diff -rupNP --exclude-from cvs/.ignore cvs/flist.c pool2/flist.c --- cvs/flist.c Wed Feb 4 17:39:32 2004 +++ pool2/flist.c Thu Feb 5 18:41:21 2004 @@ -76,13 +76,12 @@ static unsigned int min_file_struct_len; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); static void output_flist(struct file_list *flist); - void init_flist(void) { - struct file_struct f; + struct file_struct f; - /* Figure out how big the file_struct is without trailing padding */ - min_file_struct_len = ((char*)&f.flags - (char*)&f) + sizeof f.flags; + /* Figure out how big the file_struct is without trailing padding */ + min_file_struct_len = ((char*)&f.flags - (char*)&f) + sizeof f.flags; } @@ -499,7 +498,8 @@ void send_file_entry(struct file_struct -void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f) +void receive_file_entry(struct file_struct **fptr, unsigned short flags, + struct file_list *flist, int f) { static time_t modtime; static mode_t mode; @@ -616,12 +616,12 @@ void receive_file_entry(struct file_stru idev_len = 0; sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0; - file_struct_len = idev_len? sizeof file[0] : min_file_struct_len; + file_struct_len = min_file_struct_len; alloc_len = file_struct_len + dirname_len + basename_len - + linkname_len + sum_len + idev_len; - if (!(bp = new_array(char, alloc_len))) - out_of_memory("receive_file_entry"); + + linkname_len + sum_len; + bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry"); + file = *fptr = (struct file_struct *)bp; memset(bp, 0, min_file_struct_len); bp += file_struct_len; @@ -634,9 +634,9 @@ void receive_file_entry(struct file_stru file->gid = gid; #if SUPPORT_HARD_LINKS - if (idev_len) { - file->link_u.idev = (struct idev *)bp; - bp += idev_len; + if (idev_len && flist->hlink_pool) { + file->link_u.idev = pool_talloc(flist->hlink_pool, + struct idev, 1, "inode_table"); } #endif @@ -668,15 +668,19 @@ void receive_file_entry(struct file_stru #if SUPPORT_HARD_LINKS if (idev_len) { + INO64_T inode; if (protocol_version < 26) { dev = read_int(f); - file->F_INODE = read_int(f); + inode = read_int(f); } else { if (!(flags & XMIT_SAME_DEV)) dev = read_longint(f); - file->F_INODE = read_longint(f); + inode = read_longint(f); + } + if (flist->hlink_pool) { + file->F_INODE = inode; + file->F_DEV = dev; } - file->F_DEV = dev; } #endif @@ -720,7 +724,8 @@ void receive_file_entry(struct file_stru * statting directories if we're not recursing, but this is not a very * important case. Some systems may not have d_type. **/ -struct file_struct *make_file(char *fname, int exclude_level) +struct file_struct *make_file(char *fname, + struct file_list *flist, int exclude_level) { static char *lastdir; static int lastdir_len = -1; @@ -734,6 +739,7 @@ struct file_struct *make_file(char *fnam char *basename, *dirname, *bp; unsigned short flags = 0; + if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname - flist_dir_len) { rprintf(FINFO, "skipping overly long name: %s\n", fname); @@ -830,12 +836,18 @@ struct file_struct *make_file(char *fnam idev_len = 0; sum_len = always_checksum && S_ISREG(st.st_mode) ? MD4_SUM_LENGTH : 0; - file_struct_len = idev_len? sizeof file[0] : min_file_struct_len; + file_struct_len = min_file_struct_len; alloc_len = file_struct_len + dirname_len + basename_len - + linkname_len + sum_len + idev_len; - if (!(bp = new_array(char, alloc_len))) - out_of_memory("receive_file_entry"); + + linkname_len + sum_len; + if (flist) { + bp = pool_alloc(flist->file_pool, alloc_len, + "receive_file_entry"); + } else { + if (!(bp = new_array(char, alloc_len))) + out_of_memory("receive_file_entry"); + } + file = (struct file_struct *)bp; memset(bp, 0, min_file_struct_len); bp += file_struct_len; @@ -848,9 +860,9 @@ struct file_struct *make_file(char *fnam file->gid = st.st_gid; #if SUPPORT_HARD_LINKS - if (idev_len) { - file->link_u.idev = (struct idev *)bp; - bp += idev_len; + if (idev_len && flist && flist->hlink_pool) { + file->link_u.idev = pool_talloc(flist->hlink_pool, + struct idev, 1, "inode_table"); file->F_DEV = st.st_dev; file->F_INODE = st.st_ino; } @@ -905,9 +917,8 @@ void send_file_name(int f, struct file_l extern int delete_excluded; /* f is set to -1 when calculating deletion file list */ - file = make_file(fname, - f == -1 && delete_excluded? SERVER_EXCLUDES - : ALL_EXCLUDES); + file = make_file(fname, flist, + f == -1 && delete_excluded? SERVER_EXCLUDES : ALL_EXCLUDES); if (!file) return; @@ -1026,9 +1037,12 @@ struct file_list *send_file_list(int f, start_write = stats.total_written; - flist = flist_new(); + flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK, + "send_file_list"); if (f != -1) { + flist->hlink_pool = pool_create(128 * 1024, + sizeof (struct idev), out_of_memory, POOL_INTERN); io_start_buffering_out(f); if (filesfrom_fd >= 0) { if (argv[0] && !push_dir(argv[0])) { @@ -1177,6 +1191,12 @@ struct file_list *send_file_list(int f, finish_filelist_progress(flist); } + if (flist->hlink_pool) + { + pool_destroy(flist->hlink_pool); + flist->hlink_pool = NULL; + } + clean_flist(flist, 0, 0); if (f != -1) { @@ -1216,10 +1236,10 @@ struct file_list *recv_file_list(int f) start_read = stats.total_read; - flist = new(struct file_list); - if (!flist) - goto oom; + flist = flist_new(WITH_HLINK, "recv_file_list"); + flist->hlink_pool = pool_create(128 * 1024, sizeof (struct idev), + out_of_memory, POOL_INTERN); flist->count = 0; flist->malloced = 1000; flist->files = new_array(struct file_struct *, flist->malloced); @@ -1234,7 +1254,7 @@ struct file_list *recv_file_list(int f) if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS)) flags |= read_byte(f) << 8; - receive_file_entry(&flist->files[i], flags, f); + receive_file_entry(&flist->files[i], flags, flist, f); if (S_ISREG(flist->files[i]->mode)) stats.total_size += flist->files[i]->length; @@ -1248,7 +1268,7 @@ struct file_list *recv_file_list(int f) f_name(flist->files[i])); } } - receive_file_entry(NULL, 0, 0); /* Signal that we're done. */ + receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */ if (verbose > 2) rprintf(FINFO, "received %d names\n", flist->count); @@ -1337,34 +1357,42 @@ int flist_find(struct file_list *flist, return -1; } - /* - * Free up any resources a file_struct has allocated, and optionally free - * it up as well. + * Free up any resources a file_struct has allocated + * and clear the file. */ -void free_file(struct file_struct *file, int free_the_struct) +void clear_file(int i, struct file_list *flist) { - if (free_the_struct) - free(file); - else - memset(file, 0, min_file_struct_len); + if (flist->hlink_pool && flist->files[i]->link_u.idev) + pool_free(flist->hlink_pool, 0, flist->files[i]->link_u.idev); + memset(flist->files[i], 0, min_file_struct_len); } /* * allocate a new file list */ -struct file_list *flist_new(void) +struct file_list *flist_new(int with_hlink, char *msg) { struct file_list *flist; flist = new(struct file_list); if (!flist) - out_of_memory("send_file_list"); + out_of_memory(msg); - flist->count = 0; - flist->malloced = 0; - flist->files = NULL; + memset(flist, 0, sizeof (struct file_list)); + + if (!(flist->file_pool = pool_create(FILE_EXTENT, 0, + out_of_memory, POOL_INTERN))) + out_of_memory(msg); + +#if SUPPORT_HARD_LINKS + if (with_hlink && preserve_hard_links) { + if (!(flist->hlink_pool = pool_create(HLINK_EXTENT, 0, + out_of_memory, POOL_INTERN))) + out_of_memory(msg); + } +#endif return flist; } @@ -1374,9 +1402,8 @@ struct file_list *flist_new(void) */ void flist_free(struct file_list *flist) { - int i; - for (i = 1; i < flist->count; i++) - free_file(flist->files[i], FREE_STRUCT); + pool_destroy(flist->file_pool); + pool_destroy(flist->hlink_pool); free(flist->files); free(flist); } @@ -1416,7 +1443,8 @@ static void clean_flist(struct file_list * else deletions will mysteriously fail with -R). */ if (flist->files[i]->flags & FLAG_TOP_DIR) flist->files[prev_i]->flags |= FLAG_TOP_DIR; - free_file(flist->files[i], CLEAR_STRUCT); + + clear_file(i, flist); } else prev_i = i; } Binary files cvs/getgroups and pool2/getgroups differ diff -rupNP --exclude-from cvs/.ignore cvs/hlink.c pool2/hlink.c --- cvs/hlink.c Mon Feb 2 22:21:19 2004 +++ pool2/hlink.c Wed Feb 4 01:35:49 2004 @@ -46,26 +46,41 @@ int hlink_count; /* Analyze the data in the hlink_list[], remove items that aren't multiply * linked, and replace the dev+inode data with the hlindex+next linked list. */ -static void link_idev_data(void) +static void link_idev_data(struct file_list *flist) { struct file_struct *head; int from, to, start; + alloc_pool_t hlink_pool; + alloc_pool_t idev_pool = flist->hlink_pool; + + hlink_pool = pool_create(128 * 1024, sizeof (struct hlink), + out_of_memory, POOL_INTERN); + for (from = to = 0; from < hlink_count; from++) { start = from; head = hlink_list[start]; while (from < hlink_count-1 && LINKED(hlink_list[from], hlink_list[from+1])) { + pool_free(idev_pool, 0, hlink_list[from]->link_u.idev); + hlink_list[from]->link_u.links = pool_talloc(hlink_pool, + struct hlink, 1, "hlink_list"); + hlink_list[from]->F_HLINDEX = to; hlink_list[from]->F_NEXT = hlink_list[from+1]; from++; } if (from > start) { + pool_free(idev_pool, 0, hlink_list[from]->link_u.idev); + hlink_list[from]->link_u.links = pool_talloc(hlink_pool, + struct hlink, 1, "hlink_list"); + hlink_list[from]->F_HLINDEX = to; hlink_list[from]->F_NEXT = head; hlink_list[from]->flags |= FLAG_HLINK_EOL; hlink_list[to++] = head; } else { + pool_free(idev_pool, 0, head->link_u.idev); head->link_u.idev = NULL; } } @@ -73,12 +88,16 @@ static void link_idev_data(void) if (!to) { free(hlink_list); hlink_list = NULL; + pool_destroy(hlink_pool); + hlink_pool = NULL; } else { hlink_count = to; if (!(hlink_list = realloc_array(hlink_list, struct file_struct *, hlink_count))) out_of_memory("init_hard_links"); } + flist->hlink_pool = hlink_pool; + pool_destroy(idev_pool); } #endif @@ -109,7 +128,7 @@ void init_hard_links(struct file_list *f free(hlink_list); hlink_list = NULL; } else - link_idev_data(); + link_idev_data(flist); #endif } diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.3 pool2/lib/pool_alloc.3 --- cvs/lib/pool_alloc.3 Wed Dec 31 16:00:00 1969 +++ pool2/lib/pool_alloc.3 Tue Feb 3 16:30:24 2004 @@ -0,0 +1,199 @@ +.ds d \-\^\- +.ds o \fR[\fP +.ds c \fR]\fP +.ds | \fR|\fP +.de D +\\.B \*d\\$1 +.. +.de DI +\\.BI \*d\\$1 \\$2 +.. +.de DR +\\.BR \*d\\$1 \\$2 +.. +.de Di +\\.BI \*d\\$1 " \\$2" +.. +.de Db +\\.B \*d\\$1 " \\$2" +.. +.de Df +\\.B \*d\*ono\*c\\$1 +.. +.de See +See \fB\\$1\fP for details. +.. +.de SeeIn +See \fB\\$1\fP in \fB\\$2\fP for details. +.. +.TH POOL_ALLOC 3 +.SH NAME +pool_alloc, pool_free, pool_talloc, pool_tfree, pool_create, pool_destroy +\- Allocate and free memory in managed allocation pools. +.SH SYNOPSIS +.B #include "pool_alloc.h" + +\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB); + +\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB); + +\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char *\fImsg\fB); + +\fBvoid pool_free(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB, void *\fIaddr\fB); + +\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, char *\fImsg\fB); + +\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void *\fIaddr\fB); +.SH DESCRIPTION +.P +The pool allocation routines use +.B malloc() +for underlying memory management. +What allocation pools do is cause +memory within a given pool to be in large contigious blocks +(called extents) that when freed will be reusable. Unlike +.B malloc() +the allocations are not managed individually. +Instead each extent tracks the total free memory within the +extent. Each extent can either be used to allocate memory +or to manage the freeing of memory within that extent. +When an extent has less free memory than a given +allocation request or when the first request to free +memory within that extent is received the extent ceases to +be used for allocation. +.P +This form of memory management is suited to large numbers of small +related allocations that are held for a while +and then freed as a group. +Because the +underlying allocations are done in large contigious extents +when an extent is freed it releases a large enough +contigious block of memory to be useful to subsequent +.B malloc() +and +.B pool_alloc() +calls even if allocations from other pools or from +.B malloc() +are made between allocations from a given pool. +.P +.B pool_create() +Creates an allocation pool for subsequent calls to the pool +allocation functions. +When an extent is created for allocations it will be +.I size +bytes. +Allocations from the pool have their sizes rounded up to a +multiple of +.I quantum +bytes in length. +Specifying +.B 0 +for +.I quantum +Will produce a quantum that should meet maximal allignment +on most platforms. +If the +.B POOL_QALIGN +.I flag +is set allocations will be aligned to addresses that are a +multiple of +.IR quantum . +If the +.B POOL_CLEAR +.I flag +is set all allocations from the pool will be zero filled. +.P +.B pool_destroy() +destroys an allocation pool and frees all memory allocated +in that pool. +.P +.B pool_alloc() +allocates +.I size +bytes from the specified +.IR pool . +If +.I size +is +.B 0 +.I quantum +bytes will be freed. +If the requested memory cannot be allocated +.B pool_alloc() +will call +.I bomb() +function, if defined, with +.I msg +as it's sole argument and +.B NULL +will be returned. +.P +.B pool_free() +frees +.I size +bytes pointed to by +.I addr +previously allocated in the specified +.IR pool . +The memory freed within an extent will not be reusable until +all of the memory in that extent has been freed but +depending on the order in which the +allocations are freed some extents may be released for reuse +while others are still in use. +If +.I size +is +.B 0 +.I quantum +bytes will be freed. +If +.I addr +is +.B 0 +no memory will be freed but subsequent allocations will come +from a new extent. +.P +.B pool_talloc() +is a macro that take a +.I type +and +.I count +instead of +.I size +and will cast the return value to the correct type. +.P +.B pool_tfree +is a macro to free memory previously allocated in the +specified +.IR pool . +.SH RETURN VALUE +.B pool_create() +returns a pointer to +.BR "struct alloc_pool" . +.P +.B pool_alloc() +and +.B pool_talloc() +return pointers to the allocated memory, +or NULL if the request fails. +For each extent so long as no allocations are smaller than varaible +allignment requirements this pointer will be suitably +alligned for any kind of variable. +The return type of +.B pool_alloc() +will normally require casting to the desired type but +.B pool_talloc() +will returns a pointer of the requested +.IR type . +.P +.BR pool_free() , +.B pool_tfree() +and +.B pool_destroy() +return no value. +.SH SEE ALSO +.nf +malloc(3) +.SH AUTHOR +pool_alloc was created by J.W. Schultz of Pegasystems Technologies. +.SH BUGS AND ISSUES diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.c pool2/lib/pool_alloc.c --- cvs/lib/pool_alloc.c Wed Dec 31 16:00:00 1969 +++ pool2/lib/pool_alloc.c Thu Feb 5 19:28:58 2004 @@ -0,0 +1,311 @@ +#include "rsync.h" + +#define POOL_DEF_EXTENT (32 * 1024) + +struct alloc_pool +{ + size_t size; /* extent size */ + size_t quantum; /* allocation quantum */ + struct pool_extent *live; /* current extent for + * allocations */ + struct pool_extent *free; /* unfreed extent list */ + void (*bomb)(); + /* function to call if + * malloc fails */ + int flags; + + /* statistical data */ + unsigned long e_created; /* extents created */ + unsigned long e_freed; /* extents detroyed */ + uint64 n_allocated; /* calls to alloc */ + uint64 n_freed; /* calls to free */ + uint64 b_allocated; /* cum. bytes allocated */ + uint64 b_freed; /* cum. bytes freed */ +}; + +struct pool_extent +{ + void *start; /* starting address */ + size_t free; /* free bytecount */ + size_t bound; /* bytes bound by padding, + * overhead and freed */ + struct pool_extent *next; +}; + +#define MINALIGN (sizeof (void *)) + +alloc_pool_t +pool_create(size_t size, size_t quantum, + void (*bomb)(char *), int flags) +{ + struct alloc_pool *pool; + + if (!(pool = (struct alloc_pool*) malloc(sizeof (struct alloc_pool)))) + return pool; + memset(pool, 0, sizeof (struct alloc_pool)); + + pool->size = size /* round extent size to min alignment reqs */ + ? (size + MINALIGN - 1) & ~(MINALIGN - 1) + : POOL_DEF_EXTENT; + pool->quantum = quantum ? quantum : MINALIGN; + pool->live = NULL; + pool->free = NULL; + pool->bomb = bomb; + pool->flags = flags; + + return pool; +} + +void +pool_destroy(alloc_pool_t p) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + struct pool_extent *cur, *next; + + if (!pool) + return; + + if(pool->live) + { + cur = pool->live; + free(cur->start); + if (!(pool->flags & (POOL_INTERN | POOL_APPEND))) + free(cur); + } + cur = pool->free; + while(cur) + { + next = cur->next; + free(cur->start); + if (!(pool->flags & (POOL_INTERN | POOL_APPEND))) + free(cur); + cur = next; + } + free(pool); +} + +void *pool_alloc(alloc_pool_t p, size_t len, char *bomb) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + if (!pool) + return NULL; + + if(!len) + len = pool->quantum; + else if (pool->quantum > 1 && len % pool->quantum) + len += pool->quantum - len % pool->quantum; + + if (len > pool->size) + goto bomb; + + if (!pool->live || len > pool->live->free) + { + void *start; + size_t free; + size_t bound; + size_t sqew; + size_t asize; + + if (pool->live) + { + pool->live->next = pool->free; + pool->free = pool->live; + } + + free = pool->size; + bound = 0; + + asize = pool->size; + if (pool->flags & POOL_APPEND) + asize += sizeof (struct pool_extent); + + if(!(start = (void *) malloc(asize))) + goto bomb; + + if (pool->flags & POOL_CLEAR) + memset(start, 0, asize); + + if (pool->flags & POOL_INTERN) + { + bound = sizeof (struct pool_extent); + free -= sizeof (struct pool_extent); + pool->live = start + free; + } + else if (pool->flags & POOL_APPEND) + { + pool->live = start + free; + } + else if(!(pool->live = (struct pool_extent *) malloc(sizeof (struct pool_extent)))) + { + goto bomb; + } + if(pool->flags & POOL_QALIGN && pool->quantum > 1 + && (sqew = (size_t)(start + free) % pool->quantum)) + { + bound += sqew; + free -= sqew; + } + pool->live->start = start; + pool->live->free = free; + pool->live->bound = bound; + pool->live->next = NULL; + + pool->e_created++; + } + + pool->n_allocated++; + pool->b_allocated += len; + + pool->live->free -= len; + + return pool->live->start + pool->live->free; + +bomb: + if (pool->bomb) + (*pool->bomb)(bomb); + return NULL; +} + +void +pool_free(alloc_pool_t p, size_t len, void *addr) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + struct pool_extent *cur, *next; + struct pool_extent *prev = NULL; + + if (!pool) + return; + + if(!len) + len = pool->quantum; + else if (pool->quantum > 1 && len % pool->quantum) + len += pool->quantum - len % pool->quantum; + + if (!addr && pool->live) + { + pool->live->next = pool->free; + pool->free = pool->live; + pool->live = NULL; + return; + } + pool->n_freed++; + pool->b_freed += len; + + cur = pool->live; + if (cur + && addr >= cur->start + && addr < cur->start + pool->size) + { + if (addr == cur->start + cur->free) + { + if (pool->flags & POOL_CLEAR) + memset(addr, 0, len); + pool->b_freed += len; + } else { + cur->bound += len; + } + if (cur->free + cur->bound >= pool->size) + { + size_t sqew; + + cur->free = pool->size; + cur->bound = 0; + if (pool->flags & POOL_INTERN) + { + cur->bound = sizeof (struct pool_extent); + cur->free -= sizeof (struct pool_extent); + } + if(pool->flags & POOL_QALIGN && pool->quantum > 1 + && (sqew = (size_t)(cur->start + cur->free) % pool->quantum)) + { + cur->bound += sqew; + cur->free -= sqew; + } + } + return; + } + cur = pool->free; + while(cur) + { + next = cur->next; + if (addr >= cur->start + && addr < cur->start + pool->size) + break; + prev = cur; + cur = next; + } + if (!cur) + return; + + if (cur != pool->free) + { + prev->next = cur->next; + cur->next = pool->free; + pool->free = cur; + } + cur->bound += len; + + if (cur->free + cur->bound >= pool->size) + { + pool->free = cur->next; + + free(cur->start); + if (!(pool->flags & (POOL_INTERN | POOL_APPEND))) + free(cur); + pool->e_freed++; + } + return; +} + +#define FDPRINT(label, value) \ + snprintf(buf, BUFSIZ, label, value); \ + write(fd, buf, strlen(buf)); + +#define FDEXTSTAT(ext) \ + snprintf(buf, BUFSIZ, " %12ld %5ld\n", \ + (long) ext->free, \ + (long) ext->bound); \ + write(fd, buf, strlen(buf)); + +void +pool_stats(alloc_pool_t p, int fd, int summarize) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + struct pool_extent *cur; + char buf[BUFSIZ]; + + if (!pool) + return; + + FDPRINT(" Extent size: %12ld\n", (long) pool->size) + FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum) + FDPRINT(" Extents created: %12ld\n", pool->e_created) + FDPRINT(" Extents freed: %12ld\n", pool->e_freed) + FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated) + FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed) + FDPRINT(" Alloc bytes: %12.0f\n", (double) pool->b_allocated) + FDPRINT(" Free bytes: %12.0f\n", (double) pool->b_freed) + + if (summarize) + return; + + if (!pool->live && !pool->free) + return; + + write(fd, "\n", 1); + + if (pool->live) + { + FDEXTSTAT(pool->live) + } + strcpy(buf, " FREE BOUND\n"); + write(fd, buf, strlen(buf)); + + cur = pool->free; + while(cur) + { + FDEXTSTAT(cur) + cur = cur->next; + } +} + diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.h pool2/lib/pool_alloc.h --- cvs/lib/pool_alloc.h Wed Dec 31 16:00:00 1969 +++ pool2/lib/pool_alloc.h Tue Feb 3 16:30:24 2004 @@ -0,0 +1,20 @@ +#include <stddef.h> + +#define POOL_CLEAR (1<<0) /* zero fill allocations */ +#define POOL_QALIGN (1<<1) /* align data to quanta */ +#define POOL_INTERN (1<<2) /* Allocate extent structures */ +#define POOL_APPEND (1<<3) /* or appended to extent data */ + +typedef void *alloc_pool_t; + +alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(char *), int flags); +void pool_destroy(alloc_pool_t pool); +void *pool_alloc(alloc_pool_t pool, size_t size, char *bomb); +void pool_free(alloc_pool_t pool, size_t size, void *addr); + +#define pool_talloc(pool, type, count, bomb) \ + ((type *)pool_alloc(pool, sizeof(type) * count, bomb)) + +#define pool_tfree(pool, type, count, addr) \ + (pool_free(pool, sizeof(type) * count, addr)) + diff -rupNP --exclude-from cvs/.ignore cvs/proto.h pool2/proto.h --- cvs/proto.h Wed Feb 4 01:00:45 2004 +++ pool2/proto.h Thu Feb 5 18:49:59 2004 @@ -73,16 +73,18 @@ void show_flist_stats(void); int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf); int link_stat(const char *path, STRUCT_STAT * buffer); void send_file_entry(struct file_struct *file, int f, unsigned short base_flags); -void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f); -struct file_struct *make_file(char *fname, int exclude_level); +void receive_file_entry(struct file_struct **fptr, unsigned short flags, + struct file_list *flist, int f); +struct file_struct *make_file(char *fname, + struct file_list *flist, int exclude_level); void send_file_name(int f, struct file_list *flist, char *fname, int recursive, unsigned short base_flags); struct file_list *send_file_list(int f, int argc, char *argv[]); struct file_list *recv_file_list(int f); int file_compare(struct file_struct **file1, struct file_struct **file2); int flist_find(struct file_list *flist, struct file_struct *f); -void free_file(struct file_struct *file, int free_the_struct); -struct file_list *flist_new(void); +void clear_file(int i, struct file_list *flist); +struct file_list *flist_new(int with_hlink, char *msg); void flist_free(struct file_list *flist); int f_name_cmp(struct file_struct *f1, struct file_struct *f2); char *f_name_to(struct file_struct *f, char *fbuf); diff -rupNP --exclude-from cvs/.ignore cvs/receiver.c pool2/receiver.c --- cvs/receiver.c Wed Feb 4 03:49:36 2004 +++ pool2/receiver.c Wed Feb 4 03:08:45 2004 @@ -305,6 +305,12 @@ int recv_files(int f_in,struct file_list rprintf(FINFO,"recv_files(%d) starting\n",flist->count); } + if (flist->hlink_pool) + { + pool_destroy(flist->hlink_pool); + flist->hlink_pool = NULL; + } + while (1) { cleanup_disable(); diff -rupNP --exclude-from cvs/.ignore cvs/rsync.h pool2/rsync.h --- cvs/rsync.h Wed Feb 4 15:04:10 2004 +++ pool2/rsync.h Thu Feb 5 14:18:32 2004 @@ -112,8 +112,12 @@ #define FULL_FLUSH 1 #define NORMAL_FLUSH 0 -#define CLEAR_STRUCT 0 -#define FREE_STRUCT 1 +#if HAVE_LINK +#define WITH_HLINK 1 +#else +#define WITH_HLINK 0 +#endif +#define WITHOUT_HLINK 0 /* Log-message categories. FLOG is only used on the daemon side to * output messages to the log file. */ @@ -254,6 +258,7 @@ enum msgcode { #include <assert.h> +#include "lib/pool_alloc.h" #define BOOL int @@ -428,20 +433,25 @@ struct file_struct { uchar flags; /* this item MUST remain last */ }; -#define ARENA_SIZE (32 * 1024) +/* + * Extent size for allocation pools A minimum size of 128KB + * is needed to mmap them so that freeing will release the + * space to the OS. + * + * Larger sizes reduce leftover fragments and speed free calls + * (when they happen) Smaller sizes increase the chance of + * freed allocations freeing whole extents. + */ -struct string_area { - char *base; - char *end; - char *current; - struct string_area *next; -}; +#define FILE_EXTENT (256 * 1024) +#define HLINK_EXTENT (128 * 1024) struct file_list { int count; int malloced; + alloc_pool_t file_pool; + alloc_pool_t hlink_pool; struct file_struct **files; - struct string_area *string_area; }; struct sum_buf { Binary files cvs/t_unsafe and pool2/t_unsafe differ Binary files cvs/tls and pool2/tls differ Binary files cvs/trimslash and pool2/trimslash differ Binary files cvs/wildtest and pool2/wildtest differ
Hey guys, I'd like to take a full backup of our email server. Is it possible to clone the current server (postfix, spamassassin, qpopper...) to another clean system to have it ready if the current one fails? Best, Thorsten
On Thu, Feb 05, 2004 at 09:27:51PM -0800, jw schultz wrote:> As those of you who watch CVS will be aware Wayne has been > making progress in reducing memory requirements of rsync. > Much of what he has done has been the product of discussions > between he and myself that started a month ago with John Van > Essen. > > Most recently Wayne has changed how the file_struct and its > associated data are allocated, eliminating the string areas. > Most of these changes have been small and relatively low > impact although combining the allocation of the file_struct > with the strings does impact the memory management of > file_struct. > > Attached is a patch that implements the next step. It > alters flist memory management and introduces a MM pool > layer that reduces malloc overhead and allows destructors to > actually release memory to the OS. > > The patch adds a couple of new files so use patch -p1 > and rerun ./configure after patching.Here is an update of the patch to cope with patch conflicts re CVS HEAD. diff -rupNP --exclude-from cvs/.ignore cvs/Makefile.in pool2/Makefile.in --- cvs/Makefile.in Tue Feb 3 16:34:24 2004 +++ pool2/Makefile.in Tue Feb 3 16:33:00 2004 @@ -27,7 +27,7 @@ VERSION=@VERSION@ HEADERS=byteorder.h config.h errcode.h proto.h rsync.h LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \ - lib/permstring.o @LIBOBJS@ + lib/permstring.o lib/pool_alloc.o @LIBOBJS@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \ zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \ zlib/zutil.o zlib/adler32.o diff -rupNP --exclude-from cvs/.ignore cvs/backup.c pool2/backup.c --- cvs/backup.c Wed Feb 4 03:49:36 2004 +++ pool2/backup.c Wed Feb 4 22:11:26 2004 @@ -189,6 +189,7 @@ static int keep_backup(char *fname) backup_dir[--backup_dir_len] = '\0'; if (verbose > 0) rprintf(FINFO, "backup_dir is %s\n", backup_dir); + initialised = 1; } @@ -199,7 +200,7 @@ static int keep_backup(char *fname) if (do_stat(fname, &st)) return 1; #endif - file = make_file(fname, NO_EXCLUDES); + file = make_file(fname, NULL, NO_EXCLUDES); /* the file could have disappeared */ if (!file) return 1; @@ -282,7 +283,7 @@ static int keep_backup(char *fname) } } set_perms(keep_name, file, NULL, 0); - free_file(file, FREE_STRUCT); + free(file); if (verbose > 1) rprintf(FINFO, "keep_backup %s -> %s\n", fname, keep_name); diff -rupNP --exclude-from cvs/.ignore cvs/batch.c pool2/batch.c --- cvs/batch.c Fri Feb 6 01:54:56 2004 +++ pool2/batch.c Fri Feb 6 02:00:58 2004 @@ -136,9 +136,7 @@ struct file_list *create_flist_from_batc exit_cleanup(1); } - batch_flist = new(struct file_list); - if (!batch_flist) - out_of_memory("create_flist_from_batch"); + batch_flist = flist_new(WITH_HLINK, "create_flist_from_batch"); save_read = stats.total_read; save_pv = protocol_version; @@ -150,9 +148,9 @@ struct file_list *create_flist_from_batc for (i = 0; (flags = read_byte(f)) != 0; i++) { if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS)) flags |= read_byte(f) << 8; - receive_file_entry(&batch_flist->files[i], flags, f); + receive_file_entry(&batch_flist->files[i], flags, batch_flist, f); } - receive_file_entry(NULL, 0, 0); /* Signal that we're done. */ + receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */ protocol_version = save_pv; stats.total_read = save_read; diff -rupNP --exclude-from cvs/.ignore cvs/flist.c pool2/flist.c --- cvs/flist.c Fri Feb 6 01:55:41 2004 +++ pool2/flist.c Fri Feb 6 02:00:59 2004 @@ -76,7 +76,6 @@ static unsigned int min_file_struct_len; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); static void output_flist(struct file_list *flist); - void init_flist(void) { struct file_struct f; @@ -507,7 +506,8 @@ void send_file_entry(struct file_struct -void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f) +void receive_file_entry(struct file_struct **fptr, unsigned short flags, + struct file_list *flist, int f) { static time_t modtime; static mode_t mode; @@ -624,12 +624,12 @@ void receive_file_entry(struct file_stru idev_len = 0; sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0; - file_struct_len = idev_len? sizeof file[0] : min_file_struct_len; + file_struct_len = min_file_struct_len; alloc_len = file_struct_len + dirname_len + basename_len - + linkname_len + sum_len + idev_len; - if (!(bp = new_array(char, alloc_len))) - out_of_memory("receive_file_entry"); + + linkname_len + sum_len; + bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry"); + file = *fptr = (struct file_struct *)bp; memset(bp, 0, min_file_struct_len); bp += file_struct_len; @@ -642,9 +642,9 @@ void receive_file_entry(struct file_stru file->gid = gid; #if SUPPORT_HARD_LINKS - if (idev_len) { - file->link_u.idev = (struct idev *)bp; - bp += idev_len; + if (idev_len && flist->hlink_pool) { + file->link_u.idev = pool_talloc(flist->hlink_pool, + struct idev, 1, "inode_table"); } #endif @@ -676,15 +676,19 @@ void receive_file_entry(struct file_stru #if SUPPORT_HARD_LINKS if (idev_len) { + INO64_T inode; if (protocol_version < 26) { dev = read_int(f); - file->F_INODE = read_int(f); + inode = read_int(f); } else { if (!(flags & XMIT_SAME_DEV)) dev = read_longint(f); - file->F_INODE = read_longint(f); + inode = read_longint(f); + } + if (flist->hlink_pool) { + file->F_INODE = inode; + file->F_DEV = dev; } - file->F_DEV = dev; } #endif @@ -728,7 +732,8 @@ void receive_file_entry(struct file_stru * statting directories if we're not recursing, but this is not a very * important case. Some systems may not have d_type. **/ -struct file_struct *make_file(char *fname, int exclude_level) +struct file_struct *make_file(char *fname, + struct file_list *flist, int exclude_level) { static char *lastdir; static int lastdir_len = -1; @@ -742,6 +747,7 @@ struct file_struct *make_file(char *fnam char *basename, *dirname, *bp; unsigned short flags = 0; + if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname - flist_dir_len) { rprintf(FINFO, "skipping overly long name: %s\n", fname); @@ -838,12 +844,18 @@ struct file_struct *make_file(char *fnam idev_len = 0; sum_len = always_checksum && S_ISREG(st.st_mode) ? MD4_SUM_LENGTH : 0; - file_struct_len = idev_len? sizeof file[0] : min_file_struct_len; + file_struct_len = min_file_struct_len; alloc_len = file_struct_len + dirname_len + basename_len - + linkname_len + sum_len + idev_len; - if (!(bp = new_array(char, alloc_len))) - out_of_memory("receive_file_entry"); + + linkname_len + sum_len; + if (flist) { + bp = pool_alloc(flist->file_pool, alloc_len, + "receive_file_entry"); + } else { + if (!(bp = new_array(char, alloc_len))) + out_of_memory("receive_file_entry"); + } + file = (struct file_struct *)bp; memset(bp, 0, min_file_struct_len); bp += file_struct_len; @@ -856,9 +868,9 @@ struct file_struct *make_file(char *fnam file->gid = st.st_gid; #if SUPPORT_HARD_LINKS - if (idev_len) { - file->link_u.idev = (struct idev *)bp; - bp += idev_len; + if (idev_len && flist && flist->hlink_pool) { + file->link_u.idev = pool_talloc(flist->hlink_pool, + struct idev, 1, "inode_table"); file->F_DEV = st.st_dev; file->F_INODE = st.st_ino; } @@ -913,9 +925,8 @@ void send_file_name(int f, struct file_l extern int delete_excluded; /* f is set to -1 when calculating deletion file list */ - file = make_file(fname, - f == -1 && delete_excluded? SERVER_EXCLUDES - : ALL_EXCLUDES); + file = make_file(fname, flist, + f == -1 && delete_excluded? SERVER_EXCLUDES : ALL_EXCLUDES); if (!file) return; @@ -1034,9 +1045,12 @@ struct file_list *send_file_list(int f, start_write = stats.total_written; - flist = flist_new(); + flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK, + "send_file_list"); if (f != -1) { + flist->hlink_pool = pool_create(128 * 1024, + sizeof (struct idev), out_of_memory, POOL_INTERN); io_start_buffering_out(f); if (filesfrom_fd >= 0) { if (argv[0] && !push_dir(argv[0])) { @@ -1185,6 +1199,12 @@ struct file_list *send_file_list(int f, finish_filelist_progress(flist); } + if (flist->hlink_pool) + { + pool_destroy(flist->hlink_pool); + flist->hlink_pool = NULL; + } + clean_flist(flist, 0, 0); if (f != -1) { @@ -1224,10 +1244,10 @@ struct file_list *recv_file_list(int f) start_read = stats.total_read; - flist = new(struct file_list); - if (!flist) - goto oom; + flist = flist_new(WITH_HLINK, "recv_file_list"); + flist->hlink_pool = pool_create(128 * 1024, sizeof (struct idev), + out_of_memory, POOL_INTERN); flist->count = 0; flist->malloced = 1000; flist->files = new_array(struct file_struct *, flist->malloced); @@ -1242,7 +1262,7 @@ struct file_list *recv_file_list(int f) if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS)) flags |= read_byte(f) << 8; - receive_file_entry(&flist->files[i], flags, f); + receive_file_entry(&flist->files[i], flags, flist, f); if (S_ISREG(flist->files[i]->mode)) stats.total_size += flist->files[i]->length; @@ -1256,7 +1276,7 @@ struct file_list *recv_file_list(int f) f_name(flist->files[i])); } } - receive_file_entry(NULL, 0, 0); /* Signal that we're done. */ + receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */ if (verbose > 2) rprintf(FINFO, "received %d names\n", flist->count); @@ -1345,34 +1365,42 @@ int flist_find(struct file_list *flist, return -1; } - /* - * Free up any resources a file_struct has allocated, and optionally free - * it up as well. + * Free up any resources a file_struct has allocated + * and clear the file. */ -void free_file(struct file_struct *file, int free_the_struct) +void clear_file(int i, struct file_list *flist) { - if (free_the_struct) - free(file); - else - memset(file, 0, min_file_struct_len); + if (flist->hlink_pool && flist->files[i]->link_u.idev) + pool_free(flist->hlink_pool, 0, flist->files[i]->link_u.idev); + memset(flist->files[i], 0, min_file_struct_len); } /* * allocate a new file list */ -struct file_list *flist_new(void) +struct file_list *flist_new(int with_hlink, char *msg) { struct file_list *flist; flist = new(struct file_list); if (!flist) - out_of_memory("send_file_list"); + out_of_memory(msg); - flist->count = 0; - flist->malloced = 0; - flist->files = NULL; + memset(flist, 0, sizeof (struct file_list)); + + if (!(flist->file_pool = pool_create(FILE_EXTENT, 0, + out_of_memory, POOL_INTERN))) + out_of_memory(msg); + +#if SUPPORT_HARD_LINKS + if (with_hlink && preserve_hard_links) { + if (!(flist->hlink_pool = pool_create(HLINK_EXTENT, 0, + out_of_memory, POOL_INTERN))) + out_of_memory(msg); + } +#endif return flist; } @@ -1382,9 +1410,8 @@ struct file_list *flist_new(void) */ void flist_free(struct file_list *flist) { - int i; - for (i = 1; i < flist->count; i++) - free_file(flist->files[i], FREE_STRUCT); + pool_destroy(flist->file_pool); + pool_destroy(flist->hlink_pool); free(flist->files); free(flist); } @@ -1424,7 +1451,8 @@ static void clean_flist(struct file_list * else deletions will mysteriously fail with -R). */ if (flist->files[i]->flags & FLAG_TOP_DIR) flist->files[prev_i]->flags |= FLAG_TOP_DIR; - free_file(flist->files[i], CLEAR_STRUCT); + + clear_file(i, flist); } else prev_i = i; } Binary files cvs/getgroups and pool2/getgroups differ diff -rupNP --exclude-from cvs/.ignore cvs/hlink.c pool2/hlink.c --- cvs/hlink.c Mon Feb 2 22:21:19 2004 +++ pool2/hlink.c Wed Feb 4 01:35:49 2004 @@ -46,26 +46,41 @@ int hlink_count; /* Analyze the data in the hlink_list[], remove items that aren't multiply * linked, and replace the dev+inode data with the hlindex+next linked list. */ -static void link_idev_data(void) +static void link_idev_data(struct file_list *flist) { struct file_struct *head; int from, to, start; + alloc_pool_t hlink_pool; + alloc_pool_t idev_pool = flist->hlink_pool; + + hlink_pool = pool_create(128 * 1024, sizeof (struct hlink), + out_of_memory, POOL_INTERN); + for (from = to = 0; from < hlink_count; from++) { start = from; head = hlink_list[start]; while (from < hlink_count-1 && LINKED(hlink_list[from], hlink_list[from+1])) { + pool_free(idev_pool, 0, hlink_list[from]->link_u.idev); + hlink_list[from]->link_u.links = pool_talloc(hlink_pool, + struct hlink, 1, "hlink_list"); + hlink_list[from]->F_HLINDEX = to; hlink_list[from]->F_NEXT = hlink_list[from+1]; from++; } if (from > start) { + pool_free(idev_pool, 0, hlink_list[from]->link_u.idev); + hlink_list[from]->link_u.links = pool_talloc(hlink_pool, + struct hlink, 1, "hlink_list"); + hlink_list[from]->F_HLINDEX = to; hlink_list[from]->F_NEXT = head; hlink_list[from]->flags |= FLAG_HLINK_EOL; hlink_list[to++] = head; } else { + pool_free(idev_pool, 0, head->link_u.idev); head->link_u.idev = NULL; } } @@ -73,12 +88,16 @@ static void link_idev_data(void) if (!to) { free(hlink_list); hlink_list = NULL; + pool_destroy(hlink_pool); + hlink_pool = NULL; } else { hlink_count = to; if (!(hlink_list = realloc_array(hlink_list, struct file_struct *, hlink_count))) out_of_memory("init_hard_links"); } + flist->hlink_pool = hlink_pool; + pool_destroy(idev_pool); } #endif @@ -109,7 +128,7 @@ void init_hard_links(struct file_list *f free(hlink_list); hlink_list = NULL; } else - link_idev_data(); + link_idev_data(flist); #endif } diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.3 pool2/lib/pool_alloc.3 --- cvs/lib/pool_alloc.3 Wed Dec 31 16:00:00 1969 +++ pool2/lib/pool_alloc.3 Tue Feb 3 16:30:24 2004 @@ -0,0 +1,199 @@ +.ds d \-\^\- +.ds o \fR[\fP +.ds c \fR]\fP +.ds | \fR|\fP +.de D +\\.B \*d\\$1 +.. +.de DI +\\.BI \*d\\$1 \\$2 +.. +.de DR +\\.BR \*d\\$1 \\$2 +.. +.de Di +\\.BI \*d\\$1 " \\$2" +.. +.de Db +\\.B \*d\\$1 " \\$2" +.. +.de Df +\\.B \*d\*ono\*c\\$1 +.. +.de See +See \fB\\$1\fP for details. +.. +.de SeeIn +See \fB\\$1\fP in \fB\\$2\fP for details. +.. +.TH POOL_ALLOC 3 +.SH NAME +pool_alloc, pool_free, pool_talloc, pool_tfree, pool_create, pool_destroy +\- Allocate and free memory in managed allocation pools. +.SH SYNOPSIS +.B #include "pool_alloc.h" + +\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB); + +\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB); + +\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char *\fImsg\fB); + +\fBvoid pool_free(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB, void *\fIaddr\fB); + +\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, char *\fImsg\fB); + +\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void *\fIaddr\fB); +.SH DESCRIPTION +.P +The pool allocation routines use +.B malloc() +for underlying memory management. +What allocation pools do is cause +memory within a given pool to be in large contigious blocks +(called extents) that when freed will be reusable. Unlike +.B malloc() +the allocations are not managed individually. +Instead each extent tracks the total free memory within the +extent. Each extent can either be used to allocate memory +or to manage the freeing of memory within that extent. +When an extent has less free memory than a given +allocation request or when the first request to free +memory within that extent is received the extent ceases to +be used for allocation. +.P +This form of memory management is suited to large numbers of small +related allocations that are held for a while +and then freed as a group. +Because the +underlying allocations are done in large contigious extents +when an extent is freed it releases a large enough +contigious block of memory to be useful to subsequent +.B malloc() +and +.B pool_alloc() +calls even if allocations from other pools or from +.B malloc() +are made between allocations from a given pool. +.P +.B pool_create() +Creates an allocation pool for subsequent calls to the pool +allocation functions. +When an extent is created for allocations it will be +.I size +bytes. +Allocations from the pool have their sizes rounded up to a +multiple of +.I quantum +bytes in length. +Specifying +.B 0 +for +.I quantum +Will produce a quantum that should meet maximal allignment +on most platforms. +If the +.B POOL_QALIGN +.I flag +is set allocations will be aligned to addresses that are a +multiple of +.IR quantum . +If the +.B POOL_CLEAR +.I flag +is set all allocations from the pool will be zero filled. +.P +.B pool_destroy() +destroys an allocation pool and frees all memory allocated +in that pool. +.P +.B pool_alloc() +allocates +.I size +bytes from the specified +.IR pool . +If +.I size +is +.B 0 +.I quantum +bytes will be freed. +If the requested memory cannot be allocated +.B pool_alloc() +will call +.I bomb() +function, if defined, with +.I msg +as it's sole argument and +.B NULL +will be returned. +.P +.B pool_free() +frees +.I size +bytes pointed to by +.I addr +previously allocated in the specified +.IR pool . +The memory freed within an extent will not be reusable until +all of the memory in that extent has been freed but +depending on the order in which the +allocations are freed some extents may be released for reuse +while others are still in use. +If +.I size +is +.B 0 +.I quantum +bytes will be freed. +If +.I addr +is +.B 0 +no memory will be freed but subsequent allocations will come +from a new extent. +.P +.B pool_talloc() +is a macro that take a +.I type +and +.I count +instead of +.I size +and will cast the return value to the correct type. +.P +.B pool_tfree +is a macro to free memory previously allocated in the +specified +.IR pool . +.SH RETURN VALUE +.B pool_create() +returns a pointer to +.BR "struct alloc_pool" . +.P +.B pool_alloc() +and +.B pool_talloc() +return pointers to the allocated memory, +or NULL if the request fails. +For each extent so long as no allocations are smaller than varaible +allignment requirements this pointer will be suitably +alligned for any kind of variable. +The return type of +.B pool_alloc() +will normally require casting to the desired type but +.B pool_talloc() +will returns a pointer of the requested +.IR type . +.P +.BR pool_free() , +.B pool_tfree() +and +.B pool_destroy() +return no value. +.SH SEE ALSO +.nf +malloc(3) +.SH AUTHOR +pool_alloc was created by J.W. Schultz of Pegasystems Technologies. +.SH BUGS AND ISSUES diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.c pool2/lib/pool_alloc.c --- cvs/lib/pool_alloc.c Wed Dec 31 16:00:00 1969 +++ pool2/lib/pool_alloc.c Thu Feb 5 19:28:58 2004 @@ -0,0 +1,311 @@ +#include "rsync.h" + +#define POOL_DEF_EXTENT (32 * 1024) + +struct alloc_pool +{ + size_t size; /* extent size */ + size_t quantum; /* allocation quantum */ + struct pool_extent *live; /* current extent for + * allocations */ + struct pool_extent *free; /* unfreed extent list */ + void (*bomb)(); + /* function to call if + * malloc fails */ + int flags; + + /* statistical data */ + unsigned long e_created; /* extents created */ + unsigned long e_freed; /* extents detroyed */ + uint64 n_allocated; /* calls to alloc */ + uint64 n_freed; /* calls to free */ + uint64 b_allocated; /* cum. bytes allocated */ + uint64 b_freed; /* cum. bytes freed */ +}; + +struct pool_extent +{ + void *start; /* starting address */ + size_t free; /* free bytecount */ + size_t bound; /* bytes bound by padding, + * overhead and freed */ + struct pool_extent *next; +}; + +#define MINALIGN (sizeof (void *)) + +alloc_pool_t +pool_create(size_t size, size_t quantum, + void (*bomb)(char *), int flags) +{ + struct alloc_pool *pool; + + if (!(pool = (struct alloc_pool*) malloc(sizeof (struct alloc_pool)))) + return pool; + memset(pool, 0, sizeof (struct alloc_pool)); + + pool->size = size /* round extent size to min alignment reqs */ + ? (size + MINALIGN - 1) & ~(MINALIGN - 1) + : POOL_DEF_EXTENT; + pool->quantum = quantum ? quantum : MINALIGN; + pool->live = NULL; + pool->free = NULL; + pool->bomb = bomb; + pool->flags = flags; + + return pool; +} + +void +pool_destroy(alloc_pool_t p) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + struct pool_extent *cur, *next; + + if (!pool) + return; + + if(pool->live) + { + cur = pool->live; + free(cur->start); + if (!(pool->flags & (POOL_INTERN | POOL_APPEND))) + free(cur); + } + cur = pool->free; + while(cur) + { + next = cur->next; + free(cur->start); + if (!(pool->flags & (POOL_INTERN | POOL_APPEND))) + free(cur); + cur = next; + } + free(pool); +} + +void *pool_alloc(alloc_pool_t p, size_t len, char *bomb) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + if (!pool) + return NULL; + + if(!len) + len = pool->quantum; + else if (pool->quantum > 1 && len % pool->quantum) + len += pool->quantum - len % pool->quantum; + + if (len > pool->size) + goto bomb; + + if (!pool->live || len > pool->live->free) + { + void *start; + size_t free; + size_t bound; + size_t sqew; + size_t asize; + + if (pool->live) + { + pool->live->next = pool->free; + pool->free = pool->live; + } + + free = pool->size; + bound = 0; + + asize = pool->size; + if (pool->flags & POOL_APPEND) + asize += sizeof (struct pool_extent); + + if(!(start = (void *) malloc(asize))) + goto bomb; + + if (pool->flags & POOL_CLEAR) + memset(start, 0, asize); + + if (pool->flags & POOL_INTERN) + { + bound = sizeof (struct pool_extent); + free -= sizeof (struct pool_extent); + pool->live = start + free; + } + else if (pool->flags & POOL_APPEND) + { + pool->live = start + free; + } + else if(!(pool->live = (struct pool_extent *) malloc(sizeof (struct pool_extent)))) + { + goto bomb; + } + if(pool->flags & POOL_QALIGN && pool->quantum > 1 + && (sqew = (size_t)(start + free) % pool->quantum)) + { + bound += sqew; + free -= sqew; + } + pool->live->start = start; + pool->live->free = free; + pool->live->bound = bound; + pool->live->next = NULL; + + pool->e_created++; + } + + pool->n_allocated++; + pool->b_allocated += len; + + pool->live->free -= len; + + return pool->live->start + pool->live->free; + +bomb: + if (pool->bomb) + (*pool->bomb)(bomb); + return NULL; +} + +void +pool_free(alloc_pool_t p, size_t len, void *addr) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + struct pool_extent *cur, *next; + struct pool_extent *prev = NULL; + + if (!pool) + return; + + if(!len) + len = pool->quantum; + else if (pool->quantum > 1 && len % pool->quantum) + len += pool->quantum - len % pool->quantum; + + if (!addr && pool->live) + { + pool->live->next = pool->free; + pool->free = pool->live; + pool->live = NULL; + return; + } + pool->n_freed++; + pool->b_freed += len; + + cur = pool->live; + if (cur + && addr >= cur->start + && addr < cur->start + pool->size) + { + if (addr == cur->start + cur->free) + { + if (pool->flags & POOL_CLEAR) + memset(addr, 0, len); + pool->b_freed += len; + } else { + cur->bound += len; + } + if (cur->free + cur->bound >= pool->size) + { + size_t sqew; + + cur->free = pool->size; + cur->bound = 0; + if (pool->flags & POOL_INTERN) + { + cur->bound = sizeof (struct pool_extent); + cur->free -= sizeof (struct pool_extent); + } + if(pool->flags & POOL_QALIGN && pool->quantum > 1 + && (sqew = (size_t)(cur->start + cur->free) % pool->quantum)) + { + cur->bound += sqew; + cur->free -= sqew; + } + } + return; + } + cur = pool->free; + while(cur) + { + next = cur->next; + if (addr >= cur->start + && addr < cur->start + pool->size) + break; + prev = cur; + cur = next; + } + if (!cur) + return; + + if (cur != pool->free) + { + prev->next = cur->next; + cur->next = pool->free; + pool->free = cur; + } + cur->bound += len; + + if (cur->free + cur->bound >= pool->size) + { + pool->free = cur->next; + + free(cur->start); + if (!(pool->flags & (POOL_INTERN | POOL_APPEND))) + free(cur); + pool->e_freed++; + } + return; +} + +#define FDPRINT(label, value) \ + snprintf(buf, BUFSIZ, label, value); \ + write(fd, buf, strlen(buf)); + +#define FDEXTSTAT(ext) \ + snprintf(buf, BUFSIZ, " %12ld %5ld\n", \ + (long) ext->free, \ + (long) ext->bound); \ + write(fd, buf, strlen(buf)); + +void +pool_stats(alloc_pool_t p, int fd, int summarize) +{ + struct alloc_pool *pool = (struct alloc_pool *) p; + struct pool_extent *cur; + char buf[BUFSIZ]; + + if (!pool) + return; + + FDPRINT(" Extent size: %12ld\n", (long) pool->size) + FDPRINT(" Alloc quantum: %12ld\n", (long) pool->quantum) + FDPRINT(" Extents created: %12ld\n", pool->e_created) + FDPRINT(" Extents freed: %12ld\n", pool->e_freed) + FDPRINT(" Alloc count: %12.0f\n", (double) pool->n_allocated) + FDPRINT(" Free Count: %12.0f\n", (double) pool->n_freed) + FDPRINT(" Alloc bytes: %12.0f\n", (double) pool->b_allocated) + FDPRINT(" Free bytes: %12.0f\n", (double) pool->b_freed) + + if (summarize) + return; + + if (!pool->live && !pool->free) + return; + + write(fd, "\n", 1); + + if (pool->live) + { + FDEXTSTAT(pool->live) + } + strcpy(buf, " FREE BOUND\n"); + write(fd, buf, strlen(buf)); + + cur = pool->free; + while(cur) + { + FDEXTSTAT(cur) + cur = cur->next; + } +} + diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.h pool2/lib/pool_alloc.h --- cvs/lib/pool_alloc.h Wed Dec 31 16:00:00 1969 +++ pool2/lib/pool_alloc.h Tue Feb 3 16:30:24 2004 @@ -0,0 +1,20 @@ +#include <stddef.h> + +#define POOL_CLEAR (1<<0) /* zero fill allocations */ +#define POOL_QALIGN (1<<1) /* align data to quanta */ +#define POOL_INTERN (1<<2) /* Allocate extent structures */ +#define POOL_APPEND (1<<3) /* or appended to extent data */ + +typedef void *alloc_pool_t; + +alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(char *), int flags); +void pool_destroy(alloc_pool_t pool); +void *pool_alloc(alloc_pool_t pool, size_t size, char *bomb); +void pool_free(alloc_pool_t pool, size_t size, void *addr); + +#define pool_talloc(pool, type, count, bomb) \ + ((type *)pool_alloc(pool, sizeof(type) * count, bomb)) + +#define pool_tfree(pool, type, count, addr) \ + (pool_free(pool, sizeof(type) * count, addr)) + diff -rupNP --exclude-from cvs/.ignore cvs/proto.h pool2/proto.h --- cvs/proto.h Fri Feb 6 01:55:48 2004 +++ pool2/proto.h Fri Feb 6 02:01:00 2004 @@ -74,16 +74,18 @@ int readlink_stat(const char *path, STRU int link_stat(const char *path, STRUCT_STAT * buffer); void flist_expand(struct file_list *flist); void send_file_entry(struct file_struct *file, int f, unsigned short base_flags); -void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f); -struct file_struct *make_file(char *fname, int exclude_level); +void receive_file_entry(struct file_struct **fptr, unsigned short flags, + struct file_list *flist, int f); +struct file_struct *make_file(char *fname, + struct file_list *flist, int exclude_level); void send_file_name(int f, struct file_list *flist, char *fname, int recursive, unsigned short base_flags); struct file_list *send_file_list(int f, int argc, char *argv[]); struct file_list *recv_file_list(int f); int file_compare(struct file_struct **file1, struct file_struct **file2); int flist_find(struct file_list *flist, struct file_struct *f); -void free_file(struct file_struct *file, int free_the_struct); -struct file_list *flist_new(void); +void clear_file(int i, struct file_list *flist); +struct file_list *flist_new(int with_hlink, char *msg); void flist_free(struct file_list *flist); int f_name_cmp(struct file_struct *f1, struct file_struct *f2); char *f_name_to(struct file_struct *f, char *fbuf); diff -rupNP --exclude-from cvs/.ignore cvs/receiver.c pool2/receiver.c --- cvs/receiver.c Wed Feb 4 03:49:36 2004 +++ pool2/receiver.c Wed Feb 4 03:08:45 2004 @@ -305,6 +305,12 @@ int recv_files(int f_in,struct file_list rprintf(FINFO,"recv_files(%d) starting\n",flist->count); } + if (flist->hlink_pool) + { + pool_destroy(flist->hlink_pool); + flist->hlink_pool = NULL; + } + while (1) { cleanup_disable(); diff -rupNP --exclude-from cvs/.ignore cvs/rsync.h pool2/rsync.h --- cvs/rsync.h Fri Feb 6 01:13:58 2004 +++ pool2/rsync.h Fri Feb 6 02:02:23 2004 @@ -112,8 +112,12 @@ #define FULL_FLUSH 1 #define NORMAL_FLUSH 0 -#define CLEAR_STRUCT 0 -#define FREE_STRUCT 1 +#if HAVE_LINK +#define WITH_HLINK 1 +#else +#define WITH_HLINK 0 +#endif +#define WITHOUT_HLINK 0 /* Log-message categories. FLOG is only used on the daemon side to * output messages to the log file. */ @@ -254,6 +258,7 @@ enum msgcode { #include <assert.h> +#include "lib/pool_alloc.h" #define BOOL int @@ -434,19 +439,25 @@ struct file_struct { */ #define FLIST_START (32 * 1024) #define FLIST_LINEAR (FLIST_START * 512) +/* + * Extent size for allocation pools A minimum size of 128KB + * is needed to mmap them so that freeing will release the + * space to the OS. + * + * Larger sizes reduce leftover fragments and speed free calls + * (when they happen) Smaller sizes increase the chance of + * freed allocations freeing whole extents. + */ -struct string_area { - char *base; - char *end; - char *current; - struct string_area *next; -}; +#define FILE_EXTENT (256 * 1024) +#define HLINK_EXTENT (128 * 1024) struct file_list { int count; int malloced; + alloc_pool_t file_pool; + alloc_pool_t hlink_pool; struct file_struct **files; - struct string_area *string_area; }; struct sum_buf { Binary files cvs/t_unsafe and pool2/t_unsafe differ Binary files cvs/tls and pool2/tls differ Binary files cvs/trimslash and pool2/trimslash differ Binary files cvs/wildtest and pool2/wildtest differ
On Thu, Feb 05, 2004 at 09:27:51PM -0800, jw schultz wrote: | As those of you who watch CVS will be aware Wayne has been | making progress in reducing memory requirements of rsync. | Much of what he has done has been the product of discussions | between he and myself that started a month ago with John Van | Essen. | | Most recently Wayne has changed how the file_struct and its | associated data are allocated, eliminating the string areas. | Most of these changes have been small and relatively low | impact although combining the allocation of the file_struct | with the strings does impact the memory management of | file_struct. I've always felt that there would be some advantage to having a flag to specify incremental operation, where file names are collected and processed in only one directory recursion at a time (e.g. all levels of a path, but that's it). So if I am syncronizing /usr and it is in /usr/local/bin at the time, then each end would be hold the name list from /usr and /usr/local and /usr/local/bin. As soon as it gets done with a given directory, it frees up the space held for it. -- ----------------------------------------------------------------------------- | Phil Howard KA9WGN | http://linuxhomepage.com/ http://ham.org/ | | (first name) at ipal.net | http://phil.ipal.org/ http://ka9wgn.ham.org/ | -----------------------------------------------------------------------------
On Thu, Feb 05, 2004 at 09:27:51PM -0800, jw schultz wrote:> As those of you who watch CVS will be aware Wayne has been > making progress in reducing memory requirements of rsync. > Much of what he has done has been the product of discussions > between he and myself that started a month ago with John Van > Essen. > > Most recently Wayne has changed how the file_struct and its > associated data are allocated, eliminating the string areas. > Most of these changes have been small and relatively low > impact although combining the allocation of the file_struct > with the strings does impact the memory management of > file_struct. > > Attached is a patch that implements the next step. It > alters flist memory management and introduces a MM pool > layer that reduces malloc overhead and allows destructors to > actually release memory to the OS. > > The patch adds a couple of new files so use patch -p1 > and rerun ./configure after patching. >The patch has now been committed. If you use "cvs update" be sure to rerun ./configure.