diff options
author | Dave Plauger <Dave.Plauger@Sun.COM> | 2009-10-22 20:06:52 -0400 |
---|---|---|
committer | Dave Plauger <Dave.Plauger@Sun.COM> | 2009-10-22 20:06:52 -0400 |
commit | ca3e8d88e8c867355e441fbc914c52e7416fc537 (patch) | |
tree | 27934a23f3f293cfac68ec2188db5bf26361c12e | |
parent | c9cc1492d5b27b76cf77300ab3aafd0857f38228 (diff) | |
download | illumos-joyent-ca3e8d88e8c867355e441fbc914c52e7416fc537.tar.gz |
6828976 Fast Crash Dump
6878030 live crash dump is much slower than reboot dump
6626023 Crash dump size is excessive on large memory machines
33 files changed, 9540 insertions, 323 deletions
diff --git a/usr/src/cmd/dumpadm/dconf.c b/usr/src/cmd/dumpadm/dconf.c index 3cd5b725da..f4e59563f9 100644 --- a/usr/src/cmd/dumpadm/dconf.c +++ b/usr/src/cmd/dumpadm/dconf.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/stat.h> #include <sys/swap.h> @@ -56,15 +54,19 @@ static int print_device(const dumpconf_t *, FILE *); static int print_savdir(const dumpconf_t *, FILE *); static int print_content(const dumpconf_t *, FILE *); static int print_enable(const dumpconf_t *, FILE *); +static int print_csave(const dumpconf_t *, FILE *); static const dc_token_t tokens[] = { { "DUMPADM_DEVICE", dconf_str2device, print_device }, { "DUMPADM_SAVDIR", dconf_str2savdir, print_savdir }, { "DUMPADM_CONTENT", dconf_str2content, print_content }, { "DUMPADM_ENABLE", dconf_str2enable, print_enable }, + { "DUMPADM_CSAVE", dconf_str2csave, print_csave }, { NULL, NULL, NULL } }; +static const char DC_STR_ON[] = "on"; /* On string */ +static const char DC_STR_OFF[] = "off"; /* Off string */ static const char DC_STR_YES[] = "yes"; /* Enable on string */ static const char DC_STR_NO[] = "no"; /* Enable off string */ static const char DC_STR_SWAP[] = "swap"; /* Default dump device */ @@ -100,10 +102,12 @@ dconf_init(dumpconf_t *dcp, int dcmode) } /* - * Default is contents kernel, and savecore enabled on reboot. + * Default is contents kernel, savecore enabled on reboot, + * savecore saves compressed core files. */ dcp->dc_cflags = DUMP_KERNEL; dcp->dc_enable = DC_ON; + dcp->dc_csave = DC_COMPRESSED; dcp->dc_mode = dcmode; dcp->dc_conf_fp = NULL; @@ -529,6 +533,10 @@ dconf_print(dumpconf_t *dcp, FILE *fp) (void) fprintf(fp, gettext(" Savecore enabled: %s\n"), (dcp->dc_enable == DC_OFF) ? gettext("no") : gettext("yes")); + + (void) fprintf(fp, gettext(" Save compressed: %s\n"), + (dcp->dc_csave == DC_UNCOMPRESSED) ? gettext("off") : + gettext("on")); } int @@ -598,6 +606,23 @@ dconf_str2enable(dumpconf_t *dcp, char *buf) return (-1); } +int +dconf_str2csave(dumpconf_t *dcp, char *buf) +{ + if (strcasecmp(buf, DC_STR_ON) == 0) { + dcp->dc_csave = DC_COMPRESSED; + return (0); + } + + if (strcasecmp(buf, DC_STR_OFF) == 0) { + dcp->dc_csave = DC_UNCOMPRESSED; + return (0); + } + + warn(gettext("invalid save compressed value -- %s\n"), buf); + return (-1); +} + static int print_content(const dumpconf_t *dcp, FILE *fp) { @@ -628,6 +653,13 @@ print_enable(const dumpconf_t *dcp, FILE *fp) } static int +print_csave(const dumpconf_t *dcp, FILE *fp) +{ + return (fprintf(fp, "%s\n", (dcp->dc_csave == DC_COMPRESSED) ? + DC_STR_ON : DC_STR_OFF)); +} + +static int print_savdir(const dumpconf_t *dcp, FILE *fp) { return (fprintf(fp, "%s\n", dcp->dc_savdir)); diff --git a/usr/src/cmd/dumpadm/dconf.h b/usr/src/cmd/dumpadm/dconf.h index ccf0943df9..a74e0a0c20 100644 --- a/usr/src/cmd/dumpadm/dconf.h +++ b/usr/src/cmd/dumpadm/dconf.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DCONF_H #define _DCONF_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/param.h> #include <stdio.h> @@ -41,6 +39,7 @@ typedef struct dumpconf { char dc_savdir[MAXPATHLEN]; /* Savecore dir path */ int dc_cflags; /* Config flags (see <sys/dumpadm.h>) */ int dc_enable; /* Run savecore on boot? (see below) */ + int dc_csave; /* Save dump compressed? (see below) */ int dc_mode; /* Mode flags (see below) */ FILE *dc_conf_fp; /* File pointer for config file */ int dc_conf_fd; /* File descriptor for config file */ @@ -55,6 +54,12 @@ typedef struct dumpconf { #define DC_ON 1 /* Savecore enabled */ /* + * Values for dc_csave (savecore compressed) property: + */ +#define DC_UNCOMPRESSED 0 /* Savecore uncompresses the dump */ +#define DC_COMPRESSED 1 /* Savecore leaves dump compressed */ + +/* * Values for dconf_open mode: */ #define DC_CURRENT 1 /* Kernel overrides file settings */ @@ -71,6 +76,7 @@ extern int dconf_str2device(dumpconf_t *, char *); extern int dconf_str2savdir(dumpconf_t *, char *); extern int dconf_str2content(dumpconf_t *, char *); extern int dconf_str2enable(dumpconf_t *, char *); +extern int dconf_str2csave(dumpconf_t *, char *); #ifdef __cplusplus } diff --git a/usr/src/cmd/dumpadm/main.c b/usr/src/cmd/dumpadm/main.c index 7fa72af9c1..48cf9d692b 100644 --- a/usr/src/cmd/dumpadm/main.c +++ b/usr/src/cmd/dumpadm/main.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/stat.h> #include <locale.h> #include <unistd.h> @@ -37,9 +35,9 @@ static const char USAGE[] = "\ Usage: %s [-nuy] [-c kernel | curproc | all ] [-d dump-device | swap ]\n\ - [-m min {k|m|%%} ] [-s savecore-dir] [-r root-dir]\n"; + [-m min {k|m|%%} ] [-s savecore-dir] [-r root-dir] [-z on|off]\n"; -static const char OPTS[] = "nuyc:d:m:s:r:"; +static const char OPTS[] = "nuyc:d:m:s:r:z:"; static const char PATH_DEVICE[] = "/dev/dump"; static const char PATH_CONFIG[] = "/etc/dumpadm.conf"; @@ -148,6 +146,12 @@ main(int argc, char *argv[]) dc.dc_enable = DC_ON; modified++; break; + + case 'z': + if (dconf_str2csave(&dc, optarg) == -1) + return (E_USAGE); + modified++; + break; } } } diff --git a/usr/src/cmd/file/file.c b/usr/src/cmd/file/file.c index 3bfe6280ce..70cb43c005 100644 --- a/usr/src/cmd/file/file.c +++ b/usr/src/cmd/file/file.c @@ -26,12 +26,10 @@ /* All Rights Reserved */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #define _LARGEFILE64_SOURCE /* Get definitions for the relocation types supported. */ @@ -1142,14 +1140,14 @@ print_elf_flags(Elf_Info EI) (void) printf("%s", gettext(", V8+ Required")); if (flags & EF_SPARC_SUN_US3) { (void) printf("%s", - gettext(", UltraSPARC3 Extensions Required")); + gettext(", UltraSPARC3 Extensions Required")); } else if (flags & EF_SPARC_SUN_US1) { (void) printf("%s", - gettext(", UltraSPARC1 Extensions Required")); + gettext(", UltraSPARC1 Extensions Required")); } if (flags & EF_SPARC_HAL_R1) (void) printf("%s", - gettext(", HaL R1 Extensions Required")); + gettext(", HaL R1 Extensions Required")); break; default: break; @@ -1319,7 +1317,8 @@ lookup(char **tab) i++; for (j = 0; tab[j] != 0; j++) { l = 0; - for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++); + for (k = i; ((r = tab[j][l++]) == fbuf[k] && r != '\0'); k++) + ; if (r == '\0') if (fbuf[k] == ' ' || fbuf[k] == '\n' || fbuf[k] == '\t' || fbuf[k] == '{' || @@ -1626,11 +1625,16 @@ print_dumphdr(const int fd, const dumphdr_t *dhp, uint32_t (*swap)(uint32_t), */ if (swap(dhp->dump_version) > 8 && pread(fd, &dh, sizeof (dumphdr_t), (off_t)0) == sizeof (dumphdr_t)) { + const char *c = swap(dh.dump_flags) & DF_COMPRESSED ? + "compressed " : ""; + const char *l = swap(dh.dump_flags) & DF_LIVE ? + "live" : "crash"; + (void) printf(gettext( - "%s %s %s %u-bit %s crash dump from '%s'\n"), + "%s %s %s %u-bit %s %s%s dump from '%s'\n"), dh.dump_utsname.sysname, dh.dump_utsname.release, dh.dump_utsname.version, swap(dh.dump_wordsize), isa, - dh.dump_utsname.nodename); + c, l, dh.dump_utsname.nodename); } else { (void) printf(gettext("SunOS %u-bit %s crash dump\n"), swap(dhp->dump_wordsize), isa); @@ -1641,11 +1645,11 @@ static void usage(void) { (void) fprintf(stderr, gettext( - "usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n" - " file [-dh] [-M mfile] [-m mfile] -f ffile\n" - " file -i [-h] [-f ffile] file ...\n" - " file -i [-h] -f ffile\n" - " file -c [-d] [-M mfile] [-m mfile]\n")); + "usage: file [-dh] [-M mfile] [-m mfile] [-f ffile] file ...\n" + " file [-dh] [-M mfile] [-m mfile] -f ffile\n" + " file -i [-h] [-f ffile] file ...\n" + " file -i [-h] -f ffile\n" + " file -c [-d] [-M mfile] [-m mfile]\n")); exit(2); } @@ -1683,7 +1687,7 @@ is_in_list(char *str) */ for (i = 0; debug_sections[i] != NULL; i++) { if (strncmp(debug_sections[i], str, - strlen(debug_sections[i])) == 0) { + strlen(debug_sections[i])) == 0) { return (1); } } diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c index 0c1cc673fa..5d0da621cd 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Libkvm Kernel Target * @@ -1563,3 +1561,13 @@ err: mdb_free(kt, sizeof (kt_data_t)); return (-1); } + +int +mdb_kvm_is_compressed_dump(mdb_io_t *io) +{ + dumphdr_t h; + + return (IOP_READ(io, &h, sizeof (dumphdr_t)) == sizeof (dumphdr_t) && + h.dump_magic == DUMP_MAGIC && + (h.dump_flags & DF_COMPRESSED) != 0); +} diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c index b8c6144b80..1fb07f6893 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_main.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -401,6 +401,7 @@ identify_xvm_file(const char *file, int *longmode) int main(int argc, char *argv[], char *envp[]) { + extern int mdb_kvm_is_compressed_dump(mdb_io_t *); mdb_tgt_ctor_f *tgt_ctor = NULL; const char **tgt_argv = alloca(argc * sizeof (char *)); int tgt_argc = 0; @@ -821,6 +822,19 @@ main(int argc, char *argv[], char *envp[]) (void) strcpy((char *)tgt_argv[1], "vmcore."); (void) strcat((char *)tgt_argv[1], object); + if (access(tgt_argv[0], F_OK) == -1 && + access(tgt_argv[1], F_OK) == -1) { + (void) strcpy((char *)tgt_argv[1], "vmdump."); + (void) strcat((char *)tgt_argv[1], object); + if (access(tgt_argv[1], F_OK) == 0) { + mdb_iob_printf(mdb.m_err, + "cannot open compressed dump; " + "decompress using savecore -f %s\n", + tgt_argv[1]); + terminate(0); + } + } + tgt_argc = 2; } @@ -833,6 +847,20 @@ main(int argc, char *argv[], char *envp[]) die("failed to open %s", tgt_argv[0]); /* + * Check for a single vmdump.N compressed dump file, + * and give a helpful message. + */ + if (tgt_argc == 1) { + if (mdb_kvm_is_compressed_dump(io)) { + mdb_iob_printf(mdb.m_err, + "cannot open compressed dump; " + "decompress using savecore -f %s\n", + tgt_argv[0]); + terminate(0); + } + } + + /* * If the target is unknown or is not the rawfile target, do * a gelf_check to determine if the file is an ELF file. If * it is not and the target is unknown, use the rawfile tgt. @@ -889,6 +917,16 @@ main(int argc, char *argv[], char *envp[]) if (access(tgt_argv[1], F_OK) == -1) die("failed to access %s", tgt_argv[1]); + /* *.N case: drop vmdump.N from the list */ + if (tgt_argc == 3) { + if ((io = mdb_fdio_create_path(NULL, + tgt_argv[2], O_RDONLY, 0)) == NULL) + die("failed to open %s", tgt_argv[2]); + if (mdb_kvm_is_compressed_dump(io)) + tgt_argv[--tgt_argc] = NULL; + mdb_io_destroy(io); + } + if ((io = mdb_fdio_create_path(NULL, tgt_argv[1], O_RDONLY, 0)) == NULL) die("failed to open %s", tgt_argv[1]); diff --git a/usr/src/cmd/savecore/Makefile.com b/usr/src/cmd/savecore/Makefile.com index 4b9faf5683..32bb2ea7a2 100644 --- a/usr/src/cmd/savecore/Makefile.com +++ b/usr/src/cmd/savecore/Makefile.com @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# PROG= savecore SRCS= ../savecore.c ../../../uts/common/os/compress.c @@ -33,27 +31,39 @@ include ../../Makefile.cmd CFLAGS += $(CCVERBOSE) CFLAGS64 += $(CCVERBOSE) -CPPFLAGS += -D_LARGEFILE64_SOURCE=1 +CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -DBZ_NO_STDIO -I$(SRC)/uts/common + +BZIP2OBJS = bz2blocksort.o \ + bz2compress.o \ + bz2decompress.o \ + bz2randtable.o \ + bz2bzlib.o \ + bz2crctable.o \ + bz2huffman.o .KEEP_STATE: all: $(PROG) -$(PROG): $(OBJS) - $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) +$(PROG): $(OBJS) $(BZIP2OBJS) + $(LINK.c) -o $(PROG) $(OBJS) $(BZIP2OBJS) $(LDLIBS) $(POST_PROCESS) clean: - $(RM) $(OBJS) + $(RM) $(OBJS) $(BZIP2OBJS) lint: lint_SRCS include ../../Makefile.targ %.o: ../%.c - $(COMPILE.c) $< + $(COMPILE.c) -I$(SRC)/common $< $(POST_PROCESS_O) %.o: ../../../uts/common/os/%.c $(COMPILE.c) $< $(POST_PROCESS_O) + +bz2%.o: ../../../common/bzip2/%.c + $(COMPILE.c) -o $@ -I$(SRC)/common -I$(SRC)/common/bzip2 $< + $(POST_PROCESS_O) diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c index da8d6ee441..62dd900ce0 100644 --- a/usr/src/cmd/savecore/savecore.c +++ b/usr/src/cmd/savecore/savecore.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -36,16 +34,32 @@ #include <time.h> #include <syslog.h> #include <stropts.h> +#include <pthread.h> +#include <limits.h> +#include <atomic.h> #include <sys/mem.h> #include <sys/statvfs.h> #include <sys/dumphdr.h> #include <sys/dumpadm.h> #include <sys/compress.h> #include <sys/sysmacros.h> +#include <sys/stat.h> +#include <sys/resource.h> +#include <bzip2/bzlib.h> + +/* fread/fwrite buffer size */ +#define FBUFSIZE (1ULL << 20) + +/* minimum size for output buffering */ +#define MINCOREBLKSIZE (1ULL << 17) + +/* create this file if metrics collection is enabled in the kernel */ +#define METRICSFILE "METRICS.csv" static char progname[9] = "savecore"; static char *savedir; /* savecore directory */ static char *dumpfile; /* source of raw crash dump */ +static long bounds; /* numeric suffix */ static long pagesize; /* dump pagesize */ static int dumpfd = -1; /* dumpfile descriptor */ static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */ @@ -53,6 +67,15 @@ static offset_t endoff; /* offset of end-of-dump header */ static int verbose; /* chatty mode */ static int disregard_valid_flag; /* disregard valid flag */ static int livedump; /* dump the current running system */ +static int interactive; /* user invoked; no syslog */ +static int csave; /* save dump compressed */ +static int filemode; /* processing file, not dump device */ +static int percent_done; /* progress indicator */ +static hrtime_t startts; /* timestamp at start */ +static volatile uint64_t saved; /* count of pages written */ +static volatile uint64_t zpages; /* count of zero pages not written */ +static dumpdatahdr_t datahdr; /* compression info */ +static long coreblksize; /* preferred write size (st_blksize) */ static void usage(void) @@ -70,9 +93,9 @@ logprint(int logpri, int showmsg, int exitcode, char *message, ...) if (showmsg) { va_start(args, message); - (void) vsnprintf(buf, 1024, message, args); + (void) vsnprintf(buf, sizeof (buf), message, args); (void) fprintf(stderr, "%s: %s\n", progname, buf); - if (logpri >= 0) + if (!interactive && logpri >= 0) syslog(logpri, buf); va_end(args); } @@ -95,14 +118,60 @@ Open(const char *name, int oflags, mode_t mode) } static void +Fread(void *buf, size_t size, FILE *f) +{ + if (fread(buf, size, 1, f) != 1) + logprint(LOG_ERR, 1, 1, "fread: ferror %d feof %d", + ferror(f), feof(f)); +} + +static void +Fwrite(void *buf, size_t size, FILE *f) +{ + if (fwrite(buf, size, 1, f) != 1) + logprint(LOG_ERR, 1, 1, "fwrite: %s", strerror(errno)); +} + +static void +Fseek(offset_t off, FILE *f) +{ + if (fseeko64(f, off, SEEK_SET) != 0) + logprint(LOG_ERR, 1, 1, "fseeko64: %s", strerror(errno)); +} + +typedef struct stat64 Stat_t; + +static void +Fstat(int fd, Stat_t *sb, const char *fname) +{ + if (fstat64(fd, sb) != 0) + logprint(LOG_ERR, 1, 1, "fstat(\"%s\"): %s", fname, + strerror(errno)); +} + +static void +Stat(const char *fname, Stat_t *sb) +{ + if (stat64(fname, sb) != 0) + logprint(LOG_ERR, 1, 1, "stat(\"%s\"): %s", fname, + strerror(errno)); +} + +static void Pread(int fd, void *buf, size_t size, offset_t off) { - if (pread64(fd, buf, size, off) != size) - logprint(LOG_ERR, 1, 1, "pread: %s", strerror(errno)); + ssize_t sz = pread64(fd, buf, size, off); + + if (sz < 0) + logprint(LOG_ERR, 1, 1, + "pread: %s", strerror(errno)); + else if (sz != size) + logprint(LOG_ERR, 1, 1, + "pread: size %ld != %ld", sz, size); } static void -Pwrite(int fd, void *buf, size_t size, offset_t off) +Pwrite(int fd, void *buf, size_t size, off64_t off) { if (pwrite64(fd, buf, size, off) != size) logprint(LOG_ERR, 1, 1, "pwrite: %s", strerror(errno)); @@ -134,28 +203,44 @@ read_number_from_file(const char *filename, long default_value) static void read_dumphdr(void) { - dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); + if (filemode) + dumpfd = Open(dumpfile, O_RDONLY, 0644); + else + dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET; Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr)); pagesize = dumphdr.dump_pagesize; - if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag) - logprint(-1, verbose, 0, "dump already processed"); - if (dumphdr.dump_magic != DUMP_MAGIC) - logprint(-1, verbose, 0, "bad magic number %x", + logprint(-1, 1, 0, "bad magic number %x", dumphdr.dump_magic); + if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag) + logprint(-1, verbose, 0, "dump already processed"); + if (dumphdr.dump_version != DUMP_VERSION) logprint(-1, verbose, 0, "dump version (%d) != %s version (%d)", dumphdr.dump_version, progname, DUMP_VERSION); if (dumphdr.dump_wordsize != DUMP_WORDSIZE) - logprint(LOG_WARNING, 1, 0, + logprint(-1, 1, 0, "dump is from %u-bit kernel - cannot save on %u-bit kernel", dumphdr.dump_wordsize, DUMP_WORDSIZE); + + if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) { + if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION) + logprint(-1, verbose, 0, + "dump data version (%d) != %s data version (%d)", + datahdr.dump_datahdr_version, progname, + DUMP_DATAHDR_VERSION); + } else { + memset(&datahdr, 0, sizeof (datahdr)); + datahdr.dump_maxcsize = pagesize; + } + /* * Read the initial header, clear the valid bits, and compare headers. * The main header may have been overwritten by swapping if we're @@ -170,22 +255,28 @@ read_dumphdr(void) /* * Clear valid bit so we don't complain on every invocation. */ - Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + if (!filemode) + Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); logprint(LOG_ERR, 1, 1, "initial dump header corrupt"); } } static void -check_space(void) +check_space(int csave) { struct statvfs fsb; - int64_t spacefree, dumpsize, minfree; + int64_t spacefree, dumpsize, minfree, datasize; if (statvfs(".", &fsb) < 0) logprint(LOG_ERR, 1, 1, "statvfs: %s", strerror(errno)); - dumpsize = (dumphdr.dump_data - dumphdr.dump_start) + - (int64_t)dumphdr.dump_npages * pagesize; + dumpsize = dumphdr.dump_data - dumphdr.dump_start; + datasize = dumphdr.dump_npages * pagesize; + if (!csave) + dumpsize += datasize; + else + dumpsize += datahdr.dump_data_csize; + spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize; minfree = 1024LL * read_number_from_file("minfree", 1024); if (spacefree < minfree + dumpsize) @@ -202,6 +293,11 @@ build_dump_map(int corefd, const pfn_t *pfn_table) size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t); mem_vtop_t vtop; dump_map_t *dmp = Zalloc(dump_mapsize); + char *inbuf = Zalloc(FBUFSIZE); + FILE *in = fdopen(dup(dumpfd), "rb"); + + setvbuf(in, inbuf, _IOFBF, FBUFSIZE); + Fseek(dumphdr.dump_map, in); corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize); @@ -212,9 +308,7 @@ build_dump_map(int corefd, const pfn_t *pfn_table) pfn_t pfn; uintptr_t h; - Pread(dumpfd, &vtop, sizeof (mem_vtop_t), - dumphdr.dump_map + i * sizeof (mem_vtop_t)); - + Fread(&vtop, sizeof (mem_vtop_t), in); while (last >= first) { middle = (first + last) / 2; pfn = pfn_table[middle]; @@ -245,30 +339,875 @@ build_dump_map(int corefd, const pfn_t *pfn_table) Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map); free(dmp); + fclose(in); + free(inbuf); +} + +/* + * Copy whole sections of the dump device to the file. + */ +static void +Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf, + size_t sz) +{ + size_t nr; + offset_t off = *offp; + + while (nb > 0) { + nr = sz < nb ? sz : (size_t)nb; + Pread(dumpfd, buf, nr, dumpoff); + Pwrite(fd, buf, nr, off); + off += nr; + dumpoff += nr; + nb -= nr; + } + *offp = off; +} + +/* + * Copy pages when the dump data header is missing. + * This supports older kernels with latest savecore. + */ +static void +CopyPages(offset_t dumpoff, offset_t *offp, int fd, char *buf, size_t sz) +{ + uint32_t csize; + FILE *in = fdopen(dup(dumpfd), "rb"); + FILE *out = fdopen(dup(fd), "wb"); + char *cbuf = Zalloc(pagesize); + char *outbuf = Zalloc(FBUFSIZE); + pgcnt_t np = dumphdr.dump_npages; + + setvbuf(out, outbuf, _IOFBF, FBUFSIZE); + setvbuf(in, buf, _IOFBF, sz); + Fseek(dumphdr.dump_data, in); + + Fseek(*offp, out); + while (np > 0) { + Fread(&csize, sizeof (uint32_t), in); + Fwrite(&csize, sizeof (uint32_t), out); + *offp += sizeof (uint32_t); + if (csize > pagesize || csize == 0) { + logprint(LOG_ERR, 1, -1, + "CopyPages: page %lu csize %d (0x%x) pagesize %d", + dumphdr.dump_npages - np, csize, csize, + pagesize); + break; + } + Fread(cbuf, csize, in); + Fwrite(cbuf, csize, out); + *offp += csize; + np--; + } + fclose(in); + fclose(out); + free(outbuf); + free(buf); +} + +/* + * Concatenate dump contents into a new file. + * Update corehdr with new offsets. + */ +static void +copy_crashfile(const char *corefile) +{ + int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + size_t bufsz = FBUFSIZE; + char *inbuf = Zalloc(bufsz); + offset_t coreoff; + size_t nb; + + logprint(LOG_ERR, verbose, -1, + "Copying %s to %s/%s\n", dumpfile, savedir, corefile); + + /* + * This dump file is still compressed + */ + corehdr.dump_flags |= DF_COMPRESSED | DF_VALID; + + /* + * Leave room for corehdr, it is updated and written last + */ + corehdr.dump_start = 0; + coreoff = sizeof (corehdr); + + /* + * Read in the compressed symbol table, copy it to corefile. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_ksyms = coreoff; + Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd, + inbuf, bufsz); + + /* + * Save the pfn table. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_pfn = coreoff; + Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff, + corefd, inbuf, bufsz); + + /* + * Save the dump map. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_map = coreoff; + Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t), + &coreoff, corefd, inbuf, bufsz); + + /* + * Save the data pages. + */ + coreoff = roundup(coreoff, pagesize); + corehdr.dump_data = coreoff; + if (datahdr.dump_data_csize != 0) + Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff, + corefd, inbuf, bufsz); + else + CopyPages(dumphdr.dump_data, &coreoff, corefd, inbuf, bufsz); + + /* + * Now write the modified dump header to front and end of the copy. + * Make it look like a valid dump device. + * + * From dumphdr.h: Two headers are written out: one at the + * beginning of the dump, and the other at the very end of the + * dump device. The terminal header is at a known location + * (end of device) so we can always find it. + * + * Pad with zeros to each DUMP_OFFSET boundary. + */ + memset(inbuf, 0, DUMP_OFFSET); + + nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1)); + if (nb > 0) { + Pwrite(corefd, inbuf, nb, coreoff); + coreoff += nb; + } + + Pwrite(corefd, &corehdr, sizeof (corehdr), coreoff); + coreoff += sizeof (corehdr); + + Pwrite(corefd, &datahdr, sizeof (datahdr), coreoff); + coreoff += sizeof (datahdr); + + nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1)); + if (nb > 0) { + Pwrite(corefd, inbuf, nb, coreoff); + } + + free(inbuf); + Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start); + + /* + * Write out the modified dump header to the dump device. + * The dump device has been processed, so DF_VALID is clear. + */ + if (!filemode) + Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + + (void) close(corefd); +} + +/* + * compressed streams + */ +typedef struct blockhdr blockhdr_t; +typedef struct block block_t; + +struct blockhdr { + block_t *head; + block_t *tail; +}; + +struct block { + block_t *next; + char *block; + int size; +}; + +typedef enum streamstate { + STREAMSTART, + STREAMPAGES +} streamstate_t; + +typedef struct stream { + streamstate_t state; + int init; + int tag; + int bound; + int nout; + char *blkbuf; + blockhdr_t blocks; + pgcnt_t pagenum; + pgcnt_t curpage; + pgcnt_t npages; + pgcnt_t done; + bz_stream strm; + dumpcsize_t sc; + dumpstreamhdr_t sh; +} stream_t; + +static stream_t *streams; +static stream_t *endstreams; + +const int cs = sizeof (dumpcsize_t); + +typedef struct tinfo { + pthread_t tid; + int corefd; +} tinfo_t; + +static int threads_stop; +static int threads_active; +static tinfo_t *tinfo; +static tinfo_t *endtinfo; + +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t cvfree = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cvwork = PTHREAD_COND_INITIALIZER; +static pthread_cond_t cvbarrier = PTHREAD_COND_INITIALIZER; + +static blockhdr_t freeblocks; + +static void +enqt(blockhdr_t *h, block_t *b) +{ + b->next = NULL; + if (h->tail == NULL) + h->head = b; + else + h->tail->next = b; + h->tail = b; +} + +static block_t * +deqh(blockhdr_t *h) +{ + block_t *b = h->head; + + if (b != NULL) { + h->head = b->next; + if (h->head == NULL) + h->tail = NULL; + } + return (b); +} + +static void *runstreams(void *arg); + +static void +initstreams(int corefd, int nstreams, int maxcsize) +{ + int nthreads; + int nblocks; + int i; + block_t *b; + tinfo_t *t; + + nthreads = sysconf(_SC_NPROCESSORS_ONLN); + if (nstreams < nthreads) + nthreads = nstreams; + if (nthreads < 1) + nthreads = 1; + nblocks = nthreads * 2; + + tinfo = Zalloc(nthreads * sizeof (tinfo_t)); + endtinfo = &tinfo[nthreads]; + + /* init streams */ + streams = Zalloc(nstreams * sizeof (stream_t)); + endstreams = &streams[nstreams]; + + /* init stream block buffers */ + for (i = 0; i < nblocks; i++) { + b = Zalloc(sizeof (block_t)); + b->block = Zalloc(maxcsize); + enqt(&freeblocks, b); + } + + /* init worker threads */ + pthread_mutex_lock(&lock); + threads_active = 1; + threads_stop = 0; + for (t = tinfo; t != endtinfo; t++) { + t->corefd = dup(corefd); + if (t->corefd < 0) { + nthreads = t - tinfo; + endtinfo = t; + break; + } + if (pthread_create(&t->tid, NULL, runstreams, t) != 0) + logprint(LOG_ERR, 1, 1, "pthread_create: %s", + strerror(errno)); + } + pthread_mutex_unlock(&lock); +} + +static void +sbarrier() +{ + stream_t *s; + + pthread_mutex_lock(&lock); + for (s = streams; s != endstreams; s++) { + while (s->bound || s->blocks.head != NULL) + pthread_cond_wait(&cvbarrier, &lock); + } + pthread_mutex_unlock(&lock); +} + +static void +stopstreams() +{ + tinfo_t *t; + + if (threads_active) { + sbarrier(); + pthread_mutex_lock(&lock); + threads_stop = 1; + pthread_cond_signal(&cvwork); + pthread_mutex_unlock(&lock); + for (t = tinfo; t != endtinfo; t++) + pthread_join(t->tid, NULL); + free(tinfo); + tinfo = NULL; + threads_active = 0; + } +} + +static block_t * +getfreeblock() +{ + block_t *b; + + pthread_mutex_lock(&lock); + while ((b = deqh(&freeblocks)) == NULL) + pthread_cond_wait(&cvfree, &lock); + pthread_mutex_unlock(&lock); + return (b); +} + +/* data page offset from page number */ +#define BTOP(b) ((b) >> dumphdr.dump_pageshift) +#define PTOB(p) ((p) << dumphdr.dump_pageshift) +#define DATAOFF(p) (corehdr.dump_data + PTOB(p)) + +/* check for coreblksize boundary */ +static int +isblkbnd(pgcnt_t pgnum) +{ + return (P2PHASE(DATAOFF(pgnum), coreblksize) == 0); +} + +static int +iszpage(char *buf) +{ + size_t sz; + uint64_t *pl; + + pl = (uint64_t *)(buf); + for (sz = 0; sz < pagesize; sz += sizeof (*pl)) + if (*pl++ != 0) + return (0); + return (1); +} + +volatile uint_t *hist; + +/* write pages to the core file */ +static void +putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np) +{ + atomic_inc_uint(&hist[np]); + if (np > 0) + Pwrite(corefd, buf, PTOB(np), DATAOFF(pgnum)); +} + +/* + * Process one lzjb block. + * No object (stream header or page) will be split over a block boundary. + */ +static void +lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz) +{ + int rc = 0; + int in = 0; + int csize; + int doflush; + char *out; + size_t dsize; + dumpcsize_t sc; + dumpstreamhdr_t sh; + + if (!s->init) { + s->init = 1; + if (s->blkbuf == NULL) + s->blkbuf = Zalloc(coreblksize); + s->state = STREAMSTART; + } + while (in < blocksz) { + switch (s->state) { + case STREAMSTART: + memcpy(&sh, block + in, sizeof (sh)); + in += sizeof (sh); + if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMSTART: bad stream header"); + if (sh.stream_npages > datahdr.dump_maxrange) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMSTART: bad range: %d > %d", + sh.stream_npages, datahdr.dump_maxrange); + s->pagenum = sh.stream_pagenum; + s->npages = sh.stream_npages; + s->curpage = s->pagenum; + s->nout = 0; + s->done = 0; + s->state = STREAMPAGES; + break; + case STREAMPAGES: + memcpy(&sc, block + in, cs); + in += cs; + csize = DUMP_GET_CSIZE(sc); + if (csize > pagesize) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMPAGES: bad csize=%d", csize); + + out = s->blkbuf + PTOB(s->nout); + dsize = decompress(block + in, out, csize, pagesize); + + if (dsize != pagesize) + logprint(LOG_ERR, 1, 1, + "LZJB STREAMPAGES: dsize %d != pagesize %d", + dsize, pagesize); + + in += csize; + atomic_inc_64(&saved); + + doflush = 0; + if (s->nout == 0 && iszpage(out)) { + doflush = 1; + atomic_inc_64(&zpages); + } else if (++s->nout >= BTOP(coreblksize) || + isblkbnd(s->curpage + s->nout)) { + doflush = 1; + } + if (++s->done >= s->npages) { + s->state = STREAMSTART; + doflush = 1; + } + if (doflush) { + putpage(corefd, s->blkbuf, s->curpage, s->nout); + s->nout = 0; + s->curpage = s->pagenum + s->done; + } + break; + } + } +} + +/* bzlib library reports errors with this callback */ +void +bz_internal_error(int errcode) +{ + logprint(LOG_ERR, 1, 1, "bz_internal_error: err %s\n", + BZ2_bzErrorString(errcode)); +} + +/* + * Return one object in the stream. + * + * An object (stream header or page) will likely span an input block + * of compression data. Return non-zero when an entire object has been + * retrieved from the stream. + */ +static int +bz2decompress(stream_t *s, void *buf, size_t size) +{ + int rc; + + if (s->strm.avail_out == 0) { + s->strm.next_out = buf; + s->strm.avail_out = size; + } + while (s->strm.avail_in > 0) { + rc = BZ2_bzDecompress(&s->strm); + if (rc == BZ_STREAM_END) { + rc = BZ2_bzDecompressReset(&s->strm); + if (rc != BZ_OK) + logprint(LOG_ERR, 1, 1, + "BZ2_bzDecompressReset: %s", + BZ2_bzErrorString(rc)); + continue; + } + + if (s->strm.avail_out == 0) + break; + } + return (s->strm.avail_out == 0); +} + +/* + * Process one bzip2 block. + * The interface is documented here: + * http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html + */ +static void +bz2block(int corefd, stream_t *s, char *block, size_t blocksz) +{ + int rc = 0; + int doflush; + char *out; + + if (!s->init) { + s->init = 1; + rc = BZ2_bzDecompressInit(&s->strm, 0, 0); + if (rc != BZ_OK) + logprint(LOG_ERR, 1, 1, + "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc)); + if (s->blkbuf == NULL) + s->blkbuf = Zalloc(coreblksize); + s->strm.avail_out = 0; + s->state = STREAMSTART; + } + s->strm.next_in = block; + s->strm.avail_in = blocksz; + + while (s->strm.avail_in > 0) { + switch (s->state) { + case STREAMSTART: + if (!bz2decompress(s, &s->sh, sizeof (s->sh))) + return; + if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0) + logprint(LOG_ERR, 1, 1, + "BZ2 STREAMSTART: bad stream header"); + if (s->sh.stream_npages > datahdr.dump_maxrange) + logprint(LOG_ERR, 1, 1, + "BZ2 STREAMSTART: bad range: %d > %d", + s->sh.stream_npages, datahdr.dump_maxrange); + s->pagenum = s->sh.stream_pagenum; + s->npages = s->sh.stream_npages; + s->curpage = s->pagenum; + s->nout = 0; + s->done = 0; + s->state = STREAMPAGES; + break; + case STREAMPAGES: + out = s->blkbuf + PTOB(s->nout); + if (!bz2decompress(s, out, pagesize)) + return; + + atomic_inc_64(&saved); + + doflush = 0; + if (s->nout == 0 && iszpage(out)) { + doflush = 1; + atomic_inc_64(&zpages); + } else if (++s->nout >= BTOP(coreblksize) || + isblkbnd(s->curpage + s->nout)) { + doflush = 1; + } + if (++s->done >= s->npages) { + s->state = STREAMSTART; + doflush = 1; + } + if (doflush) { + putpage(corefd, s->blkbuf, s->curpage, s->nout); + s->nout = 0; + s->curpage = s->pagenum + s->done; + } + break; + } + } +} + +/* report progress */ +static void +report_progress() +{ + int sec, percent; + + if (!interactive) + return; + + percent = saved * 100LL / corehdr.dump_npages; + if (percent > percent_done) { + sec = (gethrtime() - startts) / 1000 / 1000 / 1000; + (void) printf("\r%2d:%02d %3d%% done", sec / 60, sec % 60, + percent); + (void) fflush(stdout); + percent_done = percent; + } +} + +/* thread body */ +static void * +runstreams(void *arg) +{ + tinfo_t *t = arg; + stream_t *s; + block_t *b; + int bound; + + pthread_mutex_lock(&lock); + while (!threads_stop) { + bound = 0; + for (s = streams; s != endstreams; s++) { + if (s->bound || s->blocks.head == NULL) + continue; + s->bound = 1; + bound = 1; + pthread_cond_signal(&cvwork); + while (s->blocks.head != NULL) { + b = deqh(&s->blocks); + pthread_mutex_unlock(&lock); + + if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2) + lzjbblock(t->corefd, s, b->block, + b->size); + else + bz2block(t->corefd, s, b->block, + b->size); + + pthread_mutex_lock(&lock); + enqt(&freeblocks, b); + pthread_cond_signal(&cvfree); + + report_progress(); + } + s->bound = 0; + pthread_cond_signal(&cvbarrier); + } + if (!bound && !threads_stop) + pthread_cond_wait(&cvwork, &lock); + } + close(t->corefd); + pthread_cond_signal(&cvwork); + pthread_mutex_unlock(&lock); + return (arg); +} + +/* + * Process compressed pages. + * + * The old format, now called single-threaded lzjb, is a 32-bit size + * word followed by 'size' bytes of lzjb compression data for one + * page. The new format extends this by storing a 12-bit "tag" in the + * upper bits of the size word. When the size word is pagesize or + * less, it is assumed to be one lzjb page. When the size word is + * greater than pagesize, it is assumed to be a "stream block", + * belonging to up to 4095 streams. In practice, the number of streams + * is set to one less than the number of CPUs running at crash + * time. One CPU processes the crash dump, the remaining CPUs + * separately process groups of data pages. + * + * savecore creates a thread per stream, but never more threads than + * the number of CPUs running savecore. This is because savecore can + * be processing a crash file from a remote machine, which may have + * more CPUs. + * + * When the kernel uses parallel lzjb or parallel bzip2, we expect a + * series of 128KB blocks of compression data. In this case, each + * block has a "tag", in the range 1-4095. Each block is handed off to + * to the threads running "runstreams". The dump format is either lzjb + * or bzip2, never a mixture. These threads, in turn, process the + * compression data for groups of pages. Groups of pages are delimited + * by a "stream header", which indicates a starting pfn and number of + * pages. When a stream block has been read, the condition variable + * "cvwork" is signalled, which causes one of the avaiable threads to + * wake up and process the stream. + * + * In the parallel case there will be streams blocks encoding all data + * pages. The stream of blocks is terminated by a zero size + * word. There can be a few lzjb pages tacked on the end, depending on + * the architecture. The sbarrier function ensures that all stream + * blocks have been processed so that the page number for the few + * single pages at the end can be known. + */ +static void +decompress_pages(int corefd) +{ + char *cpage = NULL; + char *dpage = NULL; + char *out; + pgcnt_t curpage; + block_t *b; + FILE *dumpf; + FILE *tracef = NULL; + stream_t *s; + size_t dsize; + size_t insz = FBUFSIZE; + char *inbuf = Zalloc(insz); + uint32_t csize; + dumpcsize_t dcsize; + dumpstreamhdr_t sh; + int nstreams = datahdr.dump_nstreams; + int maxcsize = datahdr.dump_maxcsize; + int nout, tag, doflush; + + dumpf = fdopen(dup(dumpfd), "rb"); + if (dumpf == NULL) + logprint(LOG_ERR, 1, 1, "fdopen: %s", strerror(errno)); + + setvbuf(dumpf, inbuf, _IOFBF, insz); + Fseek(dumphdr.dump_data, dumpf); + + while (1) { + + /* + * The csize word delimits stream blocks. + * See dumphdr.h for a description. + */ + Fread(&dcsize, sizeof (dcsize), dumpf); + + tag = DUMP_GET_TAG(dcsize); + csize = DUMP_GET_CSIZE(dcsize); + + if (tag != 0) { /* a stream block */ + + if (nstreams == 0) + logprint(LOG_ERR, 1, 1, + "starting data header is missing"); + + if (tag > nstreams) + logprint(LOG_ERR, 1, 1, + "stream tag %d not in range 1..%d", + tag, nstreams); + + if (csize > maxcsize) + logprint(LOG_ERR, 1, 1, + "block size 0x%x > max csize 0x%x", + csize, maxcsize); + + if (streams == NULL) + initstreams(corefd, nstreams, maxcsize); + s = &streams[tag - 1]; + s->tag = tag; + + b = getfreeblock(); + b->size = csize; + Fread(b->block, csize, dumpf); + + pthread_mutex_lock(&lock); + enqt(&s->blocks, b); + if (!s->bound) + pthread_cond_signal(&cvwork); + pthread_mutex_unlock(&lock); + + } else if (csize > 0) { /* one lzjb page */ + + if (csize > pagesize) + logprint(LOG_ERR, 1, 1, + "csize 0x%x > pagesize 0x%x", + csize, pagesize); + + if (cpage == NULL) + cpage = Zalloc(pagesize); + if (dpage == NULL) { + dpage = Zalloc(coreblksize); + nout = 0; + } + + Fread(cpage, csize, dumpf); + + out = dpage + PTOB(nout); + dsize = decompress(cpage, out, csize, pagesize); + + if (dsize != pagesize) + logprint(LOG_ERR, 1, 1, + "dsize 0x%x != pagesize 0x%x", + dsize, pagesize); + + /* + * wait for streams to flush so that 'saved' is correct + */ + if (threads_active) + sbarrier(); + + doflush = 0; + if (nout == 0) + curpage = saved; + + atomic_inc_64(&saved); + + if (nout == 0 && iszpage(dpage)) { + doflush = 1; + atomic_inc_64(&zpages); + } else if (++nout >= BTOP(coreblksize) || + isblkbnd(curpage + nout) || + saved >= dumphdr.dump_npages) { + doflush = 1; + } + + if (doflush) { + putpage(corefd, dpage, curpage, nout); + nout = 0; + } + + report_progress(); + + /* + * Non-streams lzjb does not use blocks. Stop + * here if all the pages have been decompressed. + */ + if (saved >= dumphdr.dump_npages) + break; + + } else { + break; /* end of data */ + } + } + + stopstreams(); + if (tracef != NULL) + fclose(tracef); + fclose(dumpf); + if (inbuf) + free(inbuf); + if (cpage) + free(cpage); + if (dpage) + free(dpage); + if (streams) + free(streams); } static void build_corefile(const char *namelist, const char *corefile) { - char *inbuf = Zalloc(pagesize); - char *outbuf = Zalloc(pagesize); size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t); size_t ksyms_size = dumphdr.dump_ksyms_size; size_t ksyms_csize = dumphdr.dump_ksyms_csize; - pfn_t *pfn_table = Zalloc(pfn_table_size); + pfn_t *pfn_table; char *ksyms_base = Zalloc(ksyms_size); char *ksyms_cbase = Zalloc(ksyms_csize); + size_t ksyms_dsize; + Stat_t st; int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644); - offset_t dumpoff; - int percent_done = 0; - pgcnt_t saved = 0; - uint32_t csize; - size_t dsize; (void) printf("Constructing namelist %s/%s\n", savedir, namelist); /* + * Determine the optimum write size for the core file + */ + Fstat(corefd, &st, corefile); + + if (verbose > 1) + printf("%s: %ld block size\n", corefile, st.st_blksize); + coreblksize = st.st_blksize; + if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize)) + coreblksize = MINCOREBLKSIZE; + + hist = Zalloc((sizeof (uint64_t) * BTOP(coreblksize)) + 1); + + /* + * This dump file is now uncompressed + */ + corehdr.dump_flags &= ~DF_COMPRESSED; + + /* * Read in the compressed symbol table, copy it to corefile, * decompress it, and write the result to namelist. */ @@ -276,10 +1215,12 @@ build_corefile(const char *namelist, const char *corefile) Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms); Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms); - if ((dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize, - ksyms_size)) != ksyms_size) - logprint(LOG_WARNING, 1, -1, "bad data in symbol table, %lu of %lu" - " bytes saved", dsize, ksyms_size); + ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize, + ksyms_size); + if (ksyms_dsize != ksyms_size) + logprint(LOG_WARNING, 1, -1, + "bad data in symbol table, %lu of %lu bytes saved", + ksyms_dsize, ksyms_size); Pwrite(namefd, ksyms_base, ksyms_size, 0); (void) close(namefd); @@ -291,6 +1232,7 @@ build_corefile(const char *namelist, const char *corefile) /* * Read in and write out the pfn table. */ + pfn_table = Zalloc(pfn_table_size); corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize); Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn); Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn); @@ -300,29 +1242,19 @@ build_corefile(const char *namelist, const char *corefile) */ corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize); build_dump_map(corefd, pfn_table); + free(pfn_table); /* - * Decompress and save the pages. + * Decompress the pages */ - dumpoff = dumphdr.dump_data; - while (saved < dumphdr.dump_npages) { - Pread(dumpfd, &csize, sizeof (uint32_t), dumpoff); - dumpoff += sizeof (uint32_t); - if (csize > pagesize) - break; - Pread(dumpfd, inbuf, csize, dumpoff); - dumpoff += csize; - if (decompress(inbuf, outbuf, csize, pagesize) != pagesize) - break; - Pwrite(corefd, outbuf, pagesize, - corehdr.dump_data + saved * pagesize); - if (++saved * 100LL / dumphdr.dump_npages > percent_done) { - (void) printf("\r%3d%% done", ++percent_done); - (void) fflush(stdout); - } - } + decompress_pages(corefd); + (void) printf(": %ld of %ld pages saved\n", (pgcnt_t)saved, + dumphdr.dump_npages); - (void) printf(": %ld of %ld pages saved\n", saved, dumphdr.dump_npages); + if (verbose) + (void) printf("%ld (%ld%%) zero pages were not written\n", + (pgcnt_t)zpages, (pgcnt_t)zpages * 100 / + dumphdr.dump_npages); if (saved != dumphdr.dump_npages) logprint(LOG_WARNING, 1, -1, "bad data after page %ld", saved); @@ -331,10 +1263,10 @@ build_corefile(const char *namelist, const char *corefile) * Write out the modified dump headers. */ Pwrite(corefd, &corehdr, sizeof (corehdr), 0); - Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); + if (!filemode) + Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); (void) close(corefd); - (void) close(dumpfd); } /* @@ -382,16 +1314,18 @@ message_save(void) break; if (ld.ld_magic != LOG_MAGIC) - logprint(-1, verbose, 0, "bad magic %x", ld.ld_magic); + logprint(LOG_ERR, verbose, 0, "bad magic %x", + ld.ld_magic); if (dat.len >= DUMP_LOGSIZE) - logprint(-1, verbose, 0, "bad size %d", ld.ld_msgsize); + logprint(LOG_ERR, verbose, 0, "bad size %d", + ld.ld_msgsize); Pread(dumpfd, ctl.buf, ctl.len, dumpoff); dumpoff += ctl.len; if (ld.ld_csum != checksum32(ctl.buf, ctl.len)) - logprint(-1, verbose, 0, "bad log_ctl checksum"); + logprint(LOG_ERR, verbose, 0, "bad log_ctl checksum"); lc.flags |= SL_LOGONLY; @@ -399,7 +1333,7 @@ message_save(void) dumpoff += dat.len; if (ld.ld_msum != checksum32(dat.buf, dat.len)) - logprint(-1, verbose, 0, "bad message checksum"); + logprint(LOG_ERR, verbose, 0, "bad message checksum"); if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1) logprint(LOG_ERR, 1, 1, "putpmsg: %s", strerror(errno)); @@ -410,17 +1344,37 @@ message_save(void) return (0); } +static long +getbounds(const char *f) +{ + long b = -1; + const char *p = strrchr(f, '/'); + + sscanf(p ? p + 1 : f, "vmdump.%ld", &b); + return (b); +} + int main(int argc, char *argv[]) { - int c, bfd; + int i, n, c, bfd; int mflag = 0; - long bounds; - char namelist[30], corefile[30], boundstr[30]; + Stat_t st; + struct rlimit rl; + long filebounds = -1; + char namelist[30], corefile[30], boundstr[30], metricsname[PATH_MAX]; + + startts = gethrtime(); + + getrlimit(RLIMIT_NOFILE, &rl); + rl.rlim_cur = rl.rlim_max; + setrlimit(RLIMIT_NOFILE, &rl); openlog(progname, LOG_ODELAY, LOG_AUTH); + (void) defopen("/etc/dumpadm.conf"); - savedir = defread("DUMPADM_SAVDIR="); + savedir = strdup(defread("DUMPADM_SAVDIR=")); + sprintf(metricsname, "%s/%s", savedir, METRICSFILE); while ((c = getopt(argc, argv, "Lvdmf:")) != EOF) { switch (c) { @@ -438,29 +1392,23 @@ main(int argc, char *argv[]) break; case 'f': dumpfile = optarg; + filebounds = getbounds(dumpfile); break; case '?': usage(); } } + interactive = isatty(STDOUT_FILENO); + if (dumpfile == NULL || livedump) dumpfd = Open("/dev/dump", O_RDONLY, 0444); if (dumpfile == NULL) { dumpfile = Zalloc(MAXPATHLEN); - if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) { - /* - * If this isn't an interactive session, we are running - * as part of the boot process. If this is the case, - * don't complain about the lack of dump device. - */ - if (isatty(STDOUT_FILENO)) - logprint(LOG_ERR, 1, 1, - "no dump device configured"); - else - return (1); - } + if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) + logprint(-1, interactive, 1, + "no dump device configured"); } if (mflag) @@ -468,6 +1416,7 @@ main(int argc, char *argv[]) if (optind == argc - 1) savedir = argv[optind]; + if (savedir == NULL || optind < argc - 1) usage(); @@ -475,6 +1424,14 @@ main(int argc, char *argv[]) logprint(-1, 1, 1, "dedicated dump device required"); (void) close(dumpfd); + dumpfd = -1; + + Stat(dumpfile, &st); + + filemode = S_ISREG(st.st_mode); + + if (!filemode && defread("DUMPADM_CSAVE=off") == NULL) + csave = 1; read_dumphdr(); @@ -520,24 +1477,145 @@ main(int argc, char *argv[]) if ((dumphdr.dump_flags & DF_COMPLETE) == 0) logprint(LOG_WARNING, 1, -1, "incomplete dump on dump device"); - (void) printf("System dump time: %s", ctime(&dumphdr.dump_crashtime)); + logprint(LOG_WARNING, 1, -1, "System dump time: %s", + ctime(&dumphdr.dump_crashtime)); + + check_space(csave); + + if (filebounds < 0) + bounds = read_number_from_file("bounds", 0); + else + bounds = filebounds; + + if (csave) { + size_t metrics_size = datahdr.dump_metrics; + + (void) sprintf(corefile, "vmdump.%ld", bounds); + + datahdr.dump_metrics = 0; + + logprint(LOG_ERR, 1, -1, + "Saving compressed system crash dump in %s/%s", + savedir, corefile); + + copy_crashfile(corefile); + + if (metrics_size > 0) { + int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; + FILE *mfile = fopen(metricsname, "a"); + char *metrics = Zalloc(metrics_size + 1); + + Pread(dumpfd, metrics, metrics_size, endoff + + sizeof (dumphdr) + sizeof (datahdr)); + + if (mfile == NULL) { + logprint(LOG_WARNING, 1, -1, + "Can't create %s:\n%s", + metricsname, metrics); + } else { + fprintf(mfile, "[[[[,,,"); + for (i = 0; i < argc; i++) + fprintf(mfile, "%s ", argv[i]); + fprintf(mfile, "\n"); + fprintf(mfile, ",,,%s %s %s %s %s\n", + dumphdr.dump_utsname.sysname, + dumphdr.dump_utsname.nodename, + dumphdr.dump_utsname.release, + dumphdr.dump_utsname.version, + dumphdr.dump_utsname.machine); + fprintf(mfile, ",,,%s dump time %s\n", + dumphdr.dump_flags & DF_LIVE ? "Live" : + "Crash", ctime(&dumphdr.dump_crashtime)); + fprintf(mfile, ",,,%s/%s\n", savedir, corefile); + fprintf(mfile, "Metrics:\n%s\n", metrics); + fprintf(mfile, "Copy pages,%ld\n", dumphdr. + dump_npages); + fprintf(mfile, "Copy time,%d\n", sec); + fprintf(mfile, "Copy pages/sec,%ld\n", + dumphdr.dump_npages / sec); + fprintf(mfile, "]]]]\n"); + fclose(mfile); + } + free(metrics); + } - check_space(); + logprint(LOG_ERR, 1, -1, + "Decompress the crash dump with " + "\n'savecore -vf %s/%s'", + savedir, corefile); + + } else { + (void) sprintf(namelist, "unix.%ld", bounds); + (void) sprintf(corefile, "vmcore.%ld", bounds); + + if (interactive && filebounds >= 0 && access(corefile, F_OK) + == 0) + logprint(-1, 1, 1, + "%s already exists: remove with " + "'rm -f %s/{unix,vmcore}.%ld'", + corefile, savedir, bounds); + + logprint(LOG_ERR, 1, -1, + "saving system crash dump in %s/{unix,vmcore}.%ld", + savedir, bounds); + + build_corefile(namelist, corefile); + + if (access(metricsname, F_OK) == 0) { + int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; + FILE *mfile = fopen(metricsname, "a"); + + fprintf(mfile, "[[[[,,,"); + for (i = 0; i < argc; i++) + fprintf(mfile, "%s ", argv[i]); + fprintf(mfile, "\n"); + fprintf(mfile, ",,,%s/%s\n", savedir, corefile); + fprintf(mfile, ",,,%s %s %s %s %s\n", + dumphdr.dump_utsname.sysname, + dumphdr.dump_utsname.nodename, + dumphdr.dump_utsname.release, + dumphdr.dump_utsname.version, + dumphdr.dump_utsname.machine); + fprintf(mfile, "Uncompress pages,%ld\n", saved); + fprintf(mfile, "Uncompress time,%d\n", sec); + fprintf(mfile, "Uncompress pages/sec,%ld\n", + saved / sec); + fprintf(mfile, "]]]]\n"); + fclose(mfile); + } + } - bounds = read_number_from_file("bounds", 0); + if (filebounds < 0) { + (void) sprintf(boundstr, "%ld\n", bounds + 1); + bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644); + Pwrite(bfd, boundstr, strlen(boundstr), 0); + (void) close(bfd); + } - (void) sprintf(namelist, "unix.%ld", bounds); - (void) sprintf(corefile, "vmcore.%ld", bounds); + if (verbose) { + int sec = (gethrtime() - startts) / 1000 / 1000 / 1000; - syslog(LOG_ERR, "saving system crash dump in %s/*.%ld", - savedir, bounds); + printf("%d:%02d dump %s is done\n", + sec / 60, sec % 60, + csave ? "copy" : "decompress"); + } - build_corefile(namelist, corefile); + if (verbose > 1 && hist != NULL) { + int i, nw; + + for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i) + nw += hist[i] * i; + printf("pages count %%\n"); + for (i = 0; i <= BTOP(coreblksize); ++i) { + if (hist[i] == 0) + continue; + printf("%3d %5u %6.2f\n", + i, hist[i], 100.0 * hist[i] * i / nw); + } + } - (void) sprintf(boundstr, "%ld\n", bounds + 1); - bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644); - Pwrite(bfd, boundstr, strlen(boundstr), 0); - (void) close(bfd); + (void) close(dumpfd); + dumpfd = -1; return (0); } diff --git a/usr/src/common/bzip2/LICENSE b/usr/src/common/bzip2/LICENSE new file mode 100644 index 0000000000..f420cffb67 --- /dev/null +++ b/usr/src/common/bzip2/LICENSE @@ -0,0 +1,42 @@ + +-------------------------------------------------------------------------- + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2007 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@bzip.org +bzip2/libbzip2 version 1.0.5 of 10 December 2007 + +-------------------------------------------------------------------------- diff --git a/usr/src/common/bzip2/Solaris.README.txt b/usr/src/common/bzip2/Solaris.README.txt new file mode 100644 index 0000000000..5cec61ed9b --- /dev/null +++ b/usr/src/common/bzip2/Solaris.README.txt @@ -0,0 +1,492 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +The source in this directory has been derived from libbzip2 version +1.0.5 downloaded from http://www.bzip.org. + +In an effort to provide ease of syncing with the upstream code, this +source hasn't changed much. The usual Solaris coding standards have +been waived. It does not pass cstyle. But, enough modifications were +made so that the code does compile and lint cleanly. + +Some modifications have been made for use in the Solaris kernel: +1) compilation errors were corrected +2) lint complaints were fixed +3) a few utility interfaces were added + BZ2_bzCompressInitSize + BZ2_bzCompressReset + BZ2_bzDecompressReset + BZ2_bzErrorString + + +Here is a complete list of changes made by Sun to the original 1.0.5 +source: + +------------------------ blocksort.c ------------------------ +diff bzip2-1.0.5/blocksort.c ./blocksort.c + +------------------------ bzlib.c ------------------------ +diff bzip2-1.0.5/bzlib.c ./bzlib.c +98a99,116 +> /* +> * Added for Solaris kernel +> */ +> #define BZES \ +> BZE(BZ_OK) \ +> BZE(BZ_RUN_OK) \ +> BZE(BZ_FLUSH_OK) \ +> BZE(BZ_FINISH_OK) \ +> BZE(BZ_STREAM_END) \ +> BZE(BZ_SEQUENCE_ERROR) \ +> BZE(BZ_PARAM_ERROR) \ +> BZE(BZ_MEM_ERROR) \ +> BZE(BZ_DATA_ERROR) \ +> BZE(BZ_DATA_ERROR_MAGIC) \ +> BZE(BZ_IO_ERROR) \ +> BZE(BZ_UNEXPECTED_EOF) \ +> BZE(BZ_OUTBUFF_FULL) \ +> BZE(BZ_CONFIG_ERROR) +99a118,144 +> BZ_EXTERN const char * BZ_API(BZ2_bzErrorString) ( +> int error_code +> ) +> { +> switch (error_code) +> { +> #define BZE(x) case x: return (#x); +> BZES +> #undef BZE +> } +> return ("BZ_UNKNOWN_ERROR"); +> } +> +> #include <sys/sysmacros.h> +> +> #ifdef _KERNEL +> +> #include <sys/types.h> +> #include <sys/cmn_err.h> +> #include <sys/kmem.h> +> +> void +> bz_internal_error(int errcode) +> { +> panic("bzip2 internal error: %s\n", BZ2_bzErrorString(errcode)); +> } +> +100a146,150 +> typedef struct { +> char *buf; +> size_t sz; +> } bzap; +> +103a154,181 +> size_t sz = sizeof (bzap) + BZ2_BZALLOC_ALIGN + (items * size); +> uintptr_t p = (uintptr_t)kmem_alloc(sz, KM_SLEEP); +> +> if (p != NULL) { +> bzap *pp = (bzap *)((p + sizeof (bzap) + BZ2_BZALLOC_ALIGN - 1) & +> -BZ2_BZALLOC_ALIGN); +> pp[-1].buf = (void *)p; +> pp[-1].sz = sz; +> return (pp); +> } +> return (NULL); +> } +> +> static +> void default_bzfree ( void* opaque, void* addr ) +> { +> if (addr != NULL) { +> bzap *pp = (bzap *)addr - 1; +> kmem_free(pp->buf, pp->sz); +> } +> } +> +> #else +> +> /*---------------------------------------------------*/ +> static +> void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +> { +112a191 +> #endif /* _KERNEL */ +114d192 +< +212a291,298 +> /*---------------------------------------------------*/ +> /* +> * returns the BZALLOC size needed for bzCompressInit +> */ +> int BZ_API(BZ2_bzCompressInitSize) ( +> int blockSize100k) +> { +> Int32 n, t; +213a300,312 +> n = 100000 * blockSize100k; +> t = 0; +> t += ( sizeof(EState) ); +> t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); +> t += ( n * sizeof(UInt32) ); +> t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); +> t += ( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); +> t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); +> t += ( 65537 * sizeof(UInt32) ); +> t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); +> return (t); +> } +> +214a314,376 +> /* +> * added to allow reuse of bz_stream without malloc/free +> */ +> int BZ_API(BZ2_bzCompressReset) ( bz_stream *strm ) +> { +> EState* s = strm->state; +> +> if (!bz_config_ok()) return BZ_CONFIG_ERROR; +> +> if (s == NULL) return BZ_MEM_ERROR; +> s->strm = strm; +> +> s->blockNo = 0; +> s->state = BZ_S_INPUT; +> s->mode = BZ_M_RUNNING; +> s->combinedCRC = 0; +> s->nblockMAX = 100000 * s->blockSize100k - 19; +> +> s->block = (UChar*)s->arr2; +> s->mtfv = (UInt16*)s->arr1; +> s->zbits = NULL; +> s->ptr = (UInt32*)s->arr1; +> +> strm->state = s; +> strm->total_in_lo32 = 0; +> strm->total_in_hi32 = 0; +> strm->total_out_lo32 = 0; +> strm->total_out_hi32 = 0; +> init_RL ( s ); +> prepare_new_block ( s ); +> return BZ_OK; +> } +> +> int BZ_API(BZ2_bzDecompressReset) ( bz_stream* strm ) +> { +> DState* s = strm->state; +> +> if (!bz_config_ok()) return BZ_CONFIG_ERROR; +> +> if (strm == NULL) return BZ_PARAM_ERROR; +> +> s->strm = strm; +> +> s->state = BZ_X_MAGIC_1; +> s->bsLive = 0; +> s->bsBuff = 0; +> s->calculatedCombinedCRC = 0; +> strm->total_in_lo32 = 0; +> strm->total_in_hi32 = 0; +> strm->total_out_lo32 = 0; +> strm->total_out_hi32 = 0; +> +> s->ll4 = NULL; +> s->ll16 = NULL; +> s->tt = NULL; +> s->currBlockNo = 0; +> +> +> return BZ_OK; +> } +> +> +> /*---------------------------------------------------*/ +854a1017 +> #if 0 +857a1021 +> #endif +1081c1245 +< BZ2_bzCompressEnd ( &(bzf->strm) ); +--- +> (void) BZ2_bzCompressEnd ( &(bzf->strm) ); +1155c1319 +< (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); +--- +> (void) BZ2_bzDecompressEnd ( &(bzf->strm) ); +1285c1449 +< BZ2_bzCompressEnd ( &strm ); +--- +> (void) BZ2_bzCompressEnd ( &strm ); +1289c1453 +< BZ2_bzCompressEnd ( &strm ); +--- +> (void) BZ2_bzCompressEnd ( &strm ); +1293c1457 +< BZ2_bzCompressEnd ( &strm ); +--- +> (void) BZ2_bzCompressEnd ( &strm ); +1333c1497 +< BZ2_bzDecompressEnd ( &strm ); +--- +> (void) BZ2_bzDecompressEnd ( &strm ); +1338c1502 +< BZ2_bzDecompressEnd ( &strm ); +--- +> (void) BZ2_bzDecompressEnd ( &strm ); +1341c1505 +< BZ2_bzDecompressEnd ( &strm ); +--- +> (void) BZ2_bzDecompressEnd ( &strm ); +1343c1507 +< }; +--- +> } +1346c1510 +< BZ2_bzDecompressEnd ( &strm ); +--- +> (void) BZ2_bzDecompressEnd ( &strm ); + +------------------------ bzlib.h ------------------------ +diff bzip2-1.0.5/bzlib.h ./bzlib.h +21d20 +< +24a24,27 +> #ifdef _KERNEL +> #define BZ_NO_STDIO +> #endif +> +99a103,104 +> #define BZ2_BZALLOC_ALIGN (64) +> +106a112,119 +> BZ_EXTERN int BZ_API(BZ2_bzCompressInitSize) ( +> int blockSize100k +> ); +> +> BZ_EXTERN int BZ_API(BZ2_bzCompressReset) ( +> bz_stream* strm +> ); +> +121a135,138 +> BZ_EXTERN int BZ_API(BZ2_bzDecompressReset) ( +> bz_stream* strm +> ); +> +129a147,149 +> BZ_EXTERN const char * BZ_API(BZ2_bzErrorString) ( +> int error_code +> ); +131a152 +> +278,279d298 +< #endif +< +282a302 +> #endif /* _BZLIB_H */ + +------------------------ bzlib_private.h ------------------------ +diff bzip2-1.0.5/bzlib_private.h ./bzlib_private.h +24a25,27 +> #ifdef _KERNEL +> #define BZ_NO_STDIO +> #else +25a29 +> #endif +87a92 +> #pragma weak bz_internal_error +90c95 +< { if (!(cond)) bz_internal_error ( errcode ); } +--- +> { if (!(cond) && &bz_internal_error != NULL) bz_internal_error ( errcode ); } +159c164 +< crcVar = 0xffffffffL; \ +--- +> crcVar = 0xffffffffUL; \ +495,497d499 +< #endif +< +< +509a512 +> #endif /* _BZLIB_PRIVATE_H */ + +------------------------ compress.c ------------------------ +diff bzip2-1.0.5/compress.c ./compress.c + +------------------------ crctable.c ------------------------ +diff bzip2-1.0.5/crctable.c ./crctable.c +35,98c35,98 +< 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, +< 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, +< 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, +< 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, +< 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, +< 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, +< 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, +< 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, +< 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, +< 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, +< 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, +< 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, +< 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, +< 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, +< 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, +< 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, +< 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, +< 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, +< 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, +< 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, +< 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, +< 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, +< 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, +< 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, +< 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, +< 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, +< 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, +< 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, +< 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, +< 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, +< 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, +< 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, +< 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, +< 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, +< 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, +< 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, +< 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, +< 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, +< 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, +< 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, +< 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, +< 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, +< 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, +< 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, +< 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, +< 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, +< 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, +< 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, +< 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, +< 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, +< 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, +< 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, +< 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, +< 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, +< 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, +< 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, +< 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, +< 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, +< 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, +< 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, +< 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, +< 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, +< 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, +< 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +--- +> 0x00000000UL, 0x04c11db7UL, 0x09823b6eUL, 0x0d4326d9UL, +> 0x130476dcUL, 0x17c56b6bUL, 0x1a864db2UL, 0x1e475005UL, +> 0x2608edb8UL, 0x22c9f00fUL, 0x2f8ad6d6UL, 0x2b4bcb61UL, +> 0x350c9b64UL, 0x31cd86d3UL, 0x3c8ea00aUL, 0x384fbdbdUL, +> 0x4c11db70UL, 0x48d0c6c7UL, 0x4593e01eUL, 0x4152fda9UL, +> 0x5f15adacUL, 0x5bd4b01bUL, 0x569796c2UL, 0x52568b75UL, +> 0x6a1936c8UL, 0x6ed82b7fUL, 0x639b0da6UL, 0x675a1011UL, +> 0x791d4014UL, 0x7ddc5da3UL, 0x709f7b7aUL, 0x745e66cdUL, +> 0x9823b6e0UL, 0x9ce2ab57UL, 0x91a18d8eUL, 0x95609039UL, +> 0x8b27c03cUL, 0x8fe6dd8bUL, 0x82a5fb52UL, 0x8664e6e5UL, +> 0xbe2b5b58UL, 0xbaea46efUL, 0xb7a96036UL, 0xb3687d81UL, +> 0xad2f2d84UL, 0xa9ee3033UL, 0xa4ad16eaUL, 0xa06c0b5dUL, +> 0xd4326d90UL, 0xd0f37027UL, 0xddb056feUL, 0xd9714b49UL, +> 0xc7361b4cUL, 0xc3f706fbUL, 0xceb42022UL, 0xca753d95UL, +> 0xf23a8028UL, 0xf6fb9d9fUL, 0xfbb8bb46UL, 0xff79a6f1UL, +> 0xe13ef6f4UL, 0xe5ffeb43UL, 0xe8bccd9aUL, 0xec7dd02dUL, +> 0x34867077UL, 0x30476dc0UL, 0x3d044b19UL, 0x39c556aeUL, +> 0x278206abUL, 0x23431b1cUL, 0x2e003dc5UL, 0x2ac12072UL, +> 0x128e9dcfUL, 0x164f8078UL, 0x1b0ca6a1UL, 0x1fcdbb16UL, +> 0x018aeb13UL, 0x054bf6a4UL, 0x0808d07dUL, 0x0cc9cdcaUL, +> 0x7897ab07UL, 0x7c56b6b0UL, 0x71159069UL, 0x75d48ddeUL, +> 0x6b93dddbUL, 0x6f52c06cUL, 0x6211e6b5UL, 0x66d0fb02UL, +> 0x5e9f46bfUL, 0x5a5e5b08UL, 0x571d7dd1UL, 0x53dc6066UL, +> 0x4d9b3063UL, 0x495a2dd4UL, 0x44190b0dUL, 0x40d816baUL, +> 0xaca5c697UL, 0xa864db20UL, 0xa527fdf9UL, 0xa1e6e04eUL, +> 0xbfa1b04bUL, 0xbb60adfcUL, 0xb6238b25UL, 0xb2e29692UL, +> 0x8aad2b2fUL, 0x8e6c3698UL, 0x832f1041UL, 0x87ee0df6UL, +> 0x99a95df3UL, 0x9d684044UL, 0x902b669dUL, 0x94ea7b2aUL, +> 0xe0b41de7UL, 0xe4750050UL, 0xe9362689UL, 0xedf73b3eUL, +> 0xf3b06b3bUL, 0xf771768cUL, 0xfa325055UL, 0xfef34de2UL, +> 0xc6bcf05fUL, 0xc27dede8UL, 0xcf3ecb31UL, 0xcbffd686UL, +> 0xd5b88683UL, 0xd1799b34UL, 0xdc3abdedUL, 0xd8fba05aUL, +> 0x690ce0eeUL, 0x6dcdfd59UL, 0x608edb80UL, 0x644fc637UL, +> 0x7a089632UL, 0x7ec98b85UL, 0x738aad5cUL, 0x774bb0ebUL, +> 0x4f040d56UL, 0x4bc510e1UL, 0x46863638UL, 0x42472b8fUL, +> 0x5c007b8aUL, 0x58c1663dUL, 0x558240e4UL, 0x51435d53UL, +> 0x251d3b9eUL, 0x21dc2629UL, 0x2c9f00f0UL, 0x285e1d47UL, +> 0x36194d42UL, 0x32d850f5UL, 0x3f9b762cUL, 0x3b5a6b9bUL, +> 0x0315d626UL, 0x07d4cb91UL, 0x0a97ed48UL, 0x0e56f0ffUL, +> 0x1011a0faUL, 0x14d0bd4dUL, 0x19939b94UL, 0x1d528623UL, +> 0xf12f560eUL, 0xf5ee4bb9UL, 0xf8ad6d60UL, 0xfc6c70d7UL, +> 0xe22b20d2UL, 0xe6ea3d65UL, 0xeba91bbcUL, 0xef68060bUL, +> 0xd727bbb6UL, 0xd3e6a601UL, 0xdea580d8UL, 0xda649d6fUL, +> 0xc423cd6aUL, 0xc0e2d0ddUL, 0xcda1f604UL, 0xc960ebb3UL, +> 0xbd3e8d7eUL, 0xb9ff90c9UL, 0xb4bcb610UL, 0xb07daba7UL, +> 0xae3afba2UL, 0xaafbe615UL, 0xa7b8c0ccUL, 0xa379dd7bUL, +> 0x9b3660c6UL, 0x9ff77d71UL, 0x92b45ba8UL, 0x9675461fUL, +> 0x8832161aUL, 0x8cf30badUL, 0x81b02d74UL, 0x857130c3UL, +> 0x5d8a9099UL, 0x594b8d2eUL, 0x5408abf7UL, 0x50c9b640UL, +> 0x4e8ee645UL, 0x4a4ffbf2UL, 0x470cdd2bUL, 0x43cdc09cUL, +> 0x7b827d21UL, 0x7f436096UL, 0x7200464fUL, 0x76c15bf8UL, +> 0x68860bfdUL, 0x6c47164aUL, 0x61043093UL, 0x65c52d24UL, +> 0x119b4be9UL, 0x155a565eUL, 0x18197087UL, 0x1cd86d30UL, +> 0x029f3d35UL, 0x065e2082UL, 0x0b1d065bUL, 0x0fdc1becUL, +> 0x3793a651UL, 0x3352bbe6UL, 0x3e119d3fUL, 0x3ad08088UL, +> 0x2497d08dUL, 0x2056cd3aUL, 0x2d15ebe3UL, 0x29d4f654UL, +> 0xc5a92679UL, 0xc1683bceUL, 0xcc2b1d17UL, 0xc8ea00a0UL, +> 0xd6ad50a5UL, 0xd26c4d12UL, 0xdf2f6bcbUL, 0xdbee767cUL, +> 0xe3a1cbc1UL, 0xe760d676UL, 0xea23f0afUL, 0xeee2ed18UL, +> 0xf0a5bd1dUL, 0xf464a0aaUL, 0xf9278673UL, 0xfde69bc4UL, +> 0x89b8fd09UL, 0x8d79e0beUL, 0x803ac667UL, 0x84fbdbd0UL, +> 0x9abc8bd5UL, 0x9e7d9662UL, 0x933eb0bbUL, 0x97ffad0cUL, +> 0xafb010b1UL, 0xab710d06UL, 0xa6322bdfUL, 0xa2f33668UL, +> 0xbcb4666dUL, 0xb8757bdaUL, 0xb5365d03UL, 0xb1f740b4UL + +------------------------ decompress.c ------------------------ +diff bzip2-1.0.5/decompress.c ./decompress.c +41c41 +< { retVal = rrr; goto save_state_and_return; }; +--- +> { retVal = rrr; goto save_state_and_return; } +494c494 +< RETURN(BZ_DATA_ERROR); +--- +> RETURN(BZ_DATA_ERROR) +558c558 +< RETURN(BZ_OK); +--- +> RETURN(BZ_OK) +586c586 +< RETURN(BZ_STREAM_END); +--- +> RETURN(BZ_STREAM_END) + +------------------------ huffman.c ------------------------ +diff bzip2-1.0.5/huffman.c ./huffman.c + +------------------------ randtable.c ------------------------ +diff bzip2-1.0.5/randtable.c ./randtable.c diff --git a/usr/src/common/bzip2/blocksort.c b/usr/src/common/bzip2/blocksort.c new file mode 100644 index 0000000000..bd2dec157f --- /dev/null +++ b/usr/src/common/bzip2/blocksort.c @@ -0,0 +1,1094 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/common/bzip2/bzlib.c b/usr/src/common/bzip2/bzlib.c new file mode 100644 index 0000000000..3eba10fe69 --- /dev/null +++ b/usr/src/common/bzip2/bzlib.c @@ -0,0 +1,1736 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + fixed bzWrite/bzRead to ignore zero-length requests. + fixed bzread to correctly handle read requests after EOF. + wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to me at: jseward@bzip.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks. Julian Seward, 10 December 2007.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + +/* + * Added for Solaris kernel + */ +#define BZES \ +BZE(BZ_OK) \ +BZE(BZ_RUN_OK) \ +BZE(BZ_FLUSH_OK) \ +BZE(BZ_FINISH_OK) \ +BZE(BZ_STREAM_END) \ +BZE(BZ_SEQUENCE_ERROR) \ +BZE(BZ_PARAM_ERROR) \ +BZE(BZ_MEM_ERROR) \ +BZE(BZ_DATA_ERROR) \ +BZE(BZ_DATA_ERROR_MAGIC) \ +BZE(BZ_IO_ERROR) \ +BZE(BZ_UNEXPECTED_EOF) \ +BZE(BZ_OUTBUFF_FULL) \ +BZE(BZ_CONFIG_ERROR) + +BZ_EXTERN const char * BZ_API(BZ2_bzErrorString) ( + int error_code + ) +{ + switch (error_code) + { +#define BZE(x) case x: return (#x); +BZES +#undef BZE + } + return ("BZ_UNKNOWN_ERROR"); +} + +#include <sys/sysmacros.h> + +#ifdef _KERNEL + +#include <sys/types.h> +#include <sys/cmn_err.h> +#include <sys/kmem.h> + +void +bz_internal_error(int errcode) +{ + panic("bzip2 internal error: %s\n", BZ2_bzErrorString(errcode)); +} + +/*---------------------------------------------------*/ +typedef struct { + char *buf; + size_t sz; +} bzap; + +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + size_t sz = sizeof (bzap) + BZ2_BZALLOC_ALIGN + (items * size); + uintptr_t p = (uintptr_t)kmem_alloc(sz, KM_SLEEP); + + if (p != NULL) { + bzap *pp = (bzap *)((p + sizeof (bzap) + BZ2_BZALLOC_ALIGN - 1) & + -BZ2_BZALLOC_ALIGN); + pp[-1].buf = (void *)p; + pp[-1].sz = sz; + return (pp); + } + return (NULL); +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) { + bzap *pp = (bzap *)addr - 1; + kmem_free(pp->buf, pp->sz); + } +} + +#else + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} +#endif /* _KERNEL */ + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + +/*---------------------------------------------------*/ +/* + * returns the BZALLOC size needed for bzCompressInit + */ +int BZ_API(BZ2_bzCompressInitSize) ( + int blockSize100k) +{ + Int32 n, t; + + n = 100000 * blockSize100k; + t = 0; + t += ( sizeof(EState) ); + t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); + t += ( n * sizeof(UInt32) ); + t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); + t += ( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); + t += ( 65537 * sizeof(UInt32) ); + t = P2ROUNDUP(t, BZ2_BZALLOC_ALIGN); + return (t); +} + +/*---------------------------------------------------*/ +/* + * added to allow reuse of bz_stream without malloc/free + */ +int BZ_API(BZ2_bzCompressReset) ( bz_stream *strm ) +{ + EState* s = strm->state; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->nblockMAX = 100000 * s->blockSize100k - 19; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + +int BZ_API(BZ2_bzDecompressReset) ( bz_stream* strm ) +{ + DState* s = strm->state; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + + s->strm = strm; + + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + Int32 ro_blockSize100k = s->blockSize100k; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + +#if 0 + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +#endif +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + (void) BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void) BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + (void) BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + (void) BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + (void) BZ2_bzCompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + (void) BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + (void) BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + (void) BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + } + + errhandler: + (void) BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include <fcntl.h> +# include <io.h> +# define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((int)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp; + + if (b==NULL) {return;} + fp = ((bzFile *)b)->handle; + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static const char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/common/bzip2/bzlib.h b/usr/src/common/bzip2/bzlib.h new file mode 100644 index 0000000000..e13f0df531 --- /dev/null +++ b/usr/src/common/bzip2/bzlib.h @@ -0,0 +1,302 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef _KERNEL +#define BZ_NO_STDIO +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include <stdio.h> +#endif + +#ifdef _WIN32 +# include <windows.h> +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +#define BZ2_BZALLOC_ALIGN (64) + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressInitSize) ( + int blockSize100k + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressReset) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressReset) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzErrorString) ( + int error_code + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ +#endif /* _BZLIB_H */ diff --git a/usr/src/common/bzip2/bzlib_private.h b/usr/src/common/bzip2/bzlib_private.h new file mode 100644 index 0000000000..8271d19a92 --- /dev/null +++ b/usr/src/common/bzip2/bzlib_private.h @@ -0,0 +1,512 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#ifdef _KERNEL +#define BZ_NO_STDIO +#else +#include <stdlib.h> +#endif + +#ifndef BZ_NO_STDIO +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.5, 10-Dec-2007" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +#pragma weak bz_internal_error +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond) && &bz_internal_error != NULL) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) do { } while (0) +#define VPrintf0(zf) do { } while (0) +#define VPrintf1(zf,za1) do { } while (0) +#define VPrintf2(zf,za1,za2) do { } while (0) +#define VPrintf3(zf,za1,za2,za3) do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffUL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ +#endif /* _BZLIB_PRIVATE_H */ diff --git a/usr/src/common/bzip2/compress.c b/usr/src/common/bzip2/compress.c new file mode 100644 index 0000000000..8c80a07970 --- /dev/null +++ b/usr/src/common/bzip2/compress.c @@ -0,0 +1,672 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/common/bzip2/crctable.c b/usr/src/common/bzip2/crctable.c new file mode 100644 index 0000000000..821f4e4107 --- /dev/null +++ b/usr/src/common/bzip2/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000UL, 0x04c11db7UL, 0x09823b6eUL, 0x0d4326d9UL, + 0x130476dcUL, 0x17c56b6bUL, 0x1a864db2UL, 0x1e475005UL, + 0x2608edb8UL, 0x22c9f00fUL, 0x2f8ad6d6UL, 0x2b4bcb61UL, + 0x350c9b64UL, 0x31cd86d3UL, 0x3c8ea00aUL, 0x384fbdbdUL, + 0x4c11db70UL, 0x48d0c6c7UL, 0x4593e01eUL, 0x4152fda9UL, + 0x5f15adacUL, 0x5bd4b01bUL, 0x569796c2UL, 0x52568b75UL, + 0x6a1936c8UL, 0x6ed82b7fUL, 0x639b0da6UL, 0x675a1011UL, + 0x791d4014UL, 0x7ddc5da3UL, 0x709f7b7aUL, 0x745e66cdUL, + 0x9823b6e0UL, 0x9ce2ab57UL, 0x91a18d8eUL, 0x95609039UL, + 0x8b27c03cUL, 0x8fe6dd8bUL, 0x82a5fb52UL, 0x8664e6e5UL, + 0xbe2b5b58UL, 0xbaea46efUL, 0xb7a96036UL, 0xb3687d81UL, + 0xad2f2d84UL, 0xa9ee3033UL, 0xa4ad16eaUL, 0xa06c0b5dUL, + 0xd4326d90UL, 0xd0f37027UL, 0xddb056feUL, 0xd9714b49UL, + 0xc7361b4cUL, 0xc3f706fbUL, 0xceb42022UL, 0xca753d95UL, + 0xf23a8028UL, 0xf6fb9d9fUL, 0xfbb8bb46UL, 0xff79a6f1UL, + 0xe13ef6f4UL, 0xe5ffeb43UL, 0xe8bccd9aUL, 0xec7dd02dUL, + 0x34867077UL, 0x30476dc0UL, 0x3d044b19UL, 0x39c556aeUL, + 0x278206abUL, 0x23431b1cUL, 0x2e003dc5UL, 0x2ac12072UL, + 0x128e9dcfUL, 0x164f8078UL, 0x1b0ca6a1UL, 0x1fcdbb16UL, + 0x018aeb13UL, 0x054bf6a4UL, 0x0808d07dUL, 0x0cc9cdcaUL, + 0x7897ab07UL, 0x7c56b6b0UL, 0x71159069UL, 0x75d48ddeUL, + 0x6b93dddbUL, 0x6f52c06cUL, 0x6211e6b5UL, 0x66d0fb02UL, + 0x5e9f46bfUL, 0x5a5e5b08UL, 0x571d7dd1UL, 0x53dc6066UL, + 0x4d9b3063UL, 0x495a2dd4UL, 0x44190b0dUL, 0x40d816baUL, + 0xaca5c697UL, 0xa864db20UL, 0xa527fdf9UL, 0xa1e6e04eUL, + 0xbfa1b04bUL, 0xbb60adfcUL, 0xb6238b25UL, 0xb2e29692UL, + 0x8aad2b2fUL, 0x8e6c3698UL, 0x832f1041UL, 0x87ee0df6UL, + 0x99a95df3UL, 0x9d684044UL, 0x902b669dUL, 0x94ea7b2aUL, + 0xe0b41de7UL, 0xe4750050UL, 0xe9362689UL, 0xedf73b3eUL, + 0xf3b06b3bUL, 0xf771768cUL, 0xfa325055UL, 0xfef34de2UL, + 0xc6bcf05fUL, 0xc27dede8UL, 0xcf3ecb31UL, 0xcbffd686UL, + 0xd5b88683UL, 0xd1799b34UL, 0xdc3abdedUL, 0xd8fba05aUL, + 0x690ce0eeUL, 0x6dcdfd59UL, 0x608edb80UL, 0x644fc637UL, + 0x7a089632UL, 0x7ec98b85UL, 0x738aad5cUL, 0x774bb0ebUL, + 0x4f040d56UL, 0x4bc510e1UL, 0x46863638UL, 0x42472b8fUL, + 0x5c007b8aUL, 0x58c1663dUL, 0x558240e4UL, 0x51435d53UL, + 0x251d3b9eUL, 0x21dc2629UL, 0x2c9f00f0UL, 0x285e1d47UL, + 0x36194d42UL, 0x32d850f5UL, 0x3f9b762cUL, 0x3b5a6b9bUL, + 0x0315d626UL, 0x07d4cb91UL, 0x0a97ed48UL, 0x0e56f0ffUL, + 0x1011a0faUL, 0x14d0bd4dUL, 0x19939b94UL, 0x1d528623UL, + 0xf12f560eUL, 0xf5ee4bb9UL, 0xf8ad6d60UL, 0xfc6c70d7UL, + 0xe22b20d2UL, 0xe6ea3d65UL, 0xeba91bbcUL, 0xef68060bUL, + 0xd727bbb6UL, 0xd3e6a601UL, 0xdea580d8UL, 0xda649d6fUL, + 0xc423cd6aUL, 0xc0e2d0ddUL, 0xcda1f604UL, 0xc960ebb3UL, + 0xbd3e8d7eUL, 0xb9ff90c9UL, 0xb4bcb610UL, 0xb07daba7UL, + 0xae3afba2UL, 0xaafbe615UL, 0xa7b8c0ccUL, 0xa379dd7bUL, + 0x9b3660c6UL, 0x9ff77d71UL, 0x92b45ba8UL, 0x9675461fUL, + 0x8832161aUL, 0x8cf30badUL, 0x81b02d74UL, 0x857130c3UL, + 0x5d8a9099UL, 0x594b8d2eUL, 0x5408abf7UL, 0x50c9b640UL, + 0x4e8ee645UL, 0x4a4ffbf2UL, 0x470cdd2bUL, 0x43cdc09cUL, + 0x7b827d21UL, 0x7f436096UL, 0x7200464fUL, 0x76c15bf8UL, + 0x68860bfdUL, 0x6c47164aUL, 0x61043093UL, 0x65c52d24UL, + 0x119b4be9UL, 0x155a565eUL, 0x18197087UL, 0x1cd86d30UL, + 0x029f3d35UL, 0x065e2082UL, 0x0b1d065bUL, 0x0fdc1becUL, + 0x3793a651UL, 0x3352bbe6UL, 0x3e119d3fUL, 0x3ad08088UL, + 0x2497d08dUL, 0x2056cd3aUL, 0x2d15ebe3UL, 0x29d4f654UL, + 0xc5a92679UL, 0xc1683bceUL, 0xcc2b1d17UL, 0xc8ea00a0UL, + 0xd6ad50a5UL, 0xd26c4d12UL, 0xdf2f6bcbUL, 0xdbee767cUL, + 0xe3a1cbc1UL, 0xe760d676UL, 0xea23f0afUL, 0xeee2ed18UL, + 0xf0a5bd1dUL, 0xf464a0aaUL, 0xf9278673UL, 0xfde69bc4UL, + 0x89b8fd09UL, 0x8d79e0beUL, 0x803ac667UL, 0x84fbdbd0UL, + 0x9abc8bd5UL, 0x9e7d9662UL, 0x933eb0bbUL, 0x97ffad0cUL, + 0xafb010b1UL, 0xab710d06UL, 0xa6322bdfUL, 0xa2f33668UL, + 0xbcb4666dUL, 0xb8757bdaUL, 0xb5365d03UL, 0xb1f740b4UL +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/common/bzip2/decompress.c b/usr/src/common/bzip2/decompress.c new file mode 100644 index 0000000000..e5fbe30898 --- /dev/null +++ b/usr/src/common/bzip2/decompress.c @@ -0,0 +1,626 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; } + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + s->selectorMtf[i] = j; + } + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR) + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK) + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END) + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/common/bzip2/huffman.c b/usr/src/common/bzip2/huffman.c new file mode 100644 index 0000000000..87e79e38af --- /dev/null +++ b/usr/src/common/bzip2/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/common/bzip2/randtable.c b/usr/src/common/bzip2/randtable.c new file mode 100644 index 0000000000..068b76367b --- /dev/null +++ b/usr/src/common/bzip2/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 8826e3029c..e744cdef33 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -98,6 +98,13 @@ GENUNIX_OBJS += \ bitmap.o \ blabel.o \ brandsys.o \ + bz2blocksort.o \ + bz2compress.o \ + bz2decompress.o \ + bz2randtable.o \ + bz2bzlib.o \ + bz2crctable.o \ + bz2huffman.o \ callb.o \ callout.o \ chdir.o \ diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index 4003243568..72f3e59d3e 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -1463,6 +1463,15 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/tpm/%.c $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/tpm/%.s $(COMPILE.s) -o $@ $< +$(OBJS_DIR)/bz2%.o: $(COMMONBASE)/bzip2/%.c + $(COMPILE.c) -o $@ -I$(COMMONBASE)/bzip2 $< + $(CTFCONVERT_O) + +BZ2LINT = -erroff=%all -I$(UTSBASE)/common/bzip2 + +$(LINTS_DIR)/bz2%.ln: $(COMMONBASE)/bzip2/%.c + @($(LHEAD) $(LINT.c) -C $(LINTS_DIR)/`basename $@ .ln` $(BZ2LINT) $< $(LTAIL)) + # # SVM # diff --git a/usr/src/uts/common/os/dumpsubr.c b/usr/src/uts/common/os/dumpsubr.c index e29679a985..d7c93ada2e 100644 --- a/usr/src/uts/common/os/dumpsubr.c +++ b/usr/src/uts/common/os/dumpsubr.c @@ -66,87 +66,980 @@ #include <vm/hat.h> #include <vm/as.h> #include <vm/page.h> +#include <vm/pvn.h> #include <vm/seg.h> #include <vm/seg_kmem.h> -kmutex_t dump_lock; /* lock for dump configuration */ -dumphdr_t *dumphdr; /* dump header */ +#include <bzip2/bzlib.h> + +/* + * Crash dump time is dominated by disk write time. To reduce this, + * the stronger compression method bzip2 is applied to reduce the dump + * size and hence reduce I/O time. However, bzip2 is much more + * computationally expensive than the existing lzjb algorithm, so to + * avoid increasing compression time, CPUs that are otherwise idle + * during panic are employed to parallelize the compression task. + * Many helper CPUs are needed to prevent bzip2 from being a + * bottleneck, and on systems with too few CPUs, the lzjb algorithm is + * parallelized instead. Lastly, I/O and compression are performed by + * different CPUs, and are hence overlapped in time, unlike the older + * serial code. + * + * Another important consideration is the speed of the dump + * device. Faster disks need less CPUs in order to benefit from + * parallel lzjb versus parallel bzip2. Therefore, the CPU count + * threshold for switching from parallel lzjb to paralled bzip2 is + * elevated for faster disks. The dump device speed is adduced from + * the setting for dumpbuf.iosize, see dump_update_clevel. + */ + +/* + * exported vars + */ +kmutex_t dump_lock; /* lock for dump configuration */ +dumphdr_t *dumphdr; /* dump header */ int dump_conflags = DUMP_KERNEL; /* dump configuration flags */ -vnode_t *dumpvp; /* dump device vnode pointer */ -u_offset_t dumpvp_size; /* size of dump device, in bytes */ -static u_offset_t dumpvp_limit; /* maximum write offset */ -char *dumppath; /* pathname of dump device */ -int dump_timeout = 120; /* timeout for dumping page during panic */ -int dump_timeleft; /* portion of dump_timeout remaining */ -int dump_ioerr; /* dump i/o error */ +vnode_t *dumpvp; /* dump device vnode pointer */ +u_offset_t dumpvp_size; /* size of dump device, in bytes */ +char *dumppath; /* pathname of dump device */ +int dump_timeout = 120; /* timeout for dumping pages */ +int dump_timeleft; /* portion of dump_timeout remaining */ +int dump_ioerr; /* dump i/o error */ +int dump_check_used; /* enable check for used pages */ + +/* + * Tunables for dump compression and parallelism. These can be set via + * /etc/system. + * + * dump_ncpu_low number of helpers for parallel lzjb + * This is also the minimum configuration. + * + * dump_bzip2_level bzip2 compression level: 1-9 + * Higher numbers give greater compression, but take more memory + * and time. Memory used per helper is ~(dump_bzip2_level * 1MB). + * + * dump_plat_mincpu the cross-over limit for using bzip2 (per platform): + * if dump_plat_mincpu == 0, then always do single threaded dump + * if ncpu >= dump_plat_mincpu then try to use bzip2 + * + * dump_metrics_on if set, metrics are collected in the kernel, passed + * to savecore via the dump file, and recorded by savecore in + * METRICS.txt. + */ +uint_t dump_ncpu_low = 4; /* minimum config for parallel lzjb */ +uint_t dump_bzip2_level = 1; /* bzip2 level (1-9) */ + +/* Define multiple buffers per helper to avoid stalling */ +#define NCBUF_PER_HELPER 2 +#define NCMAP_PER_HELPER 4 + +/* minimum number of helpers configured */ +#define MINHELPERS (dump_ncpu_low) +#define MINCBUFS (MINHELPERS * NCBUF_PER_HELPER) + +/* + * Define constant parameters. + * + * CBUF_SIZE size of an output buffer + * + * CBUF_MAPSIZE size of virtual range for mapping pages + * + * CBUF_MAPNP size of virtual range in pages + * + */ +#define DUMP_1KB ((size_t)1 << 10) +#define DUMP_1MB ((size_t)1 << 20) +#define CBUF_SIZE ((size_t)1 << 17) +#define CBUF_MAPSHIFT (22) +#define CBUF_MAPSIZE ((size_t)1 << CBUF_MAPSHIFT) +#define CBUF_MAPNP ((size_t)1 << (CBUF_MAPSHIFT - PAGESHIFT)) + +/* + * Compression metrics are accumulated nano-second subtotals. The + * results are normalized by the number of pages dumped. A report is + * generated when dumpsys() completes and is saved in the dump image + * after the trailing dump header. + * + * Metrics are always collected. Set the variable dump_metrics_on to + * cause metrics to be saved in the crash file, where savecore will + * save it in the file METRICS.txt. + */ +#define PERPAGES \ + PERPAGE(bitmap) PERPAGE(map) PERPAGE(unmap) \ + PERPAGE(copy) PERPAGE(compress) \ + PERPAGE(write) \ + PERPAGE(inwait) PERPAGE(outwait) + +typedef struct perpage { +#define PERPAGE(x) hrtime_t x; + PERPAGES +#undef PERPAGE +} perpage_t; + +/* + * This macro controls the code generation for collecting dump + * performance information. By default, the code is generated, but + * automatic saving of the information is disabled. If dump_metrics_on + * is set to 1, the timing information is passed to savecore via the + * crash file, where it is appended to the file dump-dir/METRICS.txt. + */ +#define COLLECT_METRICS + +#ifdef COLLECT_METRICS +uint_t dump_metrics_on = 0; /* set to 1 to enable recording metrics */ + +#define HRSTART(v, m) v##ts.m = gethrtime() +#define HRSTOP(v, m) v.m += gethrtime() - v##ts.m +#define HRBEGIN(v, m, s) v##ts.m = gethrtime(); v.size += s +#define HREND(v, m) v.m += gethrtime() - v##ts.m +#define HRNORM(v, m, n) v.m /= (n) -#ifdef DEBUG -int dumpfaildebug = 1; /* enter debugger if dump fails */ #else -int dumpfaildebug = 0; -#endif +#define HRSTART(v, m) +#define HRSTOP(v, m) +#define HRBEGIN(v, m, s) +#define HREND(v, m) +#define HRNORM(v, m, n) +#endif /* COLLECT_METRICS */ -static ulong_t *dump_bitmap; /* bitmap for marking pages to dump */ -static pgcnt_t dump_bitmapsize; /* size of bitmap */ -static pid_t *dump_pids; /* list of process IDs at dump time */ -static offset_t dumpvp_off; /* current dump device offset */ -static char *dump_cmap; /* VA for dump compression mapping */ -static char *dumpbuf_cur, *dumpbuf_start, *dumpbuf_end; -static char *dump_cbuf; /* compression buffer */ -static char *dump_uebuf; /* memory error detection buffer */ -static size_t dumpbuf_size; /* size of dumpbuf in bytes */ -static size_t dumpbuf_limit = 1UL << 23; /* 8MB */ -static size_t dump_iosize; /* device's best transfer size, if any */ -static uint64_t dumpbuf_thresh = 1ULL << 30; /* 1GB */ -static ulong_t dumpbuf_mult = 8; - -/* - * The dump i/o buffer must be at least one page, at most xfer_size bytes, and - * should scale with physmem in between. The transfer size passed in will - * either represent a global default (maxphys) or the best size for the device. - * Once the physical memory size exceeds dumpbuf_thresh (1GB by default), we - * increase the percentage of physical memory that dumpbuf can consume by a - * factor of dumpbuf_mult (8 by default) to improve large memory performance. - * The size of the dumpbuf i/o buffer is limited by dumpbuf_limit (8MB by - * default) because the dump performance saturates beyond a certain size. +/* + * Buffers for copying and compressing memory pages. + * + * cbuf_t buffer controllers: used for both input and output. + * + * The buffer state indicates how it is being used: + * + * CBUF_FREEMAP: CBUF_MAPSIZE virtual address range is available for + * mapping input pages. + * + * CBUF_INREADY: input pages are mapped and ready for compression by a + * helper. + * + * CBUF_USEDMAP: mapping has been consumed by a helper. Needs unmap. + * + * CBUF_FREEBUF: CBUF_SIZE output buffer, which is available. + * + * CBUF_WRITE: CBUF_SIZE block of compressed pages from a helper, + * ready to write out. + * + * CBUF_ERRMSG: CBUF_SIZE block of error messages from a helper + * (reports UE errors.) + */ + +typedef enum cbufstate { + CBUF_FREEMAP, + CBUF_INREADY, + CBUF_USEDMAP, + CBUF_FREEBUF, + CBUF_WRITE, + CBUF_ERRMSG +} cbufstate_t; + +typedef struct cbuf cbuf_t; + +struct cbuf { + cbuf_t *next; /* next in list */ + cbufstate_t state; /* processing state */ + size_t used; /* amount used */ + size_t size; /* mem size */ + char *buf; /* kmem or vmem */ + pgcnt_t pagenum; /* index to pfn map */ + pgcnt_t bitnum; /* first set bitnum */ + pfn_t pfn; /* first pfn in mapped range */ + int off; /* byte offset to first pfn */ +}; + +/* + * cqueue_t queues: a uni-directional channel for communication + * from the master to helper tasks or vice-versa using put and + * get primitives. Both mappings and data buffers are passed via + * queues. Producers close a queue when done. The number of + * active producers is reference counted so the consumer can + * detect end of data. Concurrent access is mediated by atomic + * operations for panic dump, or mutex/cv for live dump. + * + * There a four queues, used as follows: + * + * Queue Dataflow NewState + * -------------------------------------------------- + * mainq master -> master FREEMAP + * master has initialized or unmapped an input buffer + * -------------------------------------------------- + * helperq master -> helper INREADY + * master has mapped input for use by helper + * -------------------------------------------------- + * mainq master <- helper USEDMAP + * helper is done with input + * -------------------------------------------------- + * freebufq master -> helper FREEBUF + * master has initialized or written an output buffer + * -------------------------------------------------- + * mainq master <- helper WRITE + * block of compressed pages from a helper + * -------------------------------------------------- + * mainq master <- helper ERRMSG + * error messages from a helper (memory error case) + * -------------------------------------------------- + * writerq master <- master WRITE + * non-blocking queue of blocks to write + * -------------------------------------------------- + */ +typedef struct cqueue { + cbuf_t *volatile first; /* first in list */ + cbuf_t *last; /* last in list */ + hrtime_t ts; /* timestamp */ + hrtime_t empty; /* total time empty */ + kmutex_t mutex; /* live state lock */ + kcondvar_t cv; /* live wait var */ + lock_t spinlock; /* panic mode spin lock */ + volatile uint_t open; /* producer ref count */ +} cqueue_t; + +/* + * Convenience macros for using the cqueue functions + * Note that the caller must have defined "dumpsync_t *ds" + */ +#define CQ_IS_EMPTY(q) \ + (ds->q.first == NULL) + +#define CQ_OPEN(q) \ + atomic_inc_uint(&ds->q.open) + +#define CQ_CLOSE(q) \ + dumpsys_close_cq(&ds->q, ds->live) + +#define CQ_PUT(q, cp, st) \ + dumpsys_put_cq(&ds->q, cp, st, ds->live) + +#define CQ_GET(q) \ + dumpsys_get_cq(&ds->q, ds->live) + +/* + * Dynamic state when dumpsys() is running. */ +typedef struct dumpsync { + pgcnt_t npages; /* subtotal of pages dumped */ + pgcnt_t pages_mapped; /* subtotal of pages mapped */ + pgcnt_t pages_used; /* subtotal of pages used per map */ + size_t nwrite; /* subtotal of bytes written */ + uint_t live; /* running live dump */ + uint_t neednl; /* will need to print a newline */ + uint_t percent; /* dump progress */ + uint_t percent_done; /* dump progress reported */ + cqueue_t freebufq; /* free kmem bufs for writing */ + cqueue_t mainq; /* input for main task */ + cqueue_t helperq; /* input for helpers */ + cqueue_t writerq; /* input for writer */ + hrtime_t start; /* start time */ + hrtime_t elapsed; /* elapsed time when completed */ + hrtime_t iotime; /* time spent writing nwrite bytes */ + hrtime_t iowait; /* time spent waiting for output */ + hrtime_t iowaitts; /* iowait timestamp */ + perpage_t perpage; /* metrics */ + perpage_t perpagets; + int dumpcpu; /* master cpu */ +} dumpsync_t; + +static dumpsync_t dumpsync; /* synchronization vars */ + +/* + * helper_t helpers: contains the context for a stream. CPUs run in + * parallel at dump time; each CPU creates a single stream of + * compression data. Stream data is divided into CBUF_SIZE blocks. + * The blocks are written in order within a stream. But, blocks from + * multiple streams can be interleaved. Each stream is identified by a + * unique tag. + */ +typedef struct helper { + int helper; /* bound helper id */ + int tag; /* compression stream tag */ + perpage_t perpage; /* per page metrics */ + perpage_t perpagets; /* per page metrics (timestamps) */ + taskqid_t taskqid; /* live dump task ptr */ + int in, out; /* buffer offsets */ + cbuf_t *cpin, *cpout, *cperr; /* cbuf objects in process */ + dumpsync_t *ds; /* pointer to sync vars */ + size_t used; /* counts input consumed */ + char *page; /* buffer for page copy */ + char *lzbuf; /* lzjb output */ + bz_stream bzstream; /* bzip2 state */ +} helper_t; + +#define MAINHELPER (-1) /* helper is also the main task */ +#define FREEHELPER (-2) /* unbound helper */ +#define DONEHELPER (-3) /* helper finished */ + +/* + * configuration vars for dumpsys + */ +typedef struct dumpcfg { + int threshold; /* ncpu threshold for bzip2 */ + int nhelper; /* number of helpers */ + int nhelper_used; /* actual number of helpers used */ + int ncmap; /* number VA pages for compression */ + int ncbuf; /* number of bufs for compression */ + int ncbuf_used; /* number of bufs in use */ + uint_t clevel; /* dump compression level */ + helper_t *helper; /* array of helpers */ + cbuf_t *cmap; /* array of input (map) buffers */ + cbuf_t *cbuf; /* array of output buffers */ + ulong_t *helpermap; /* set of dumpsys helper CPU ids */ + ulong_t *bitmap; /* bitmap for marking pages to dump */ + ulong_t *rbitmap; /* bitmap for used CBUF_MAPSIZE ranges */ + pgcnt_t bitmapsize; /* size of bitmap */ + pgcnt_t rbitmapsize; /* size of bitmap for ranges */ + pgcnt_t found4m; /* number ranges allocated by dump */ + pgcnt_t foundsm; /* number small pages allocated by dump */ + pid_t *pids; /* list of process IDs at dump time */ + size_t maxsize; /* memory size needed at dump time */ + size_t maxvmsize; /* size of reserved VM */ + char *maxvm; /* reserved VM for spare pages */ + lock_t helper_lock; /* protect helper state */ + char helpers_wanted; /* flag to enable parallelism */ +} dumpcfg_t; + +static dumpcfg_t dumpcfg; /* config vars */ + +/* + * The dump I/O buffer. + * + * There is one I/O buffer used by dumpvp_write and dumvp_flush. It is + * sized according to the optimum device transfer speed. + */ +typedef struct dumpbuf { + vnode_t *cdev_vp; /* VCHR open of the dump device */ + len_t vp_limit; /* maximum write offset */ + offset_t vp_off; /* current dump device offset */ + char *cur; /* dump write pointer */ + char *start; /* dump buffer address */ + char *end; /* dump buffer end */ + size_t size; /* size of dumpbuf in bytes */ + size_t iosize; /* best transfer size for device */ +} dumpbuf_t; + +dumpbuf_t dumpbuf; /* I/O buffer */ + +/* + * The dump I/O buffer must be at least one page, at most xfer_size + * bytes, and should scale with physmem in between. The transfer size + * passed in will either represent a global default (maxphys) or the + * best size for the device. The size of the dumpbuf I/O buffer is + * limited by dumpbuf_limit (8MB by default) because the dump + * performance saturates beyond a certain size. The default is to + * select 1/4096 of the memory. + */ +static int dumpbuf_fraction = 12; /* memory size scale factor */ +static size_t dumpbuf_limit = 8 * DUMP_1MB; /* max I/O buf size */ + static size_t dumpbuf_iosize(size_t xfer_size) { - pgcnt_t scale = physmem; - size_t iosize; - - if (scale >= dumpbuf_thresh / PAGESIZE) { - scale *= dumpbuf_mult; /* increase scaling factor */ - iosize = MIN(xfer_size, scale) & PAGEMASK; - if (dumpbuf_limit && iosize > dumpbuf_limit) - iosize = MAX(PAGESIZE, dumpbuf_limit & PAGEMASK); - } else - iosize = MAX(PAGESIZE, MIN(xfer_size, scale) & PAGEMASK); - - return (iosize); + size_t iosize = ptob(physmem >> dumpbuf_fraction); + + if (iosize < PAGESIZE) + iosize = PAGESIZE; + else if (iosize > xfer_size) + iosize = xfer_size; + if (iosize > dumpbuf_limit) + iosize = dumpbuf_limit; + return (iosize & PAGEMASK); } +/* + * resize the I/O buffer + */ static void dumpbuf_resize(void) { - char *old_buf = dumpbuf_start; - size_t old_size = dumpbuf_size; + char *old_buf = dumpbuf.start; + size_t old_size = dumpbuf.size; char *new_buf; size_t new_size; ASSERT(MUTEX_HELD(&dump_lock)); - if ((new_size = dumpbuf_iosize(MAX(dump_iosize, maxphys))) <= old_size) + new_size = dumpbuf_iosize(MAX(dumpbuf.iosize, maxphys)); + if (new_size <= old_size) return; /* no need to reallocate buffer */ new_buf = kmem_alloc(new_size, KM_SLEEP); - dumpbuf_size = new_size; - dumpbuf_start = new_buf; - dumpbuf_end = new_buf + new_size; + dumpbuf.size = new_size; + dumpbuf.start = new_buf; + dumpbuf.end = new_buf + new_size; kmem_free(old_buf, old_size); } +/* + * dump_update_clevel is called when dumpadm configures the dump device. + * Calculate number of helpers and buffers. + * Allocate the minimum configuration for now. + * + * When the dump file is configured we reserve a minimum amount of + * memory for use at crash time. But we reserve VA for all the memory + * we really want in order to do the fastest dump possible. The VA is + * backed by pages not being dumped, according to the bitmap. If + * there is insufficient spare memory, however, we fall back to the + * minimum. + * + * Live dump (savecore -L) always uses the minimum config. + * + * clevel 0 is single threaded lzjb + * clevel 1 is parallel lzjb + * clevel 2 is parallel bzip2 + * + * The ncpu threshold is selected with dump_plat_mincpu. + * On OPL, set_platform_defaults() overrides the sun4u setting. + * The actual values are defined via DUMP_PLAT_*_MINCPU macros. + * + * Architecture Threshold Algorithm + * sun4u < 51 parallel lzjb + * sun4u >= 51 parallel bzip2(*) + * sun4u OPL < 8 parallel lzjb + * sun4u OPL >= 8 parallel bzip2(*) + * sun4v < 128 parallel lzjb + * sun4v >= 128 parallel bzip2(*) + * x86 < 11 parallel lzjb + * x86 >= 11 parallel bzip2(*) + * 32-bit N/A single-threaded lzjb + * + * (*) bzip2 is only chosen if there is sufficient available + * memory for buffers at dump time. See dumpsys_get_maxmem(). + * + * Faster dump devices have larger I/O buffers. The threshold value is + * increased according to the size of the dump I/O buffer, because + * parallel lzjb performs better with faster disks. For buffers >= 1MB + * the threshold is 3X; for buffers >= 256K threshold is 2X. + * + * For parallel dumps, the number of helpers is ncpu-1. The CPU + * running panic runs the main task. For single-threaded dumps, the + * panic CPU does lzjb compression (it is tagged as MAINHELPER.) + * + * Need multiple buffers per helper so that they do not block waiting + * for the main task. + * parallel single-threaded + * Number of output buffers: nhelper*2 1 + * Number of mapping buffers: nhelper*4 1 + * + */ +static void +dump_update_clevel() +{ + int tag; + size_t bz2size; + helper_t *hp, *hpend; + cbuf_t *cp, *cpend; + dumpcfg_t *old = &dumpcfg; + dumpcfg_t newcfg = *old; + dumpcfg_t *new = &newcfg; + + ASSERT(MUTEX_HELD(&dump_lock)); + + /* + * Free the previously allocated bufs and VM. + */ + if (old->helper != NULL) { + + /* helpers */ + hpend = &old->helper[old->nhelper]; + for (hp = old->helper; hp != hpend; hp++) { + if (hp->lzbuf != NULL) + kmem_free(hp->lzbuf, PAGESIZE); + if (hp->page != NULL) + kmem_free(hp->page, PAGESIZE); + } + kmem_free(old->helper, old->nhelper * sizeof (helper_t)); + + /* VM space for mapping pages */ + cpend = &old->cmap[old->ncmap]; + for (cp = old->cmap; cp != cpend; cp++) + vmem_xfree(heap_arena, cp->buf, CBUF_MAPSIZE); + kmem_free(old->cmap, old->ncmap * sizeof (cbuf_t)); + + /* output bufs */ + cpend = &old->cbuf[old->ncbuf]; + for (cp = old->cbuf; cp != cpend; cp++) + if (cp->buf != NULL) + kmem_free(cp->buf, cp->size); + kmem_free(old->cbuf, old->ncbuf * sizeof (cbuf_t)); + + /* reserved VM for dumpsys_get_maxmem */ + if (old->maxvmsize > 0) + vmem_xfree(heap_arena, old->maxvm, old->maxvmsize); + } + + /* + * Allocate memory and VM. + * One CPU runs dumpsys, the rest are helpers. + */ + new->nhelper = ncpus - 1; + if (new->nhelper < 1) + new->nhelper = 1; + + if (new->nhelper > DUMP_MAX_NHELPER) + new->nhelper = DUMP_MAX_NHELPER; + + /* increase threshold for faster disks */ + new->threshold = dump_plat_mincpu; + if (dumpbuf.iosize >= DUMP_1MB) + new->threshold *= 3; + else if (dumpbuf.iosize >= (256 * DUMP_1KB)) + new->threshold *= 2; + + /* figure compression level based upon the computed threshold. */ + if (dump_plat_mincpu == 0 || new->nhelper < 2) { + new->clevel = 0; + new->nhelper = 1; + } else if ((new->nhelper + 1) >= new->threshold) { + new->clevel = DUMP_CLEVEL_BZIP2; + } else { + new->clevel = DUMP_CLEVEL_LZJB; + } + + if (new->clevel == 0) { + new->ncbuf = 1; + new->ncmap = 1; + } else { + new->ncbuf = NCBUF_PER_HELPER * new->nhelper; + new->ncmap = NCMAP_PER_HELPER * new->nhelper; + } + + /* + * Allocate new data structures and buffers for MINHELPERS, + * and also figure the max desired size. + */ + bz2size = BZ2_bzCompressInitSize(dump_bzip2_level); + new->maxsize = 0; + new->maxvmsize = 0; + new->maxvm = NULL; + tag = 1; + new->helper = kmem_zalloc(new->nhelper * sizeof (helper_t), KM_SLEEP); + hpend = &new->helper[new->nhelper]; + for (hp = new->helper; hp != hpend; hp++) { + hp->tag = tag++; + if (hp < &new->helper[MINHELPERS]) { + hp->lzbuf = kmem_alloc(PAGESIZE, KM_SLEEP); + hp->page = kmem_alloc(PAGESIZE, KM_SLEEP); + } else if (new->clevel < DUMP_CLEVEL_BZIP2) { + new->maxsize += 2 * PAGESIZE; + } else { + new->maxsize += PAGESIZE; + } + if (new->clevel >= DUMP_CLEVEL_BZIP2) + new->maxsize += bz2size; + } + + new->cbuf = kmem_zalloc(new->ncbuf * sizeof (cbuf_t), KM_SLEEP); + cpend = &new->cbuf[new->ncbuf]; + for (cp = new->cbuf; cp != cpend; cp++) { + cp->state = CBUF_FREEBUF; + cp->size = CBUF_SIZE; + if (cp < &new->cbuf[MINCBUFS]) + cp->buf = kmem_alloc(cp->size, KM_SLEEP); + else + new->maxsize += cp->size; + } + + new->cmap = kmem_zalloc(new->ncmap * sizeof (cbuf_t), KM_SLEEP); + cpend = &new->cmap[new->ncmap]; + for (cp = new->cmap; cp != cpend; cp++) { + cp->state = CBUF_FREEMAP; + cp->size = CBUF_MAPSIZE; + cp->buf = vmem_xalloc(heap_arena, CBUF_MAPSIZE, CBUF_MAPSIZE, + 0, 0, NULL, NULL, VM_SLEEP); + } + + /* reserve VA to be backed with spare pages at crash time */ + if (new->maxsize > 0) { + new->maxsize = P2ROUNDUP(new->maxsize, PAGESIZE); + new->maxvmsize = P2ROUNDUP(new->maxsize, CBUF_MAPSIZE); + new->maxvm = vmem_xalloc(heap_arena, new->maxvmsize, + CBUF_MAPSIZE, 0, 0, NULL, NULL, VM_SLEEP); + } + + /* set new config pointers */ + *old = *new; +} + +/* + * Define a struct memlist walker to optimize bitnum to pfn + * lookup. The walker maintains the state of the list traversal. + */ +typedef struct dumpmlw { + struct memlist *mp; /* current memlist */ + pgcnt_t basenum; /* bitnum base offset */ + pgcnt_t mppages; /* current memlist size */ + pgcnt_t mpleft; /* size to end of current memlist */ + pfn_t mpaddr; /* first pfn in memlist */ +} dumpmlw_t; + +/* initialize the walker */ +static inline void +dump_init_memlist_walker(dumpmlw_t *pw) +{ + pw->mp = phys_install; + pw->basenum = 0; + pw->mppages = pw->mp->size >> PAGESHIFT; + pw->mpleft = pw->mppages; + pw->mpaddr = pw->mp->address >> PAGESHIFT; +} + +/* + * Lookup pfn given bitnum. The memlist can be quite long on some + * systems (e.g.: one per board). To optimize sequential lookups, the + * caller initializes and presents a memlist walker. + */ +static pfn_t +dump_bitnum_to_pfn(pgcnt_t bitnum, dumpmlw_t *pw) +{ + bitnum -= pw->basenum; + while (pw->mp != NULL) { + if (bitnum < pw->mppages) { + pw->mpleft = pw->mppages - bitnum; + return (pw->mpaddr + bitnum); + } + bitnum -= pw->mppages; + pw->basenum += pw->mppages; + pw->mp = pw->mp->next; + if (pw->mp != NULL) { + pw->mppages = pw->mp->size >> PAGESHIFT; + pw->mpleft = pw->mppages; + pw->mpaddr = pw->mp->address >> PAGESHIFT; + } + } + return (PFN_INVALID); +} + +static pgcnt_t +dump_pfn_to_bitnum(pfn_t pfn) +{ + struct memlist *mp; + pgcnt_t bitnum = 0; + + for (mp = phys_install; mp != NULL; mp = mp->next) { + if (pfn >= (mp->address >> PAGESHIFT) && + pfn < ((mp->address + mp->size) >> PAGESHIFT)) + return (bitnum + pfn - (mp->address >> PAGESHIFT)); + bitnum += mp->size >> PAGESHIFT; + } + return ((pgcnt_t)-1); +} + +/* + * Set/test bitmap for a CBUF_MAPSIZE range which includes pfn. The + * mapping of pfn to range index is imperfect because pfn and bitnum + * do not have the same phase. To make sure a CBUF_MAPSIZE range is + * covered, call this for both ends: + * dump_set_used(base) + * dump_set_used(base+CBUF_MAPNP-1) + * + * This is used during a panic dump to mark pages allocated by + * dumpsys_get_maxmem(). The macro IS_DUMP_PAGE(pp) is used by + * page_get_mnode_freelist() to make sure pages used by dump are never + * allocated. + */ +#define CBUF_MAPP2R(pfn) ((pfn) >> (CBUF_MAPSHIFT - PAGESHIFT)) + +static void +dump_set_used(pfn_t pfn) +{ + + pgcnt_t bitnum, rbitnum; + + bitnum = dump_pfn_to_bitnum(pfn); + ASSERT(bitnum != (pgcnt_t)-1); + + rbitnum = CBUF_MAPP2R(bitnum); + ASSERT(rbitnum < dumpcfg.rbitmapsize); + + BT_SET(dumpcfg.rbitmap, rbitnum); +} + +int +dump_test_used(pfn_t pfn) +{ + pgcnt_t bitnum, rbitnum; + + bitnum = dump_pfn_to_bitnum(pfn); + ASSERT(bitnum != (pgcnt_t)-1); + + rbitnum = CBUF_MAPP2R(bitnum); + ASSERT(rbitnum < dumpcfg.rbitmapsize); + + return (BT_TEST(dumpcfg.rbitmap, rbitnum)); +} + +/* + * dumpbzalloc and dumpbzfree are callbacks from the bzip2 library. + * dumpsys_get_maxmem() uses them for BZ2_bzCompressInit(). + */ +static void * +dumpbzalloc(void *opaque, int items, int size) +{ + size_t *sz; + char *ret; + + ASSERT(opaque != NULL); + sz = opaque; + ret = dumpcfg.maxvm + *sz; + *sz += items * size; + *sz = P2ROUNDUP(*sz, BZ2_BZALLOC_ALIGN); + ASSERT(*sz <= dumpcfg.maxvmsize); + return (ret); +} + +/*ARGSUSED*/ +static void +dumpbzfree(void *opaque, void *addr) +{ +} + +/* + * Perform additional checks on the page to see if we can really use + * it. The kernel (kas) pages are always set in the bitmap. However, + * boot memory pages (prom_ppages or P_BOOTPAGES) are not in the + * bitmap. So we check for them. + */ +static inline int +dump_pfn_check(pfn_t pfn) +{ + page_t *pp = page_numtopp_nolock(pfn); +#if defined(__sparc) + extern struct vnode prom_ppages; +#endif + + if (pp == NULL || pp->p_pagenum != pfn || +#if defined(__sparc) + pp->p_vnode == &prom_ppages || +#else + PP_ISBOOTPAGES(pp) || +#endif + pp->p_toxic != 0) + return (0); + return (1); +} + +/* + * Check a range to see if all contained pages are available and + * return non-zero if the range can be used. + */ +static inline int +dump_range_check(pgcnt_t start, pgcnt_t end, pfn_t pfn) +{ + for (; start < end; start++, pfn++) { + if (BT_TEST(dumpcfg.bitmap, start)) + return (0); + if (!dump_pfn_check(pfn)) + return (0); + } + return (1); +} + +/* + * dumpsys_get_maxmem() is called during panic. Find unused ranges + * and use them for buffers. If we find enough memory switch to + * parallel bzip2, otherwise use parallel lzjb. + * + * It searches the dump bitmap in 2 passes. The first time it looks + * for CBUF_MAPSIZE ranges. On the second pass it uses small pages. + */ +static void +dumpsys_get_maxmem() +{ + dumpcfg_t *cfg = &dumpcfg; + cbuf_t *endcp = &cfg->cbuf[cfg->ncbuf]; + helper_t *endhp = &cfg->helper[cfg->nhelper]; + pgcnt_t bitnum, end; + size_t sz, endsz, bz2size; + pfn_t pfn, off; + cbuf_t *cp; + helper_t *hp, *ohp; + dumpmlw_t mlw; + int k; + + if (cfg->maxsize == 0 || cfg->clevel < DUMP_CLEVEL_LZJB || + (dump_conflags & DUMP_ALL) != 0) + return; + + sz = 0; + cfg->found4m = 0; + cfg->foundsm = 0; + + /* bitmap of ranges used to estimate which pfns are being used */ + bzero(dumpcfg.rbitmap, BT_SIZEOFMAP(dumpcfg.rbitmapsize)); + + /* find ranges that are not being dumped to use for buffers */ + dump_init_memlist_walker(&mlw); + for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum = end) { + dump_timeleft = dump_timeout; + end = bitnum + CBUF_MAPNP; + pfn = dump_bitnum_to_pfn(bitnum, &mlw); + ASSERT(pfn != PFN_INVALID); + + /* skip partial range at end of mem segment */ + if (mlw.mpleft < CBUF_MAPNP) { + end = bitnum + mlw.mpleft; + continue; + } + + /* skip non aligned pages */ + off = P2PHASE(pfn, CBUF_MAPNP); + if (off != 0) { + end -= off; + continue; + } + + if (!dump_range_check(bitnum, end, pfn)) + continue; + + ASSERT((sz + CBUF_MAPSIZE) <= cfg->maxvmsize); + hat_devload(kas.a_hat, cfg->maxvm + sz, CBUF_MAPSIZE, pfn, + PROT_READ | PROT_WRITE, HAT_LOAD_NOCONSIST); + sz += CBUF_MAPSIZE; + cfg->found4m++; + + /* set the bitmap for both ends to be sure to cover the range */ + dump_set_used(pfn); + dump_set_used(pfn + CBUF_MAPNP - 1); + + if (sz >= cfg->maxsize) + goto foundmax; + } + + /* Add small pages if we can't find enough large pages. */ + dump_init_memlist_walker(&mlw); + for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum = end) { + dump_timeleft = dump_timeout; + end = bitnum + CBUF_MAPNP; + pfn = dump_bitnum_to_pfn(bitnum, &mlw); + ASSERT(pfn != PFN_INVALID); + + /* Find any non-aligned pages at start and end of segment. */ + off = P2PHASE(pfn, CBUF_MAPNP); + if (mlw.mpleft < CBUF_MAPNP) { + end = bitnum + mlw.mpleft; + } else if (off != 0) { + end -= off; + } else if (cfg->found4m && dump_test_used(pfn)) { + continue; + } + + for (; bitnum < end; bitnum++, pfn++) { + dump_timeleft = dump_timeout; + if (BT_TEST(dumpcfg.bitmap, bitnum)) + continue; + if (!dump_pfn_check(pfn)) + continue; + ASSERT((sz + PAGESIZE) <= cfg->maxvmsize); + hat_devload(kas.a_hat, cfg->maxvm + sz, PAGESIZE, pfn, + PROT_READ | PROT_WRITE, HAT_LOAD_NOCONSIST); + sz += PAGESIZE; + cfg->foundsm++; + dump_set_used(pfn); + if (sz >= cfg->maxsize) + goto foundmax; + } + } + + /* Fall back to lzjb if we did not get enough memory for bzip2. */ + endsz = (cfg->maxsize * cfg->threshold) / cfg->nhelper; + if (sz < endsz) { + cfg->clevel = DUMP_CLEVEL_LZJB; + } + + /* Allocate memory for as many helpers as we can. */ +foundmax: + + /* Byte offsets into memory found and mapped above */ + endsz = sz; + sz = 0; + + /* Set the size for bzip2 state. Only bzip2 needs it. */ + bz2size = BZ2_bzCompressInitSize(dump_bzip2_level); + + /* Skip the preallocate output buffers. */ + cp = &cfg->cbuf[MINCBUFS]; + + /* Use this to move memory up from the preallocated helpers. */ + ohp = cfg->helper; + + /* Loop over all helpers and allocate memory. */ + for (hp = cfg->helper; hp < endhp; hp++) { + + /* Skip preallocated helpers by checking hp->page. */ + if (hp->page == NULL) { + if (cfg->clevel <= DUMP_CLEVEL_LZJB) { + /* lzjb needs 2 1-page buffers */ + if ((sz + (2 * PAGESIZE)) > endsz) + break; + hp->page = cfg->maxvm + sz; + sz += PAGESIZE; + hp->lzbuf = cfg->maxvm + sz; + sz += PAGESIZE; + + } else if (ohp->lzbuf != NULL) { + /* re-use the preallocted lzjb page for bzip2 */ + hp->page = ohp->lzbuf; + ohp->lzbuf = NULL; + ++ohp; + + } else { + /* bzip2 needs a 1-page buffer */ + if ((sz + PAGESIZE) > endsz) + break; + hp->page = cfg->maxvm + sz; + sz += PAGESIZE; + } + } + + /* + * Add output buffers per helper. The number of + * buffers per helper is determined by the ratio of + * ncbuf to nhelper. + */ + for (k = 0; cp < endcp && (sz + CBUF_SIZE) <= endsz && + k < NCBUF_PER_HELPER; k++) { + cp->state = CBUF_FREEBUF; + cp->size = CBUF_SIZE; + cp->buf = cfg->maxvm + sz; + sz += CBUF_SIZE; + ++cp; + } + + /* + * bzip2 needs compression state. Use the dumpbzalloc + * and dumpbzfree callbacks to allocate the memory. + * bzip2 does allocation only at init time. + */ + if (cfg->clevel >= DUMP_CLEVEL_BZIP2) { + if ((sz + bz2size) > endsz) { + hp->page = NULL; + break; + } else { + hp->bzstream.opaque = &sz; + hp->bzstream.bzalloc = dumpbzalloc; + hp->bzstream.bzfree = dumpbzfree; + (void) BZ2_bzCompressInit(&hp->bzstream, + dump_bzip2_level, 0, 0); + hp->bzstream.opaque = NULL; + } + } + } + + /* Finish allocating output buffers */ + for (; cp < endcp && (sz + CBUF_SIZE) <= endsz; cp++) { + cp->state = CBUF_FREEBUF; + cp->size = CBUF_SIZE; + cp->buf = cfg->maxvm + sz; + sz += CBUF_SIZE; + } + + /* Enable IS_DUMP_PAGE macro, which checks for pages we took. */ + if (cfg->found4m || cfg->foundsm) + dump_check_used = 1; + + ASSERT(sz <= endsz); +} + static void dumphdr_init(void) { @@ -163,22 +1056,31 @@ dumphdr_init(void) dumphdr->dump_pagesize = PAGESIZE; dumphdr->dump_utsname = utsname; (void) strcpy(dumphdr->dump_platform, platform); - dump_cmap = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP); - dumpbuf_size = dumpbuf_iosize(maxphys); - dumpbuf_start = kmem_alloc(dumpbuf_size, KM_SLEEP); - dumpbuf_end = dumpbuf_start + dumpbuf_size; - dump_cbuf = kmem_alloc(PAGESIZE, KM_SLEEP); /* compress buf */ - dump_uebuf = kmem_alloc(PAGESIZE, KM_SLEEP); /* UE buf */ - dump_pids = kmem_alloc(v.v_proc * sizeof (pid_t), KM_SLEEP); + dumpbuf.size = dumpbuf_iosize(maxphys); + dumpbuf.start = kmem_alloc(dumpbuf.size, KM_SLEEP); + dumpbuf.end = dumpbuf.start + dumpbuf.size; + dumpcfg.pids = kmem_alloc(v.v_proc * sizeof (pid_t), KM_SLEEP); + dumpcfg.helpermap = kmem_zalloc(BT_SIZEOFMAP(NCPU), KM_SLEEP); + LOCK_INIT_HELD(&dumpcfg.helper_lock); } npages = num_phys_pages(); - if (dump_bitmapsize != npages) { + if (dumpcfg.bitmapsize != npages) { + size_t rlen = CBUF_MAPP2R(P2ROUNDUP(npages, CBUF_MAPNP)); void *map = kmem_alloc(BT_SIZEOFMAP(npages), KM_SLEEP); - kmem_free(dump_bitmap, BT_SIZEOFMAP(dump_bitmapsize)); - dump_bitmap = map; - dump_bitmapsize = npages; + void *rmap = kmem_alloc(BT_SIZEOFMAP(rlen), KM_SLEEP); + + if (dumpcfg.bitmap != NULL) + kmem_free(dumpcfg.bitmap, BT_SIZEOFMAP(dumpcfg. + bitmapsize)); + if (dumpcfg.rbitmap != NULL) + kmem_free(dumpcfg.rbitmap, BT_SIZEOFMAP(dumpcfg. + rbitmapsize)); + dumpcfg.bitmap = map; + dumpcfg.bitmapsize = npages; + dumpcfg.rbitmap = rmap; + dumpcfg.rbitmapsize = rlen; } } @@ -246,7 +1148,7 @@ dumpinit(vnode_t *vp, char *name, int justchecking) dumpvp_size = vattr.va_size & -DUMP_OFFSET; dumppath = kmem_alloc(strlen(name) + 1, KM_SLEEP); (void) strcpy(dumppath, name); - dump_iosize = 0; + dumpbuf.iosize = 0; /* * If the dump device is a block device, attempt to open up the @@ -270,7 +1172,7 @@ dumpinit(vnode_t *vp, char *name, int justchecking) if (VOP_IOCTL(cdev_vp, DKIOCINFO, (intptr_t)&dki, FKIOCTL, kcred, NULL, NULL) == 0) { - dump_iosize = dki.dki_maxtransfer * blk_size; + dumpbuf.iosize = dki.dki_maxtransfer * blk_size; dumpbuf_resize(); } /* @@ -295,6 +1197,8 @@ dumpinit(vnode_t *vp, char *name, int justchecking) cmn_err(CE_CONT, "?dump on %s size %llu MB\n", name, dumpvp_size >> 20); + dump_update_clevel(); + return (error); } @@ -341,70 +1245,62 @@ dumpfini(void) dumppath = NULL; } -static pfn_t -dump_bitnum_to_pfn(pgcnt_t bitnum) -{ - struct memlist *mp; - - for (mp = phys_install; mp != NULL; mp = mp->next) { - if (bitnum < (mp->size >> PAGESHIFT)) - return ((mp->address >> PAGESHIFT) + bitnum); - bitnum -= mp->size >> PAGESHIFT; - } - return (PFN_INVALID); -} - -static pgcnt_t -dump_pfn_to_bitnum(pfn_t pfn) -{ - struct memlist *mp; - pgcnt_t bitnum = 0; - - for (mp = phys_install; mp != NULL; mp = mp->next) { - if (pfn >= (mp->address >> PAGESHIFT) && - pfn < ((mp->address + mp->size) >> PAGESHIFT)) - return (bitnum + pfn - (mp->address >> PAGESHIFT)); - bitnum += mp->size >> PAGESHIFT; - } - return ((pgcnt_t)-1); -} - static offset_t dumpvp_flush(void) { - size_t size = P2ROUNDUP(dumpbuf_cur - dumpbuf_start, PAGESIZE); + size_t size = P2ROUNDUP(dumpbuf.cur - dumpbuf.start, PAGESIZE); + hrtime_t iotime; int err; - if (dumpvp_off + size > dumpvp_limit) { + if (dumpbuf.vp_off + size > dumpbuf.vp_limit) { dump_ioerr = ENOSPC; + dumpbuf.vp_off = dumpbuf.vp_limit; } else if (size != 0) { + iotime = gethrtime(); + dumpsync.iowait += iotime - dumpsync.iowaitts; if (panicstr) - err = VOP_DUMP(dumpvp, dumpbuf_start, - lbtodb(dumpvp_off), btod(size), NULL); + err = VOP_DUMP(dumpvp, dumpbuf.start, + lbtodb(dumpbuf.vp_off), btod(size), NULL); else - err = vn_rdwr(UIO_WRITE, dumpvp, dumpbuf_start, size, - dumpvp_off, UIO_SYSSPACE, 0, dumpvp_limit, + err = vn_rdwr(UIO_WRITE, dumpbuf.cdev_vp != NULL ? + dumpbuf.cdev_vp : dumpvp, dumpbuf.start, size, + dumpbuf.vp_off, UIO_SYSSPACE, 0, dumpbuf.vp_limit, kcred, 0); if (err && dump_ioerr == 0) dump_ioerr = err; + dumpsync.iowaitts = gethrtime(); + dumpsync.iotime += dumpsync.iowaitts - iotime; + dumpsync.nwrite += size; + dumpbuf.vp_off += size; } - dumpvp_off += size; - dumpbuf_cur = dumpbuf_start; + dumpbuf.cur = dumpbuf.start; dump_timeleft = dump_timeout; - return (dumpvp_off); + return (dumpbuf.vp_off); } +/* maximize write speed by keeping seek offset aligned with size */ void dumpvp_write(const void *va, size_t size) { + size_t len, off, sz; + while (size != 0) { - size_t len = MIN(size, dumpbuf_end - dumpbuf_cur); + len = MIN(size, dumpbuf.end - dumpbuf.cur); if (len == 0) { - (void) dumpvp_flush(); + off = P2PHASE(dumpbuf.vp_off, dumpbuf.size); + if (off == 0 || !ISP2(dumpbuf.size)) { + (void) dumpvp_flush(); + } else { + sz = dumpbuf.size - off; + dumpbuf.cur = dumpbuf.start + sz; + (void) dumpvp_flush(); + ovbcopy(dumpbuf.start + sz, dumpbuf.start, off); + dumpbuf.cur += off; + } } else { - bcopy(va, dumpbuf_cur, len); + bcopy(va, dumpbuf.cur, len); va = (char *)va + len; - dumpbuf_cur += len; + dumpbuf.cur += len; size -= len; } } @@ -427,9 +1323,9 @@ dump_addpage(struct as *as, void *va, pfn_t pfn) pgcnt_t bitnum; if ((bitnum = dump_pfn_to_bitnum(pfn)) != (pgcnt_t)-1) { - if (!BT_TEST(dump_bitmap, bitnum)) { + if (!BT_TEST(dumpcfg.bitmap, bitnum)) { dumphdr->dump_npages++; - BT_SET(dump_bitmap, bitnum); + BT_SET(dumpcfg.bitmap, bitnum); } dumphdr->dump_nvtop++; mem_vtop.m_as = as; @@ -449,9 +1345,9 @@ dump_page(pfn_t pfn) pgcnt_t bitnum; if ((bitnum = dump_pfn_to_bitnum(pfn)) != (pgcnt_t)-1) { - if (!BT_TEST(dump_bitmap, bitnum)) { + if (!BT_TEST(dumpcfg.bitmap, bitnum)) { dumphdr->dump_npages++; - BT_SET(dump_bitmap, bitnum); + BT_SET(dumpcfg.bitmap, bitnum); } } dump_timeleft = dump_timeout; @@ -508,10 +1404,10 @@ dump_ereports(void) if (dumpvp == NULL || dumphdr == NULL) return; - dumpbuf_cur = dumpbuf_start; - dumpvp_limit = dumpvp_size - (DUMP_OFFSET + DUMP_LOGSIZE); - dumpvp_start = dumpvp_limit - DUMP_ERPTSIZE; - dumpvp_off = dumpvp_start; + dumpbuf.cur = dumpbuf.start; + dumpbuf.vp_limit = dumpvp_size - (DUMP_OFFSET + DUMP_LOGSIZE); + dumpvp_start = dumpbuf.vp_limit - DUMP_ERPTSIZE; + dumpbuf.vp_off = dumpvp_start; fm_ereport_dump(); if (panicstr) @@ -523,7 +1419,7 @@ dump_ereports(void) if (!panicstr) { (void) VOP_PUTPAGE(dumpvp, dumpvp_start, - (size_t)(dumpvp_off - dumpvp_start), + (size_t)(dumpbuf.vp_off - dumpvp_start), B_INVAL | B_FORCE, kcred, NULL); } } @@ -539,10 +1435,10 @@ dump_messages(void) if (dumpvp == NULL || dumphdr == NULL || log_consq == NULL) return; - dumpbuf_cur = dumpbuf_start; - dumpvp_limit = dumpvp_size - DUMP_OFFSET; - dumpvp_start = dumpvp_limit - DUMP_LOGSIZE; - dumpvp_off = dumpvp_start; + dumpbuf.cur = dumpbuf.start; + dumpbuf.vp_limit = dumpvp_size - DUMP_OFFSET; + dumpvp_start = dumpbuf.vp_limit - DUMP_LOGSIZE; + dumpbuf.vp_off = dumpvp_start; qlast = NULL; do { @@ -566,12 +1462,19 @@ dump_messages(void) (void) dumpvp_flush(); if (!panicstr) { (void) VOP_PUTPAGE(dumpvp, dumpvp_start, - (size_t)(dumpvp_off - dumpvp_start), + (size_t)(dumpbuf.vp_off - dumpvp_start), B_INVAL | B_FORCE, kcred, NULL); } } -static void +/* + * The following functions are called on multiple CPUs during dump. + * They must not use most kernel services, because all cross-calls are + * disabled during panic. Therefore, blocking locks and cache flushes + * will not work. + */ + +static int dump_pagecopy(void *src, void *dst) { long *wsrc = (long *)src; @@ -582,15 +1485,8 @@ dump_pagecopy(void *src, void *dst) on_trap_data_t otd; if (on_trap(&otd, OT_DATA_EC)) { - if (ueoff == -1) { - uint64_t pa; - + if (ueoff == -1) ueoff = w * sizeof (long); - pa = ptob((uint64_t)hat_getpfnum(kas.a_hat, src)) - + ueoff; - cmn_err(CE_WARN, "memory error at PA 0x%08x.%08x", - (uint32_t)(pa >> 32), (uint32_t)pa); - } #ifdef _LP64 wdst[w++] = 0xbadecc00badecc; #else @@ -602,30 +1498,997 @@ dump_pagecopy(void *src, void *dst) w++; } no_trap(); + return (ueoff); +} + +static void +dumpsys_close_cq(cqueue_t *cq, int live) +{ + if (live) { + mutex_enter(&cq->mutex); + atomic_dec_uint(&cq->open); + cv_signal(&cq->cv); + mutex_exit(&cq->mutex); + } else { + atomic_dec_uint(&cq->open); + } +} + +static inline void +dumpsys_spinlock(lock_t *lp) +{ + uint_t backoff = 0; + int loop_count = 0; + + while (LOCK_HELD(lp) || !lock_spin_try(lp)) { + if (++loop_count >= ncpus) { + backoff = mutex_lock_backoff(0); + loop_count = 0; + } else { + backoff = mutex_lock_backoff(backoff); + } + mutex_lock_delay(backoff); + } +} + +static inline void +dumpsys_spinunlock(lock_t *lp) +{ + lock_clear(lp); +} + +static inline void +dumpsys_lock(cqueue_t *cq, int live) +{ + if (live) + mutex_enter(&cq->mutex); + else + dumpsys_spinlock(&cq->spinlock); +} + +static inline void +dumpsys_unlock(cqueue_t *cq, int live, int signal) +{ + if (live) { + if (signal) + cv_signal(&cq->cv); + mutex_exit(&cq->mutex); + } else { + dumpsys_spinunlock(&cq->spinlock); + } +} + +static void +dumpsys_wait_cq(cqueue_t *cq, int live) +{ + if (live) { + cv_wait(&cq->cv, &cq->mutex); + } else { + dumpsys_spinunlock(&cq->spinlock); + while (cq->open) + if (cq->first) + break; + dumpsys_spinlock(&cq->spinlock); + } +} + +static void +dumpsys_put_cq(cqueue_t *cq, cbuf_t *cp, int newstate, int live) +{ + if (cp == NULL) + return; + + dumpsys_lock(cq, live); + + if (cq->ts != 0) { + cq->empty += gethrtime() - cq->ts; + cq->ts = 0; + } + + cp->state = newstate; + cp->next = NULL; + if (cq->last == NULL) + cq->first = cp; + else + cq->last->next = cp; + cq->last = cp; + + dumpsys_unlock(cq, live, 1); +} + +static cbuf_t * +dumpsys_get_cq(cqueue_t *cq, int live) +{ + cbuf_t *cp; + hrtime_t now = gethrtime(); + + dumpsys_lock(cq, live); + + /* CONSTCOND */ + while (1) { + cp = (cbuf_t *)cq->first; + if (cp == NULL) { + if (cq->open == 0) + break; + dumpsys_wait_cq(cq, live); + continue; + } + cq->first = cp->next; + if (cq->first == NULL) { + cq->last = NULL; + cq->ts = now; + } + break; + } + + dumpsys_unlock(cq, live, cq->first != NULL || cq->open == 0); + return (cp); +} + +/* + * Send an error message to the console. If the main task is running + * just write the message via uprintf. If a helper is running the + * message has to be put on a queue for the main task. Setting fmt to + * NULL means flush the error message buffer. If fmt is not NULL, just + * add the text to the existing buffer. + */ +static void +dumpsys_errmsg(helper_t *hp, const char *fmt, ...) +{ + dumpsync_t *ds = hp->ds; + cbuf_t *cp = hp->cperr; + va_list adx; + + if (hp->helper == MAINHELPER) { + if (fmt != NULL) { + if (ds->neednl) { + uprintf("\n"); + ds->neednl = 0; + } + va_start(adx, fmt); + vuprintf(fmt, adx); + va_end(adx); + } + } else if (fmt == NULL) { + if (cp != NULL) { + CQ_PUT(mainq, cp, CBUF_ERRMSG); + hp->cperr = NULL; + } + } else { + if (hp->cperr == NULL) { + cp = CQ_GET(freebufq); + hp->cperr = cp; + cp->used = 0; + } + va_start(adx, fmt); + cp->used += vsnprintf(cp->buf + cp->used, cp->size - cp->used, + fmt, adx); + va_end(adx); + if ((cp->used + LOG_MSGSIZE) > cp->size) { + CQ_PUT(mainq, cp, CBUF_ERRMSG); + hp->cperr = NULL; + } + } } /* + * Write an output buffer to the dump file. If the main task is + * running just write the data. If a helper is running the output is + * placed on a queue for the main task. + */ +static void +dumpsys_swrite(helper_t *hp, cbuf_t *cp, size_t used) +{ + dumpsync_t *ds = hp->ds; + + if (hp->helper == MAINHELPER) { + HRSTART(ds->perpage, write); + dumpvp_write(cp->buf, used); + HRSTOP(ds->perpage, write); + CQ_PUT(freebufq, cp, CBUF_FREEBUF); + } else { + cp->used = used; + CQ_PUT(mainq, cp, CBUF_WRITE); + } +} + +/* + * Copy one page within the mapped range. The offset starts at 0 and + * is relative to the first pfn. cp->buf + cp->off is the address of + * the first pfn. If dump_pagecopy returns a UE offset, create an + * error message. Returns the offset to the next pfn in the range + * selected by the bitmap. + */ +static int +dumpsys_copy_page(helper_t *hp, int offset) +{ + cbuf_t *cp = hp->cpin; + int ueoff; + + ASSERT(cp->off + offset + PAGESIZE <= cp->size); + ASSERT(BT_TEST(dumpcfg.bitmap, cp->bitnum)); + + ueoff = dump_pagecopy(cp->buf + cp->off + offset, hp->page); + + /* ueoff is the offset in the page to a UE error */ + if (ueoff != -1) { + uint64_t pa = ptob(cp->pfn) + offset + ueoff; + + dumpsys_errmsg(hp, "memory error at PA 0x%08x.%08x\n", + (uint32_t)(pa >> 32), (uint32_t)pa); + } + + /* + * Advance bitnum and offset to the next input page for the + * next call to this function. + */ + offset += PAGESIZE; + cp->bitnum++; + while (cp->off + offset < cp->size) { + if (BT_TEST(dumpcfg.bitmap, cp->bitnum)) + break; + offset += PAGESIZE; + cp->bitnum++; + } + + return (offset); +} + +/* + * Read the helper queue, and copy one mapped page. Return 0 when + * done. Return 1 when a page has been copied into hp->page. + */ +static int +dumpsys_sread(helper_t *hp) +{ + dumpsync_t *ds = hp->ds; + + /* CONSTCOND */ + while (1) { + + /* Find the next input buffer. */ + if (hp->cpin == NULL) { + HRSTART(hp->perpage, inwait); + + /* CONSTCOND */ + while (1) { + hp->cpin = CQ_GET(helperq); + dump_timeleft = dump_timeout; + + /* + * NULL return means the helper queue + * is closed and empty. + */ + if (hp->cpin == NULL) + break; + + /* Have input, check for dump I/O error. */ + if (!dump_ioerr) + break; + + /* + * If an I/O error occurs, stay in the + * loop in order to empty the helper + * queue. Return the buffers to the + * main task to unmap and free it. + */ + hp->cpin->used = 0; + CQ_PUT(mainq, hp->cpin, CBUF_USEDMAP); + } + HRSTOP(hp->perpage, inwait); + + /* Stop here when the helper queue is closed. */ + if (hp->cpin == NULL) + break; + + /* Set the offset=0 to get the first pfn. */ + hp->in = 0; + + /* Set the total processed to 0 */ + hp->used = 0; + } + + /* Process the next page. */ + if (hp->used < hp->cpin->used) { + + /* + * Get the next page from the input buffer and + * return a copy. + */ + ASSERT(hp->in != -1); + HRSTART(hp->perpage, copy); + hp->in = dumpsys_copy_page(hp, hp->in); + hp->used += PAGESIZE; + HRSTOP(hp->perpage, copy); + break; + + } else { + + /* + * Done with the input. Flush the VM and + * return the buffer to the main task. + */ + if (panicstr && hp->helper != MAINHELPER) + hat_flush_range(kas.a_hat, + hp->cpin->buf, hp->cpin->size); + dumpsys_errmsg(hp, NULL); + CQ_PUT(mainq, hp->cpin, CBUF_USEDMAP); + hp->cpin = NULL; + } + } + + return (hp->cpin != NULL); +} + +/* + * Compress size bytes starting at buf with bzip2 + * mode: + * BZ_RUN add one more compressed page + * BZ_FINISH no more input, flush the state + */ +static void +dumpsys_bzrun(helper_t *hp, void *buf, size_t size, int mode) +{ + dumpsync_t *ds = hp->ds; + const int CSIZE = sizeof (dumpcsize_t); + bz_stream *ps = &hp->bzstream; + int rc = 0; + uint32_t csize; + dumpcsize_t cs; + + /* Set input pointers to new input page */ + if (size > 0) { + ps->avail_in = size; + ps->next_in = buf; + } + + /* CONSTCOND */ + while (1) { + + /* Quit when all input has been consumed */ + if (ps->avail_in == 0 && mode == BZ_RUN) + break; + + /* Get a new output buffer */ + if (hp->cpout == NULL) { + HRSTART(hp->perpage, outwait); + hp->cpout = CQ_GET(freebufq); + HRSTOP(hp->perpage, outwait); + ps->avail_out = hp->cpout->size - CSIZE; + ps->next_out = hp->cpout->buf + CSIZE; + } + + /* Compress input, or finalize */ + HRSTART(hp->perpage, compress); + rc = BZ2_bzCompress(ps, mode); + HRSTOP(hp->perpage, compress); + + /* Check for error */ + if (mode == BZ_RUN && rc != BZ_RUN_OK) { + dumpsys_errmsg(hp, "%d: BZ_RUN error %s at page %lx\n", + hp->helper, BZ2_bzErrorString(rc), + hp->cpin->pagenum); + break; + } + + /* Write the buffer if it is full, or we are flushing */ + if (ps->avail_out == 0 || mode == BZ_FINISH) { + csize = hp->cpout->size - CSIZE - ps->avail_out; + cs = DUMP_SET_TAG(csize, hp->tag); + if (csize > 0) { + (void) memcpy(hp->cpout->buf, &cs, CSIZE); + dumpsys_swrite(hp, hp->cpout, csize + CSIZE); + hp->cpout = NULL; + } + } + + /* Check for final complete */ + if (mode == BZ_FINISH) { + if (rc == BZ_STREAM_END) + break; + if (rc != BZ_FINISH_OK) { + dumpsys_errmsg(hp, "%d: BZ_FINISH error %s\n", + hp->helper, BZ2_bzErrorString(rc)); + break; + } + } + } + + /* Cleanup state and buffers */ + if (mode == BZ_FINISH) { + + /* Reset state so that it is re-usable. */ + (void) BZ2_bzCompressReset(&hp->bzstream); + + /* Give any unused outout buffer to the main task */ + if (hp->cpout != NULL) { + hp->cpout->used = 0; + CQ_PUT(mainq, hp->cpout, CBUF_ERRMSG); + hp->cpout = NULL; + } + } +} + +static void +dumpsys_bz2compress(helper_t *hp) +{ + dumpsync_t *ds = hp->ds; + dumpstreamhdr_t sh; + + (void) strcpy(sh.stream_magic, DUMP_STREAM_MAGIC); + sh.stream_pagenum = (pgcnt_t)-1; + sh.stream_npages = 0; + hp->cpin = NULL; + hp->cpout = NULL; + hp->cperr = NULL; + hp->in = 0; + hp->out = 0; + hp->bzstream.avail_in = 0; + + /* Bump reference to mainq while we are running */ + CQ_OPEN(mainq); + + /* Get one page at a time */ + while (dumpsys_sread(hp)) { + if (sh.stream_pagenum != hp->cpin->pagenum) { + sh.stream_pagenum = hp->cpin->pagenum; + sh.stream_npages = btop(hp->cpin->used); + dumpsys_bzrun(hp, &sh, sizeof (sh), BZ_RUN); + } + dumpsys_bzrun(hp, hp->page, PAGESIZE, 0); + } + + /* Done with input, flush any partial buffer */ + if (sh.stream_pagenum != (pgcnt_t)-1) { + dumpsys_bzrun(hp, NULL, 0, BZ_FINISH); + dumpsys_errmsg(hp, NULL); + } + + ASSERT(hp->cpin == NULL && hp->cpout == NULL && hp->cperr == NULL); + + /* Decrement main queue count, we are done */ + CQ_CLOSE(mainq); +} + +/* + * Compress with lzjb + * write stream block if full or size==0 + * if csize==0 write stream header, else write <csize, data> + * size==0 is a call to flush a buffer + * hp->cpout is the buffer we are flushing or filling + * hp->out is the next index to fill data + * osize is either csize+data, or the size of a stream header + */ +static void +dumpsys_lzjbrun(helper_t *hp, size_t csize, void *buf, size_t size) +{ + dumpsync_t *ds = hp->ds; + const int CSIZE = sizeof (dumpcsize_t); + dumpcsize_t cs; + size_t osize = csize > 0 ? CSIZE + size : size; + + /* If flush, and there is no buffer, just return */ + if (size == 0 && hp->cpout == NULL) + return; + + /* If flush, or cpout is full, write it out */ + if (size == 0 || + hp->cpout != NULL && hp->out + osize > hp->cpout->size) { + + /* Set tag+size word at the front of the stream block. */ + cs = DUMP_SET_TAG(hp->out - CSIZE, hp->tag); + (void) memcpy(hp->cpout->buf, &cs, CSIZE); + + /* Write block to dump file. */ + dumpsys_swrite(hp, hp->cpout, hp->out); + + /* Clear pointer to indicate we need a new buffer */ + hp->cpout = NULL; + + /* flushing, we are done */ + if (size == 0) + return; + } + + /* Get an output buffer if we dont have one. */ + if (hp->cpout == NULL) { + HRSTART(hp->perpage, outwait); + hp->cpout = CQ_GET(freebufq); + HRSTOP(hp->perpage, outwait); + hp->out = CSIZE; + } + + /* Store csize word. This is the size of compressed data. */ + if (csize > 0) { + cs = DUMP_SET_TAG(csize, 0); + (void) memcpy(hp->cpout->buf + hp->out, &cs, CSIZE); + hp->out += CSIZE; + } + + /* Store the data. */ + (void) memcpy(hp->cpout->buf + hp->out, buf, size); + hp->out += size; +} + +static void +dumpsys_lzjbcompress(helper_t *hp) +{ + dumpsync_t *ds = hp->ds; + size_t csize; + dumpstreamhdr_t sh; + + (void) strcpy(sh.stream_magic, DUMP_STREAM_MAGIC); + sh.stream_pagenum = (pfn_t)-1; + sh.stream_npages = 0; + hp->cpin = NULL; + hp->cpout = NULL; + hp->cperr = NULL; + hp->in = 0; + hp->out = 0; + + /* Bump reference to mainq while we are running */ + CQ_OPEN(mainq); + + /* Get one page at a time */ + while (dumpsys_sread(hp)) { + + /* Create a stream header for each new input map */ + if (sh.stream_pagenum != hp->cpin->pagenum) { + sh.stream_pagenum = hp->cpin->pagenum; + sh.stream_npages = btop(hp->cpin->used); + dumpsys_lzjbrun(hp, 0, &sh, sizeof (sh)); + } + + /* Compress one page */ + HRSTART(hp->perpage, compress); + csize = compress(hp->page, hp->lzbuf, PAGESIZE); + HRSTOP(hp->perpage, compress); + + /* Add csize+data to output block */ + ASSERT(csize > 0 && csize <= PAGESIZE); + dumpsys_lzjbrun(hp, csize, hp->lzbuf, csize); + } + + /* Done with input, flush any partial buffer */ + if (sh.stream_pagenum != (pfn_t)-1) { + dumpsys_lzjbrun(hp, 0, NULL, 0); + dumpsys_errmsg(hp, NULL); + } + + ASSERT(hp->cpin == NULL && hp->cpout == NULL && hp->cperr == NULL); + + /* Decrement main queue count, we are done */ + CQ_CLOSE(mainq); +} + +/* + * Dump helper called from panic_idle() to compress pages. CPUs in + * this path must not call most kernel services. + * + * During panic, all but one of the CPUs is idle. These CPUs are used + * as helpers working in parallel to copy and compress memory + * pages. During a panic, however, these processors cannot call any + * kernel services. This is because mutexes become no-ops during + * panic, and, cross-call interrupts are inhibited. Therefore, during + * panic dump the helper CPUs communicate with the panic CPU using + * memory variables. All memory mapping and I/O is performed by the + * panic CPU. + */ +void +dumpsys_helper() +{ + dumpsys_spinlock(&dumpcfg.helper_lock); + if (dumpcfg.helpers_wanted) { + helper_t *hp, *hpend = &dumpcfg.helper[dumpcfg.nhelper]; + + for (hp = dumpcfg.helper; hp != hpend; hp++) { + if (hp->helper == FREEHELPER) { + hp->helper = CPU->cpu_id; + BT_SET(dumpcfg.helpermap, CPU->cpu_seqid); + + dumpsys_spinunlock(&dumpcfg.helper_lock); + + if (dumpcfg.clevel < DUMP_CLEVEL_BZIP2) + dumpsys_lzjbcompress(hp); + else + dumpsys_bz2compress(hp); + + hp->helper = DONEHELPER; + return; + } + } + } + dumpsys_spinunlock(&dumpcfg.helper_lock); +} + +/* + * Dump helper for live dumps. + * These run as a system task. + */ +static void +dumpsys_live_helper(void *arg) +{ + helper_t *hp = arg; + + BT_ATOMIC_SET(dumpcfg.helpermap, CPU->cpu_seqid); + if (dumpcfg.clevel < DUMP_CLEVEL_BZIP2) + dumpsys_lzjbcompress(hp); + else + dumpsys_bz2compress(hp); +} + +/* + * Compress one page with lzjb (single threaded case) + */ +static void +dumpsys_lzjb_page(helper_t *hp, cbuf_t *cp) +{ + dumpsync_t *ds = hp->ds; + uint32_t csize; + + hp->helper = MAINHELPER; + hp->in = 0; + hp->used = 0; + hp->cpin = cp; + while (hp->used < cp->used) { + HRSTART(hp->perpage, copy); + hp->in = dumpsys_copy_page(hp, hp->in); + hp->used += PAGESIZE; + HRSTOP(hp->perpage, copy); + + HRSTART(hp->perpage, compress); + csize = compress(hp->page, hp->lzbuf, PAGESIZE); + HRSTOP(hp->perpage, compress); + + HRSTART(hp->perpage, write); + dumpvp_write(&csize, sizeof (csize)); + dumpvp_write(hp->lzbuf, csize); + HRSTOP(hp->perpage, write); + } + CQ_PUT(mainq, hp->cpin, CBUF_USEDMAP); + hp->cpin = NULL; +} + +/* + * Main task to dump pages. This is called on the dump CPU. + */ +static void +dumpsys_main_task(void *arg) +{ + dumpsync_t *ds = arg; + pgcnt_t pagenum = 0, bitnum = 0, hibitnum; + dumpmlw_t mlw; + cbuf_t *cp; + pgcnt_t baseoff, pfnoff; + pfn_t base, pfn; + int sec; + + dump_init_memlist_walker(&mlw); + + /* CONSTCOND */ + while (1) { + + if (ds->percent > ds->percent_done) { + ds->percent_done = ds->percent; + sec = (gethrtime() - ds->start) / 1000 / 1000 / 1000; + uprintf("^\r%2d:%02d %3d%% done", + sec / 60, sec % 60, ds->percent); + ds->neednl = 1; + } + + while (CQ_IS_EMPTY(mainq) && !CQ_IS_EMPTY(writerq)) { + + /* the writerq never blocks */ + cp = CQ_GET(writerq); + if (cp == NULL) + break; + + dump_timeleft = dump_timeout; + + HRSTART(ds->perpage, write); + dumpvp_write(cp->buf, cp->used); + HRSTOP(ds->perpage, write); + + CQ_PUT(freebufq, cp, CBUF_FREEBUF); + } + + /* + * Wait here for some buffers to process. Returns NULL + * when all helpers have terminated and all buffers + * have been processed. + */ + cp = CQ_GET(mainq); + + if (cp == NULL) { + + /* Drain the write queue. */ + if (!CQ_IS_EMPTY(writerq)) + continue; + + /* Main task exits here. */ + break; + } + + dump_timeleft = dump_timeout; + + switch (cp->state) { + + case CBUF_FREEMAP: + + /* + * Note that we drop CBUF_FREEMAP buffers on + * the floor (they will not be on any cqueue) + * when we no longer need them. + */ + if (bitnum >= dumpcfg.bitmapsize) + break; + + if (dump_ioerr) { + bitnum = dumpcfg.bitmapsize; + CQ_CLOSE(helperq); + break; + } + + HRSTART(ds->perpage, bitmap); + for (; bitnum < dumpcfg.bitmapsize; bitnum++) + if (BT_TEST(dumpcfg.bitmap, bitnum)) + break; + HRSTOP(ds->perpage, bitmap); + dump_timeleft = dump_timeout; + + if (bitnum >= dumpcfg.bitmapsize) { + CQ_CLOSE(helperq); + break; + } + + /* + * Try to map CBUF_MAPSIZE ranges. Can't + * assume that memory segment size is a + * multiple of CBUF_MAPSIZE. Can't assume that + * the segment starts on a CBUF_MAPSIZE + * boundary. + */ + pfn = dump_bitnum_to_pfn(bitnum, &mlw); + ASSERT(pfn != PFN_INVALID); + ASSERT(bitnum + mlw.mpleft <= dumpcfg.bitmapsize); + + base = P2ALIGN(pfn, CBUF_MAPNP); + if (base < mlw.mpaddr) { + base = mlw.mpaddr; + baseoff = P2PHASE(base, CBUF_MAPNP); + } else { + baseoff = 0; + } + + pfnoff = pfn - base; + if (pfnoff + mlw.mpleft < CBUF_MAPNP) { + hibitnum = bitnum + mlw.mpleft; + cp->size = ptob(pfnoff + mlw.mpleft); + } else { + hibitnum = bitnum - pfnoff + CBUF_MAPNP - + baseoff; + cp->size = CBUF_MAPSIZE - ptob(baseoff); + } + + cp->pfn = pfn; + cp->bitnum = bitnum++; + cp->pagenum = pagenum++; + cp->off = ptob(pfnoff); + + for (; bitnum < hibitnum; bitnum++) + if (BT_TEST(dumpcfg.bitmap, bitnum)) + pagenum++; + + dump_timeleft = dump_timeout; + cp->used = ptob(pagenum - cp->pagenum); + + HRSTART(ds->perpage, map); + hat_devload(kas.a_hat, cp->buf, cp->size, base, + PROT_READ, HAT_LOAD_NOCONSIST); + HRSTOP(ds->perpage, map); + + ds->pages_mapped += btop(cp->size); + ds->pages_used += pagenum - cp->pagenum; + + CQ_OPEN(mainq); + + /* + * If there are no helpers the main task does + * non-streams lzjb compress. + */ + if (dumpcfg.clevel == 0) { + dumpsys_lzjb_page(dumpcfg.helper, cp); + break; + } + + /* pass mapped pages to a helper */ + CQ_PUT(helperq, cp, CBUF_INREADY); + + /* the last page was done */ + if (bitnum >= dumpcfg.bitmapsize) + CQ_CLOSE(helperq); + + break; + + case CBUF_USEDMAP: + + ds->npages += btop(cp->used); + + HRSTART(ds->perpage, unmap); + hat_unload(kas.a_hat, cp->buf, cp->size, HAT_UNLOAD); + HRSTOP(ds->perpage, unmap); + + if (bitnum < dumpcfg.bitmapsize) + CQ_PUT(mainq, cp, CBUF_FREEMAP); + CQ_CLOSE(mainq); + + ASSERT(ds->npages <= dumphdr->dump_npages); + ds->percent = ds->npages * 100LL / dumphdr->dump_npages; + break; + + case CBUF_WRITE: + + CQ_PUT(writerq, cp, CBUF_WRITE); + break; + + case CBUF_ERRMSG: + + if (cp->used > 0) { + cp->buf[cp->size - 2] = '\n'; + cp->buf[cp->size - 1] = '\0'; + if (ds->neednl) { + uprintf("\n%s", cp->buf); + ds->neednl = 0; + } else { + uprintf("%s", cp->buf); + } + } + CQ_PUT(freebufq, cp, CBUF_FREEBUF); + break; + + default: + uprintf("dump: unexpected buffer state %d, " + "buffer will be lost\n", cp->state); + break; + + } /* end switch */ + + } /* end while(1) */ +} + +#ifdef COLLECT_METRICS +size_t +dumpsys_metrics(dumpsync_t *ds, char *buf, size_t size) +{ + dumpcfg_t *cfg = &dumpcfg; + int myid = CPU->cpu_seqid; + int i, compress_ratio; + int sec, iorate; + helper_t *hp, *hpend = &cfg->helper[cfg->nhelper]; + char *e = buf + size; + char *p = buf; + + sec = ds->elapsed / (1000 * 1000 * 1000ULL); + if (sec < 1) + sec = 1; + + if (ds->iotime < 1) + ds->iotime = 1; + iorate = (ds->nwrite * 100000ULL) / ds->iotime; + + compress_ratio = 100LL * ds->npages / btopr(ds->nwrite + 1); + +#define P(...) (p += p < e ? snprintf(p, e - p, __VA_ARGS__) : 0) + + P("Master cpu_seqid,%d\n", CPU->cpu_seqid); + P("Master cpu_id,%d\n", CPU->cpu_id); + P("dump_flags,0x%x\n", dumphdr->dump_flags); + P("dump_ioerr,%d\n", dump_ioerr); + + P("Helpers:\n"); + for (i = 0; i < ncpus; i++) { + if ((i & 15) == 0) + P(",,%03d,", i); + if (i == myid) + P(" M"); + else if (BT_TEST(cfg->helpermap, i)) + P("%4d", cpu_seq[i]->cpu_id); + else + P(" *"); + if ((i & 15) == 15) + P("\n"); + } + + P("ncbuf_used,%d\n", cfg->ncbuf_used); + P("ncmap,%d\n", cfg->ncmap); + + P("Found %ldM ranges,%ld\n", (CBUF_MAPSIZE / DUMP_1MB), cfg->found4m); + P("Found small pages,%ld\n", cfg->foundsm); + + P("Compression level,%d\n", cfg->clevel); + P("Compression type,%s %s\n", cfg->clevel == 0 ? "serial" : "parallel", + cfg->clevel >= DUMP_CLEVEL_BZIP2 ? "bzip2" : "lzjb"); + P("Compression ratio,%d.%02d\n", compress_ratio / 100, compress_ratio % + 100); + P("nhelper_used,%d\n", cfg->nhelper_used); + + P("Dump I/O rate MBS,%d.%02d\n", iorate / 100, iorate % 100); + P("..total bytes,%lld\n", (u_longlong_t)ds->nwrite); + P("..total nsec,%lld\n", (u_longlong_t)ds->iotime); + P("dumpbuf.iosize,%ld\n", dumpbuf.iosize); + P("dumpbuf.size,%ld\n", dumpbuf.size); + + P("Dump pages/sec,%llu\n", (u_longlong_t)ds->npages / sec); + P("Dump pages,%llu\n", (u_longlong_t)ds->npages); + P("Dump time,%d\n", sec); + + if (ds->pages_mapped > 0) + P("per-cent map utilization,%d\n", (int)((100 * ds->pages_used) + / ds->pages_mapped)); + + P("\nPer-page metrics:\n"); + if (ds->npages > 0) { + for (hp = cfg->helper; hp != hpend; hp++) { +#define PERPAGE(x) ds->perpage.x += hp->perpage.x; + PERPAGES; +#undef PERPAGE + } +#define PERPAGE(x) \ + P("%s nsec/page,%d\n", #x, (int)(ds->perpage.x / ds->npages)); + PERPAGES; +#undef PERPAGE + P("freebufq.empty,%d\n", (int)(ds->freebufq.empty / + ds->npages)); + P("helperq.empty,%d\n", (int)(ds->helperq.empty / + ds->npages)); + P("writerq.empty,%d\n", (int)(ds->writerq.empty / + ds->npages)); + P("mainq.empty,%d\n", (int)(ds->mainq.empty / ds->npages)); + + P("I/O wait nsec/page,%llu\n", (u_longlong_t)(ds->iowait / + ds->npages)); + } +#undef P + if (p < e) + bzero(p, e - p); + return (p - buf); +} +#endif /* COLLECT_METRICS */ + +/* * Dump the system. */ void dumpsys(void) { + dumpsync_t *ds = &dumpsync; + taskq_t *livetaskq = NULL; pfn_t pfn; pgcnt_t bitnum; - int npages = 0; - int percent_done = 0; - uint32_t csize; - u_offset_t total_csize = 0; - int compress_ratio; proc_t *p; + helper_t *hp, *hpend = &dumpcfg.helper[dumpcfg.nhelper]; + cbuf_t *cp; pid_t npids, pidx; char *content; + int save_dump_clevel; + dumpmlw_t mlw; + dumpcsize_t datatag; + dumpdatahdr_t datahdr; if (dumpvp == NULL || dumphdr == NULL) { uprintf("skipping system dump - no dump device configured\n"); + if (panicstr) { + dumpcfg.helpers_wanted = 0; + dumpsys_spinunlock(&dumpcfg.helper_lock); + } return; } - dumpbuf_cur = dumpbuf_start; + dumpbuf.cur = dumpbuf.start; + + /* clear the sync variables */ + ASSERT(dumpcfg.nhelper > 0); + bzero(ds, sizeof (*ds)); + ds->dumpcpu = CPU->cpu_id; /* * Calculate the starting block for dump. If we're dumping on a @@ -637,11 +2500,11 @@ dumpsys(void) else dumphdr->dump_start = DUMP_OFFSET; - dumphdr->dump_flags = DF_VALID | DF_COMPLETE | DF_LIVE; + dumphdr->dump_flags = DF_VALID | DF_COMPLETE | DF_LIVE | DF_COMPRESSED; dumphdr->dump_crashtime = gethrestime_sec(); dumphdr->dump_npages = 0; dumphdr->dump_nvtop = 0; - bzero(dump_bitmap, BT_SIZEOFMAP(dump_bitmapsize)); + bzero(dumpcfg.bitmap, BT_SIZEOFMAP(dumpcfg.bitmapsize)); dump_timeleft = dump_timeout; if (panicstr) { @@ -650,6 +2513,7 @@ dumpsys(void) (void) VOP_DUMPCTL(dumpvp, DUMP_ALLOC, NULL, NULL); (void) vsnprintf(dumphdr->dump_panicstring, DUMP_PANICSIZE, panicstr, panicargs); + } if (dump_conflags & DUMP_ALL) @@ -661,17 +2525,45 @@ dumpsys(void) uprintf("dumping to %s, offset %lld, content: %s\n", dumppath, dumphdr->dump_start, content); + /* Make sure nodename is current */ + bcopy(utsname.nodename, dumphdr->dump_utsname.nodename, SYS_NMLN); + + /* + * If this is a live dump, try to open a VCHR vnode for better + * performance. We must take care to flush the buffer cache + * first. + */ + if (!panicstr) { + vnode_t *cdev_vp, *cmn_cdev_vp; + + ASSERT(dumpbuf.cdev_vp == NULL); + cdev_vp = makespecvp(VTOS(dumpvp)->s_dev, VCHR); + if (cdev_vp != NULL) { + cmn_cdev_vp = common_specvp(cdev_vp); + if (VOP_OPEN(&cmn_cdev_vp, FREAD | FWRITE, kcred, NULL) + == 0) { + if (vn_has_cached_data(dumpvp)) + (void) pvn_vplist_dirty(dumpvp, 0, NULL, + B_INVAL | B_TRUNC, kcred); + dumpbuf.cdev_vp = cmn_cdev_vp; + } else { + VN_RELE(cdev_vp); + } + } + } + /* * Leave room for the message and ereport save areas and terminal dump * header. */ - dumpvp_limit = dumpvp_size - DUMP_LOGSIZE - DUMP_OFFSET - DUMP_ERPTSIZE; + dumpbuf.vp_limit = dumpvp_size - DUMP_LOGSIZE - DUMP_OFFSET - + DUMP_ERPTSIZE; /* * Write out the symbol table. It's no longer compressed, * so its 'size' and 'csize' are equal. */ - dumpvp_off = dumphdr->dump_ksyms = dumphdr->dump_start + PAGESIZE; + dumpbuf.vp_off = dumphdr->dump_ksyms = dumphdr->dump_start + PAGESIZE; dumphdr->dump_ksyms_size = dumphdr->dump_ksyms_csize = ksyms_snapshot(dumpvp_ksyms_write, NULL, LONG_MAX); @@ -692,18 +2584,18 @@ dumpsys(void) mutex_enter(&pidlock); for (npids = 0, p = practive; p != NULL; p = p->p_next) - dump_pids[npids++] = p->p_pid; + dumpcfg.pids[npids++] = p->p_pid; mutex_exit(&pidlock); for (pidx = 0; pidx < npids; pidx++) - (void) dump_process(dump_pids[pidx]); + (void) dump_process(dumpcfg.pids[pidx]); - for (bitnum = 0; bitnum < dump_bitmapsize; bitnum++) { + for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum++) { dump_timeleft = dump_timeout; - BT_SET(dump_bitmap, bitnum); + BT_SET(dumpcfg.bitmap, bitnum); } - dumphdr->dump_npages = dump_bitmapsize; + dumphdr->dump_npages = dumpcfg.bitmapsize; dumphdr->dump_flags |= DF_ALL; } else if (dump_conflags & DUMP_CURPROC) { @@ -718,14 +2610,14 @@ dumpsys(void) if (panic_thread != NULL && panic_thread->t_procp != NULL && panic_thread->t_procp != &p0) { - dump_pids[npids++] = + dumpcfg.pids[npids++] = panic_thread->t_procp->p_pid; } } else { - dump_pids[npids++] = curthread->t_procp->p_pid; + dumpcfg.pids[npids++] = curthread->t_procp->p_pid; } - if (npids && dump_process(dump_pids[0]) == 0) + if (npids && dump_process(dumpcfg.pids[0]) == 0) dumphdr->dump_flags |= DF_CURPROC; else dumphdr->dump_flags |= DF_KERNEL; @@ -740,11 +2632,12 @@ dumpsys(void) * Write out the pfn table. */ dumphdr->dump_pfn = dumpvp_flush(); - for (bitnum = 0; bitnum < dump_bitmapsize; bitnum++) { + dump_init_memlist_walker(&mlw); + for (bitnum = 0; bitnum < dumpcfg.bitmapsize; bitnum++) { dump_timeleft = dump_timeout; - if (!BT_TEST(dump_bitmap, bitnum)) + if (!BT_TEST(dumpcfg.bitmap, bitnum)) continue; - pfn = dump_bitnum_to_pfn(bitnum); + pfn = dump_bitnum_to_pfn(bitnum, &mlw); ASSERT(pfn != PFN_INVALID); dumpvp_write(&pfn, sizeof (pfn_t)); } @@ -752,67 +2645,144 @@ dumpsys(void) /* * Write out all the pages. + * Map pages, copy them handling UEs, compress, and write them out. + * Cooperate with any helpers running on CPUs in panic_idle(). */ dumphdr->dump_data = dumpvp_flush(); - for (bitnum = 0; bitnum < dump_bitmapsize; bitnum++) { - dump_timeleft = dump_timeout; - if (!BT_TEST(dump_bitmap, bitnum)) + + bzero(dumpcfg.helpermap, BT_SIZEOFMAP(NCPU)); + ds->live = dumpcfg.clevel > 0 && + (dumphdr->dump_flags & DF_LIVE) != 0; + + save_dump_clevel = dumpcfg.clevel; + if (panicstr) + dumpsys_get_maxmem(); + else if (dumpcfg.clevel >= DUMP_CLEVEL_BZIP2) + dumpcfg.clevel = DUMP_CLEVEL_LZJB; + + dumpcfg.nhelper_used = 0; + for (hp = dumpcfg.helper; hp != hpend; hp++) { + if (hp->page == NULL) { + hp->helper = DONEHELPER; continue; - pfn = dump_bitnum_to_pfn(bitnum); - ASSERT(pfn != PFN_INVALID); + } + ++dumpcfg.nhelper_used; + hp->helper = FREEHELPER; + hp->taskqid = NULL; + hp->ds = ds; + bzero(&hp->perpage, sizeof (hp->perpage)); + if (dumpcfg.clevel >= DUMP_CLEVEL_BZIP2) + (void) BZ2_bzCompressReset(&hp->bzstream); + } - /* - * Map in page frame 'pfn', scan it for UE's while copying - * the data to dump_uebuf, unmap it, compress dump_uebuf into - * dump_cbuf, and write out dump_cbuf. The UE check ensures - * that we don't lose the whole dump because of a latent UE. - */ - hat_devload(kas.a_hat, dump_cmap, PAGESIZE, pfn, PROT_READ, - HAT_LOAD_NOCONSIST); - dump_pagecopy(dump_cmap, dump_uebuf); - hat_unload(kas.a_hat, dump_cmap, PAGESIZE, HAT_UNLOAD); - csize = (uint32_t)compress(dump_uebuf, dump_cbuf, PAGESIZE); - dumpvp_write(&csize, sizeof (uint32_t)); - dumpvp_write(dump_cbuf, csize); - if (dump_ioerr) { - dumphdr->dump_flags &= ~DF_COMPLETE; - dumphdr->dump_npages = npages; - break; + CQ_OPEN(freebufq); + CQ_OPEN(helperq); + + dumpcfg.ncbuf_used = 0; + for (cp = dumpcfg.cbuf; cp != &dumpcfg.cbuf[dumpcfg.ncbuf]; cp++) { + if (cp->buf != NULL) { + CQ_PUT(freebufq, cp, CBUF_FREEBUF); + ++dumpcfg.ncbuf_used; } - total_csize += csize; - if (++npages * 100LL / dumphdr->dump_npages > percent_done) { - uprintf("^\r%3d%% done", ++percent_done); - if (!panicstr) - delay(1); /* let the output be sent */ + } + + for (cp = dumpcfg.cmap; cp != &dumpcfg.cmap[dumpcfg.ncmap]; cp++) + CQ_PUT(mainq, cp, CBUF_FREEMAP); + + ds->start = gethrtime(); + ds->iowaitts = ds->start; + + /* start helpers */ + if (ds->live) { + int n = dumpcfg.nhelper_used; + int pri = MINCLSYSPRI - 25; + + livetaskq = taskq_create("LiveDump", n, pri, n, n, + TASKQ_PREPOPULATE); + for (hp = dumpcfg.helper; hp != hpend; hp++) { + if (hp->page == NULL) + continue; + hp->helper = hp - dumpcfg.helper; + hp->taskqid = taskq_dispatch(livetaskq, + dumpsys_live_helper, (void *)hp, TQ_NOSLEEP); } + + } else { + dumpcfg.helpers_wanted = dumpcfg.clevel > 0; + dumpsys_spinunlock(&dumpcfg.helper_lock); } - dumphdr->dump_npages += dump_plat_data(dump_cbuf); - (void) dumpvp_flush(); + /* run main task */ + dumpsys_main_task(ds); + + ds->elapsed = gethrtime() - ds->start; + if (ds->elapsed < 1) + ds->elapsed = 1; + + if (livetaskq != NULL) + taskq_destroy(livetaskq); + + if (ds->neednl) { + uprintf("\n"); + ds->neednl = 0; + } + + /* record actual pages dumped */ + dumphdr->dump_npages = ds->npages; + + /* platform-specific data */ + dumphdr->dump_npages += dump_plat_data(dumpcfg.cbuf[0].buf); + + /* note any errors by clearing DF_COMPLETE */ + if (dump_ioerr || ds->npages < dumphdr->dump_npages) + dumphdr->dump_flags &= ~DF_COMPLETE; + + /* end of stream blocks */ + datatag = 0; + dumpvp_write(&datatag, sizeof (datatag)); + + /* compression info in data header */ + bzero(&datahdr, sizeof (datahdr)); + datahdr.dump_datahdr_magic = DUMP_DATAHDR_MAGIC; + datahdr.dump_datahdr_version = DUMP_DATAHDR_VERSION; + datahdr.dump_maxcsize = CBUF_SIZE; + datahdr.dump_maxrange = CBUF_MAPSIZE / PAGESIZE; + datahdr.dump_nstreams = dumpcfg.nhelper_used; + datahdr.dump_clevel = dumpcfg.clevel; +#ifdef COLLECT_METRICS + if (dump_metrics_on) + datahdr.dump_metrics = dumpsys_metrics(ds, dumpcfg.cbuf[0].buf, + MIN(dumpcfg.cbuf[0].size, DUMP_OFFSET - sizeof (dumphdr_t) - + sizeof (dumpdatahdr_t))); +#endif + datahdr.dump_data_csize = dumpvp_flush() - dumphdr->dump_data; /* * Write out the initial and terminal dump headers. */ - dumpvp_off = dumphdr->dump_start; + dumpbuf.vp_off = dumphdr->dump_start; dumpvp_write(dumphdr, sizeof (dumphdr_t)); (void) dumpvp_flush(); - dumpvp_limit = dumpvp_size; - dumpvp_off = dumpvp_limit - DUMP_OFFSET; + dumpbuf.vp_limit = dumpvp_size; + dumpbuf.vp_off = dumpbuf.vp_limit - DUMP_OFFSET; dumpvp_write(dumphdr, sizeof (dumphdr_t)); - (void) dumpvp_flush(); + dumpvp_write(&datahdr, sizeof (dumpdatahdr_t)); + dumpvp_write(dumpcfg.cbuf[0].buf, datahdr.dump_metrics); - compress_ratio = (int)(100LL * npages / (btopr(total_csize + 1))); + (void) dumpvp_flush(); - uprintf("\r%3d%% done: %d pages dumped, compression ratio %d.%02d, ", - percent_done, npages, compress_ratio / 100, compress_ratio % 100); + uprintf("\r%3d%% done: %llu pages dumped, ", + ds->percent_done, (u_longlong_t)ds->npages); if (dump_ioerr == 0) { uprintf("dump succeeded\n"); } else { uprintf("dump failed: error %d\n", dump_ioerr); - if (panicstr && dumpfaildebug) +#ifdef DEBUG + if (panicstr) debug_enter("dump failed"); +#endif } /* @@ -827,6 +2797,19 @@ dumpsys(void) delay(2 * hz); /* let people see the 'done' message */ dump_timeleft = 0; dump_ioerr = 0; + + /* restore settings after live dump completes */ + if (!panicstr) { + dumpcfg.clevel = save_dump_clevel; + + /* release any VCHR open of the dump device */ + if (dumpbuf.cdev_vp != NULL) { + (void) VOP_CLOSE(dumpbuf.cdev_vp, FREAD | FWRITE, 1, 0, + kcred, NULL); + VN_RELE(dumpbuf.cdev_vp); + dumpbuf.cdev_vp = NULL; + } + } } /* @@ -839,6 +2822,7 @@ dump_resize() mutex_enter(&dump_lock); dumphdr_init(); dumpbuf_resize(); + dump_update_clevel(); mutex_exit(&dump_lock); } diff --git a/usr/src/uts/common/sys/dumphdr.h b/usr/src/uts/common/sys/dumphdr.h index 72c6e41c71..1dd57a0cec 100644 --- a/usr/src/uts/common/sys/dumphdr.h +++ b/usr/src/uts/common/sys/dumphdr.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_DUMPHDR_H #define _SYS_DUMPHDR_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/param.h> #include <sys/utsname.h> @@ -87,6 +85,7 @@ typedef struct dumphdr { #define DF_VALID 0x00000001 /* Dump is valid (savecore clears) */ #define DF_COMPLETE 0x00000002 /* All pages present as configured */ #define DF_LIVE 0x00000004 /* Dump was taken on a live system */ +#define DF_COMPRESSED 0x00000008 /* Dump is compressed */ #define DF_KERNEL 0x00010000 /* Contains kernel pages only */ #define DF_ALL 0x00020000 /* Contains all pages */ #define DF_CURPROC 0x00040000 /* Contains kernel + cur proc pages */ @@ -110,6 +109,58 @@ typedef struct dump_map { ((((uintptr_t)(as) >> 3) + ((va) >> (dhp)->dump_pageshift)) & \ (dhp)->dump_hashmask) +/* + * Encoding of the csize word used to provide meta information + * between dumpsys and savecore. + * + * tag size + * 1-4095 1..dump_maxcsize stream block + * 0 1..pagesize one lzjb page + * 0 0 marks end of data + */ +typedef uint32_t dumpcsize_t; + +#define DUMP_MAX_TAG (0xfffU) +#define DUMP_MAX_CSIZE (0xfffffU) +#define DUMP_SET_TAG(w, v) (((w) & DUMP_MAX_CSIZE) | ((v) << 20)) +#define DUMP_GET_TAG(w) (((w) >> 20) & DUMP_MAX_TAG) +#define DUMP_SET_CSIZE(w, v) \ + (((w) & (DUMP_MAX_TAG << 20)) | ((v) & DUMP_MAX_CSIZE)) +#define DUMP_GET_CSIZE(w) ((w) & DUMP_MAX_CSIZE) + +typedef struct dumpstreamhdr { + char stream_magic[8]; /* "StrmHdr" */ + pgcnt_t stream_pagenum; /* starting pfn */ + pgcnt_t stream_npages; /* uncompressed size */ +} dumpstreamhdr_t; + +#define DUMP_STREAM_MAGIC "StrmHdr" + +/* The number of helpers is limited by the number of stream tags. */ +#define DUMP_MAX_NHELPER DUMP_MAX_TAG + +/* + * The dump data header is placed after the dumphdr in the compressed + * image. It is not needed after savecore runs and the data pages have + * been decompressed. + */ +typedef struct dumpdatahdr { + uint32_t dump_datahdr_magic; /* data header presence */ + uint32_t dump_datahdr_version; /* data header version */ + uint64_t dump_data_csize; /* compressed data size */ + uint32_t dump_maxcsize; /* compressed data max block size */ + uint32_t dump_maxrange; /* max number of pages per range */ + uint16_t dump_nstreams; /* number of compression streams */ + uint16_t dump_clevel; /* compression level (0-9) */ + uint32_t dump_metrics; /* size of metrics data */ +} dumpdatahdr_t; + +#define DUMP_DATAHDR_MAGIC ('d' << 24 | 'h' << 16 | 'd' << 8 | 'r') + +#define DUMP_DATAHDR_VERSION 1 +#define DUMP_CLEVEL_LZJB 1 /* parallel lzjb compression */ +#define DUMP_CLEVEL_BZIP2 2 /* parallel bzip2 level 1 */ + #ifdef _KERNEL extern kmutex_t dump_lock; @@ -131,6 +182,7 @@ extern void dump_resize(void); extern void dump_page(pfn_t); extern void dump_addpage(struct as *, void *, pfn_t); extern void dumpsys(void); +extern void dumpsys_helper(void); extern void dump_messages(void); extern void dump_ereports(void); extern void dumpvp_write(const void *, size_t); @@ -139,6 +191,29 @@ extern int dump_plat_addr(void); extern void dump_plat_pfn(void); extern int dump_plat_data(void *); +/* + * Define a CPU count threshold that determines when to employ + * bzip2. The values are defined per-platform in dump_plat_mincpu, and + * may be changed with /etc/system. The value 0 disables parallelism, + * and the old format dump is produced. + */ +extern uint_t dump_plat_mincpu; + +#define DUMP_PLAT_SUN4U_MINCPU 51 +#define DUMP_PLAT_SUN4U_OPL_MINCPU 8 +#define DUMP_PLAT_SUN4V_MINCPU 128 +#define DUMP_PLAT_X86_64_MINCPU 11 +#define DUMP_PLAT_X86_32_MINCPU 0 + +/* + * Pages may be stolen at dump time. Prevent the pages from ever being + * allocated while dump is running. + */ +#define IS_DUMP_PAGE(pp) (dump_check_used && dump_test_used((pp)->p_pagenum)) + +extern int dump_test_used(pfn_t); +extern int dump_check_used; + #endif /* _KERNEL */ #ifdef __cplusplus diff --git a/usr/src/uts/common/vm/hat.h b/usr/src/uts/common/vm/hat.h index b966acf7fc..bed40f63d9 100644 --- a/usr/src/uts/common/vm/hat.h +++ b/usr/src/uts/common/vm/hat.h @@ -200,6 +200,9 @@ void hat_thread_exit(kthread_t *); * given to the specified virtual protection. If vprot is ~PROT_WRITE, * then remove write permission, leaving the other permissions * unchanged. If vprot is ~PROT_USER, remove user permissions. + * + * void hat_flush_range(hat, addr, size) + * Invalidate a virtual address translation for the local CPU. */ void hat_memload(struct hat *, caddr_t, struct page *, uint_t, uint_t); @@ -218,6 +221,7 @@ void hat_unlock_region(struct hat *, caddr_t, size_t, hat_region_cookie_t); void hat_unload(struct hat *, caddr_t, size_t, uint_t); void hat_unload_callback(struct hat *, caddr_t, size_t, uint_t, hat_callback_t *); +void hat_flush_range(struct hat *, caddr_t, size_t); void hat_sync(struct hat *, caddr_t, size_t, uint_t); void hat_map(struct hat *, caddr_t, size_t, uint_t); void hat_setattr(struct hat *, caddr_t, size_t, uint_t); diff --git a/usr/src/uts/common/vm/vm_page.c b/usr/src/uts/common/vm/vm_page.c index a6af733be8..43f153f19f 100644 --- a/usr/src/uts/common/vm/vm_page.c +++ b/usr/src/uts/common/vm/vm_page.c @@ -4267,6 +4267,8 @@ retry: return (pp); } +#define SYNC_PROGRESS_NPAGES 1000 + /* * Returns a count of dirty pages that are in the process * of being written out. If 'cleanit' is set, try to push the page. @@ -4277,12 +4279,22 @@ page_busy(int cleanit) page_t *page0 = page_first(); page_t *pp = page0; pgcnt_t nppbusy = 0; + int counter = 0; u_offset_t off; do { vnode_t *vp = pp->p_vnode; /* + * Reset the sync timeout. The page list is very long + * on large memory systems. + */ + if (++counter > SYNC_PROGRESS_NPAGES) { + counter = 0; + vfs_syncprogress(); + } + + /* * A page is a candidate for syncing if it is: * * (a) On neither the freelist nor the cachelist @@ -4299,7 +4311,6 @@ page_busy(int cleanit) hat_ismod(pp) && !IS_SWAPVP(vp) && vp->v_vfsp != NULL && vfs_can_sync(vp->v_vfsp)) { nppbusy++; - vfs_syncprogress(); if (!cleanit) continue; @@ -4322,6 +4333,7 @@ page_busy(int cleanit) } } while ((pp = page_next(pp)) != page0); + vfs_syncprogress(); return (nppbusy); } diff --git a/usr/src/uts/common/vm/vm_pagelist.c b/usr/src/uts/common/vm/vm_pagelist.c index d31ca1bda9..59b4e079c5 100644 --- a/usr/src/uts/common/vm/vm_pagelist.c +++ b/usr/src/uts/common/vm/vm_pagelist.c @@ -59,6 +59,7 @@ #include <sys/callb.h> #include <sys/mem_cage.h> #include <sys/sdt.h> +#include <sys/dumphdr.h> extern uint_t vac_colors; @@ -2951,7 +2952,8 @@ try_again: * page of each large page. */ first_pp = pp; - while (!page_trylock_cons(pp, SE_EXCL)) { + while (!page_trylock_cons(pp, SE_EXCL) || + IS_DUMP_PAGE(pp)) { if (szc == 0) { pp = pp->p_next; } else { diff --git a/usr/src/uts/i86pc/os/machdep.c b/usr/src/uts/i86pc/os/machdep.c index 5c68c7ce58..25413d5831 100644 --- a/usr/src/uts/i86pc/os/machdep.c +++ b/usr/src/uts/i86pc/os/machdep.c @@ -871,6 +871,8 @@ panic_idle(void) splx(ipltospl(CLOCK_LEVEL)); (void) setjmp(&curthread->t_pcb); + dumpsys_helper(); + #ifndef __xpv for (;;) i86_halt(); @@ -1194,6 +1196,13 @@ num_phys_pages() return (npages); } +/* cpu threshold for compressed dumps */ +#ifdef _LP64 +uint_t dump_plat_mincpu = DUMP_PLAT_X86_64_MINCPU; +#else +uint_t dump_plat_mincpu = DUMP_PLAT_X86_32_MINCPU; +#endif + int dump_plat_addr() { diff --git a/usr/src/uts/i86pc/vm/hat_i86.c b/usr/src/uts/i86pc/vm/hat_i86.c index 58793e22d2..79afd56ac8 100644 --- a/usr/src/uts/i86pc/vm/hat_i86.c +++ b/usr/src/uts/i86pc/vm/hat_i86.c @@ -2436,6 +2436,35 @@ hat_unload_callback( } /* + * Invalidate a virtual address translation for the local CPU. + * For best performance ensure that the va range is completely + * mapped, otherwise the entire TLB will be flushed. + */ +void +hat_flush_range(hat_t *hat, caddr_t va, size_t size) +{ + ssize_t sz; + caddr_t endva = va + size; + + while (va < endva) { + sz = hat_getpagesize(hat, va); + if (sz < 0) + va = (caddr_t)DEMAP_ALL_ADDR; +#ifdef __xpv + if (va == (caddr_t)DEMAP_ALL_ADDR) + xen_flush_tlb(); + else + xen_flush_va(va); +#else + (void) hati_demap_func((xc_arg_t)hat, (xc_arg_t)va, NULL); +#endif + if (sz < 0) + break; + va += sz; + } +} + +/* * synchronize mapping with software data structures * * This interface is currently only used by the working set monitor diff --git a/usr/src/uts/sfmmu/vm/hat_sfmmu.c b/usr/src/uts/sfmmu/vm/hat_sfmmu.c index 8713fef22a..d1c618d37a 100644 --- a/usr/src/uts/sfmmu/vm/hat_sfmmu.c +++ b/usr/src/uts/sfmmu/vm/hat_sfmmu.c @@ -6168,6 +6168,28 @@ tte_unloaded: } /* + * Invalidate a virtual address range for the local CPU. + * For best performance ensure that the va range is completely + * mapped, otherwise the entire TLB will be flushed. + */ +void +hat_flush_range(struct hat *sfmmup, caddr_t va, size_t size) +{ + ssize_t sz; + caddr_t endva = va + size; + + while (va < endva) { + sz = hat_getpagesize(sfmmup, va); + if (sz < 0) { + vtag_flushall(); + break; + } + vtag_flushpage(va, (uint64_t)sfmmup); + va += sz; + } +} + +/* * Synchronize all the mappings in the range [addr..addr+len). * Can be called with clearflag having two states: * HAT_SYNC_DONTZERO means just return the rm stats diff --git a/usr/src/uts/sun4/os/machdep.c b/usr/src/uts/sun4/os/machdep.c index d90db8d04f..d4f2669abd 100644 --- a/usr/src/uts/sun4/os/machdep.c +++ b/usr/src/uts/sun4/os/machdep.c @@ -807,6 +807,13 @@ plat_mem_do_mmio(struct uio *uio, enum uio_rw rw) return (ENOTSUP); } +/* cpu threshold for compressed dumps */ +#ifdef sun4v +uint_t dump_plat_mincpu = DUMP_PLAT_SUN4V_MINCPU; +#else +uint_t dump_plat_mincpu = DUMP_PLAT_SUN4U_MINCPU; +#endif + int dump_plat_addr() { diff --git a/usr/src/uts/sun4u/opl/os/opl.c b/usr/src/uts/sun4u/opl/os/opl.c index b933c48f4c..a8880ac679 100644 --- a/usr/src/uts/sun4u/opl/os/opl.c +++ b/usr/src/uts/sun4u/opl/os/opl.c @@ -46,6 +46,7 @@ #include <sys/sysmacros.h> #include <sys/time.h> #include <sys/cpu.h> +#include <sys/dumphdr.h> #include <vm/vm_dep.h> int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); @@ -221,6 +222,9 @@ set_platform_defaults(void) tsb_lgrp_affinity = 1; set_max_mmu_ctxdoms(); + + /* set OPL threshold for compressed dumps */ + dump_plat_mincpu = DUMP_PLAT_SUN4U_OPL_MINCPU; } /* diff --git a/usr/src/uts/sun4u/os/mach_cpu_states.c b/usr/src/uts/sun4u/os/mach_cpu_states.c index 9e14ff1930..f154bea238 100644 --- a/usr/src/uts/sun4u/os/mach_cpu_states.c +++ b/usr/src/uts/sun4u/os/mach_cpu_states.c @@ -19,12 +19,10 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/t_lock.h> #include <sys/uadmin.h> @@ -41,6 +39,7 @@ #include <sys/consdev.h> #include <sys/kdi_impl.h> #include <sys/callb.h> +#include <sys/dumphdr.h> #ifdef TRAPTRACE #include <sys/traptrace.h> @@ -200,6 +199,8 @@ panic_idle(void) CPU->cpu_m.in_prom = 1; membar_stld(); + dumpsys_helper(); + for (;;) continue; } diff --git a/usr/src/uts/sun4v/os/mach_cpu_states.c b/usr/src/uts/sun4v/os/mach_cpu_states.c index 737bb0e1ac..d582873776 100644 --- a/usr/src/uts/sun4v/os/mach_cpu_states.c +++ b/usr/src/uts/sun4v/os/mach_cpu_states.c @@ -54,6 +54,7 @@ #include <sys/hsvc.h> #include <sys/ldoms.h> #include <sys/kldc.h> +#include <sys/dumphdr.h> /* * hvdump_buf_va is a pointer to the currently-configured hvdump_buf. @@ -319,6 +320,8 @@ panic_idle(void) CPU->cpu_m.in_prom = 1; membar_stld(); + dumpsys_helper(); + for (;;) ; } |