svn commit: r295943 - in head: share/man/man4 sys/conf sys/geom/uzip sys/modules/geom/geom_uzip usr.bin/mkuzip

Maxim Sobolev sobomax at FreeBSD.org
Tue Feb 23 23:59:10 UTC 2016


Author: sobomax
Date: Tue Feb 23 23:59:08 2016
New Revision: 295943
URL: https://svnweb.freebsd.org/changeset/base/295943

Log:
  Improve mkuzip(8) and geom_uzip(4), merge in LZMA support from mkulzma(8)
  and geom_uncompress(4):
  
  1. mkuzip(8):
  
   - Proper support for eliminating all-zero blocks when compressing an
     image. This feature is already supported by the geom_uzip(4) module
     and CLOOP format in general, so it's just a matter of making mkuzip(8)
     match. It should be noted, however that this feature while it sounds
     great, results in very slight improvement in the overall compression
     ratio, since compressing default 16k all-zero block produces only 39
     bytes compressed output block, which is 99.8% compression ratio. With
     typical average compression ratio of amd64 binaries and data being
     around 60-70% the difference between 99.8% and 100.0% is not that
     great further diluted by the ratio of number of zero blocks in the
     uncompressed image to the overall number of blocks being less than
     0.5 (typically). However, this may be important from performance
     standpoint, so that kernel are not spinning its wheels decompressing
     those empty blocks every time this zero region is read. It could also
     be important when you create huge image mostly filled with zero
     blocks for testing purposes.
  
   - New feature allowing to de-duplicate output image. It turns out that
     if you twist CLOOP format a bit you can do that as well. And unlike
     zero-blocks elimination, this gives a noticeable improvement in the
     overall compression ratio, reducing output image by something like
     3-4% on my test UFS2 3GB image consisting of full FreeBSD base system
     plus some of the packages (openjdk, apache etc), about 2.3GB worth of
     file data (800+MB compressed). The only caveat is that images created
     with this feature "on" would not work on older versions of FeeBSDxi
     kernel, hence it's turned off by default.
  
   - provide options to control both features and document them in manual
     page.
  
   - merge in all relevant LZMA compression support from the mkulzma(8),
     add new option to select between both.
  
   - switch license from ad-hoc beerware into standard 2-clause BSD.
  
  2. geom_uzip(4):
  
   - implement support for de-duplicated images;
  
   - optimize some code paths to handle "all-zero" blocks without reading
     any compressed data;
  
   - beef up manual page to explain that geom_uzip(4) is not limited only
     to md(4) images. The compressed data can be written to the block
     device and accessed directly via magic of GEOM(4) and devfs(4),
     including to mount root fs from a compressed drive.
  
   - convert debug log code from being compiled in conditionally into
     being present all the time and provide two sysctls to turn it on or
     off. Due to intended use of the module, it can be used in
     environments where there may not be a luxury to put new kernel with
     debug code enabled. Having those options handy allows debug issues
     without as much problem by just having access to serial console or
     network shell access to a box/appliance. The resulting additional
     CPU cycles are just few int comparisons and branches, and those are
     minuscule when compared to data decompression which is the main
     feature of the module.
  
   - hopefully improve robustness and resiliency of the geom_uzip(4) by
     performing some of the data validation / range checking on the TOC
     entries and rejecting to attach to an image if those checks fail.
  
   - merge in all relevant LZMA decompression support from the
     geom_uncompress(4), enable automatically when appropriate format is
     indicated in the header.
  
   - move compilation work into its own worker thread so that it does not
     clog g_up. This allows multiple instances work in parallel utilizing
     smp cores.
  
   - document new knobs in the manual page.
  
  Reviewed by:		adrian
  MFC after:		1 month
  Differential Revision:	https://reviews.freebsd.org/D5333

Added:
  head/sys/geom/uzip/g_uzip.h   (contents, props changed)
  head/sys/geom/uzip/g_uzip_cloop.h   (contents, props changed)
  head/sys/geom/uzip/g_uzip_dapi.h   (contents, props changed)
  head/sys/geom/uzip/g_uzip_lzma.c   (contents, props changed)
  head/sys/geom/uzip/g_uzip_lzma.h   (contents, props changed)
  head/sys/geom/uzip/g_uzip_softc.h   (contents, props changed)
  head/sys/geom/uzip/g_uzip_wrkthr.c   (contents, props changed)
  head/sys/geom/uzip/g_uzip_wrkthr.h   (contents, props changed)
  head/sys/geom/uzip/g_uzip_zlib.c   (contents, props changed)
  head/sys/geom/uzip/g_uzip_zlib.h   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_blockcache.c   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_blockcache.h   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_cloop.h   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_lzma.c   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_lzma.h   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_zlib.c   (contents, props changed)
  head/usr.bin/mkuzip/mkuz_zlib.h   (contents, props changed)
  head/usr.bin/mkuzip/mkuzip.h   (contents, props changed)
Modified:
  head/share/man/man4/geom_uzip.4
  head/sys/conf/files
  head/sys/geom/uzip/g_uzip.c
  head/sys/modules/geom/geom_uzip/Makefile
  head/usr.bin/mkuzip/Makefile
  head/usr.bin/mkuzip/mkuzip.8
  head/usr.bin/mkuzip/mkuzip.c

Modified: head/share/man/man4/geom_uzip.4
==============================================================================
--- head/share/man/man4/geom_uzip.4	Tue Feb 23 23:57:24 2016	(r295942)
+++ head/share/man/man4/geom_uzip.4	Tue Feb 23 23:59:08 2016	(r295943)
@@ -30,7 +30,7 @@
 .Os
 .Sh NAME
 .Nm geom_uzip
-.Nd "GEOM based compressed disk images"
+.Nd "GEOM based compressed disk images and partitions"
 .Sh SYNOPSIS
 To compile this driver into the kernel,
 place the following line in your
@@ -51,7 +51,7 @@ The
 framework provides support for compressed read-only
 disk images.
 This allows significant storage savings at the expense of
-a little CPU time on each read.
+a some CPU time on each read.
 Data written in the GEOM label area allows
 .Nm
 to detect compressed images which have been created with
@@ -63,17 +63,53 @@ creates a unique
 .Pa md#.uzip
 device for each image.
 .Pp
+.Nm
+is not limited to supporting only
+.Xr md 4
+images.
+The image can also reside on a block device.
+.Pq For example, a disk, USB flash drive,  DVD-ROM, etc.
+The appropriate device node will appear with the
+.Pa .uzip
+suffix.
+.Bd -literal -offset indent
+# gpart show da0
+=>      0  7833600  da0  BSD  (3.7G)
+        0  2097152    1  freebsd-ufs  (1.0G)
+  2097152  5736448       - free -  (2.7G)
+# gpart add -t freebsd-ufs -s 1G da0
+da0b added
+# dd if=/tmp/20160217_dcomp_zcomp.uzip bs=256k of=/dev/da0b
+3190+1 records in
+3190+1 records out
+836331008 bytes transferred in 111.021489 secs (7533055 bytes/sec)
+# fsck -t ffs /dev/da0b.uzip
+** /dev/da0b.uzip (NO WRITE)
+** Last Mounted on /mnt
+** Phase 1 - Check Blocks and Sizes
+** Phase 2 - Check Pathnames
+** Phase 3 - Check Connectivity
+** Phase 4 - Check Reference Counts
+** Phase 5 - Check Cyl groups
+97455 files, 604242 used, 184741 free (2349 frags, 22799 blocks,
+   0.3% fragmentation)
+# mount -o ro /dev/da0b.uzip /mnt
+# df /dev/da0b.uzip
+Filesystem     1K-blocks    Used  Avail Capacity  Mounted on
+/dev/da0b.uzip   3155932 2416968 738964    77%    /mnt
+.Ed
+.Pp
 The
 .Nm
-device is subsequently used by the
+device is subsequently used by
 .Fx
-kernel to access the disk images.
+kernel to access the uncompressed data.
 The
 .Nm
 driver does not allow write operations to the underlying disk image.
 To check which
-.Xr md 4
-devices match a given
+.Dq providers
+match a given
 .Nm
 device:
 .Bd -literal -offset indent
@@ -83,13 +119,44 @@ Providers:
 1. Name: md1.uzip
    Mediasize: 22003712 (21M)
    Sectorsize: 512
-   Mode: r1w0e1
 Consumers:
 1. Name: md1
    Mediasize: 9563648 (9.1M)
    Sectorsize: 512
-   Mode: r1w0e1
+
+Geom name: da0b.uzip
+Providers:
+1. Name: da0b.uzip
+   Mediasize: 3355443200 (3.1G)
+   Sectorsize: 512
+Consumers:
+1. Name: da0b
+   Mediasize: 1073741824 (1.0G)
+   Sectorsize: 512
 .Ed
+.Pp
+.Nm
+allows mounting the root file system from a compressed disk partition by
+setting the
+.Dv vfs.root.mountfrom
+tunable.
+See
+.Xr loader.conf 5
+for details.
+.Sh DIAGNOSTICS
+Several flags are provided for tracing
+.Nm
+I/O operations and TOC parsing via the following sysctls.
+.Bl -tag -width indent
+.It Va kern.geom.uzip.debug
+Log level.
+Zero disables logging.
+Higher values enable more verbose debug logging for
+.Nm .
+Supported levels are from 0 (no logging) to 4 (maximum amount of logging).
+.It Va kern.geom.uzip.debug_block
+Log operations involving compressed cluster number.
+.El
 .Sh SEE ALSO
 .Xr GEOM 4 ,
 .Xr md 4 ,
@@ -101,5 +168,12 @@ The
 .Nm
 driver was written by
 .An Max Khon Aq Mt fjoe at FreeBSD.org .
+The block de-duplication code as well as some
+.Nm
+driver optimizations have been contributed by
+.An Maxim Sobolev Aq Mt sobomax at FreeBSD.org .
+The LZMA decompression support and CLOOP 3.0 support have been added by
+.An Aleksandr Rybalko Aq Mt ray at FreeBSD.org .
+.Pp
 This manual page was written by
 .An Ceri Davies Aq Mt ceri at FreeBSD.org .

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Tue Feb 23 23:57:24 2016	(r295942)
+++ head/sys/conf/files	Tue Feb 23 23:59:08 2016	(r295943)
@@ -3083,21 +3083,24 @@ geom/shsec/g_shsec.c		optional geom_shse
 geom/stripe/g_stripe.c		optional geom_stripe
 geom/uncompress/g_uncompress.c	optional geom_uncompress
 contrib/xz-embedded/freebsd/xz_malloc.c	\
-	optional xz_embedded | geom_uncompress \
+	optional xz_embedded | geom_uncompress | geom_uzip \
 	compile-with "${NORMAL_C} -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/"
 contrib/xz-embedded/linux/lib/xz/xz_crc32.c \
-	optional xz_embedded | geom_uncompress \
+	optional xz_embedded | geom_uncompress | geom_uzip \
 	compile-with "${NORMAL_C} -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/"
 contrib/xz-embedded/linux/lib/xz/xz_dec_bcj.c \
-	optional xz_embedded | geom_uncompress \
+	optional xz_embedded | geom_uncompress | geom_uzip \
 	compile-with "${NORMAL_C} -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/"
 contrib/xz-embedded/linux/lib/xz/xz_dec_lzma2.c \
-	optional xz_embedded | geom_uncompress \
+	optional xz_embedded | geom_uncompress | geom_uzip \
 	compile-with "${NORMAL_C} -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/"
 contrib/xz-embedded/linux/lib/xz/xz_dec_stream.c \
-	optional xz_embedded | geom_uncompress \
+	optional xz_embedded | geom_uncompress | geom_uzip \
 	compile-with "${NORMAL_C} -I$S/contrib/xz-embedded/freebsd/ -I$S/contrib/xz-embedded/linux/lib/xz/ -I$S/contrib/xz-embedded/linux/include/linux/"
 geom/uzip/g_uzip.c		optional geom_uzip
+geom/uzip/g_uzip_lzma.c		optional geom_uzip
+geom/uzip/g_uzip_wrkthr.c	optional geom_uzip
+geom/uzip/g_uzip_zlib.c		optional geom_uzip
 geom/vinum/geom_vinum.c		optional geom_vinum
 geom/vinum/geom_vinum_create.c	optional geom_vinum
 geom/vinum/geom_vinum_drive.c	optional geom_vinum

Modified: head/sys/geom/uzip/g_uzip.c
==============================================================================
--- head/sys/geom/uzip/g_uzip.c	Tue Feb 23 23:57:24 2016	(r295942)
+++ head/sys/geom/uzip/g_uzip.c	Tue Feb 23 23:59:08 2016	(r295943)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2004 Max Khon
  * Copyright (c) 2014 Juniper Networks, Inc.
+ * Copyright (c) 2006-2016 Maxim Sobolev <sobomax at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,91 +39,116 @@ __FBSDID("$FreeBSD$");
 #include <sys/malloc.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
-#include <sys/zlib.h>
+#include <sys/kthread.h>
 
 #include <geom/geom.h>
 
-FEATURE(geom_uzip, "GEOM uzip read-only compressed disks support");
+MALLOC_DEFINE(M_GEOM_UZIP, "geom_uzip", "GEOM UZIP data structures");
+
+#include <geom/uzip/g_uzip.h>
+#include <geom/uzip/g_uzip_cloop.h>
+#include <geom/uzip/g_uzip_softc.h>
+#include <geom/uzip/g_uzip_dapi.h>
+#include <geom/uzip/g_uzip_zlib.h>
+#include <geom/uzip/g_uzip_lzma.h>
+#include <geom/uzip/g_uzip_wrkthr.h>
+
+FEATURE(geom_uzip, "GEOM read-only compressed disks support");
+
+struct g_uzip_blk {
+        uint64_t offset;
+        uint32_t blen;
+#define BLEN_UNDEF      UINT32_MAX
+};
+
+#ifndef ABS
+#define	ABS(a)			((a) < 0 ? -(a) : (a))
+#endif
+
+#define BLK_IN_RANGE(mcn, bcn, ilen)	\
+    (((bcn) != BLEN_UNDEF) && ( \
+	((ilen) >= 0 && (mcn >= bcn) && (mcn <= ((intmax_t)(bcn) + (ilen)))) || \
+	((ilen) < 0 && (mcn <= bcn) && (mcn >= ((intmax_t)(bcn) + (ilen)))) \
+    ))
 
-#undef GEOM_UZIP_DEBUG
 #ifdef GEOM_UZIP_DEBUG
-#define	DPRINTF(a)	printf a
+# define GEOM_UZIP_DBG_DEFAULT	3
 #else
-#define	DPRINTF(a)
+# define GEOM_UZIP_DBG_DEFAULT	0
 #endif
 
-static MALLOC_DEFINE(M_GEOM_UZIP, "geom_uzip", "GEOM UZIP data structures");
+#define	GUZ_DBG_ERR	1
+#define	GUZ_DBG_INFO	2
+#define	GUZ_DBG_IO	3
+#define	GUZ_DBG_TOC	4
+
+SYSCTL_DECL(_kern_geom);
+SYSCTL_NODE(_kern_geom, OID_AUTO, uzip, CTLFLAG_RW, 0, "GEOM_UZIP stuff");
+static u_int g_uzip_debug = GEOM_UZIP_DBG_DEFAULT;
+SYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug, CTLFLAG_RWTUN, &g_uzip_debug, 0,
+    "Debug level (0-4)");
+static u_int g_uzip_debug_block = BLEN_UNDEF;
+SYSCTL_UINT(_kern_geom_uzip, OID_AUTO, debug_block, CTLFLAG_RWTUN,
+    &g_uzip_debug_block, 0, "Debug operations around specific cluster#");
+
+#define	DPRINTF(lvl, a)		\
+	if ((lvl) <= g_uzip_debug) { \
+		printf a; \
+	}
+#define	DPRINTF_BLK(lvl, cn, a)	\
+	if ((lvl) <= g_uzip_debug || \
+	    BLK_IN_RANGE(cn, g_uzip_debug_block, 8) || \
+	    BLK_IN_RANGE(cn, g_uzip_debug_block, -8)) { \
+		printf a; \
+	}
+#define	DPRINTF_BRNG(lvl, bcn, ecn, a) \
+	if (bcn >= ecn) { \
+		printf("DPRINTF_BRNG: invalid range (%ju, %ju), BUG BUG " \
+		    "BUG!\n", (uintmax_t)bcn, (uintmax_t)ecn); \
+	} else if (((lvl) <= g_uzip_debug) || \
+	    BLK_IN_RANGE(g_uzip_debug_block, bcn, \
+	     (intmax_t)ecn - (intmax_t)bcn)) { \
+		printf a; \
+	}
 
 #define	UZIP_CLASS_NAME	"UZIP"
 
 /*
  * Maximum allowed valid block size (to prevent foot-shooting)
  */
-#define	MAX_BLKSZ	(MAXPHYS - MAXPHYS / 1000 - 12)
+#define	MAX_BLKSZ	(MAXPHYS)
 
-/*
- * Integer values (block size, number of blocks, offsets)
- * are stored in big-endian (network) order on disk and struct cloop_header
- * and in native order in struct g_uzip_softc
- */
-
-#define	CLOOP_MAGIC_LEN	128
 static char CLOOP_MAGIC_START[] = "#!/bin/sh\n";
 
-struct cloop_header {
-	char magic[CLOOP_MAGIC_LEN];	/* cloop magic */
-	uint32_t blksz;			/* block size */
-	uint32_t nblocks;		/* number of blocks */
-};
-
-struct g_uzip_softc {
-	uint32_t blksz;			/* block size */
-	uint32_t nblocks;		/* number of blocks */
-	uint64_t *offsets;
-
-	struct mtx last_mtx;
-	uint32_t last_blk;		/* last blk no */
-	char *last_buf;			/* last blk data */
-	int req_total;			/* total requests */
-	int req_cached;			/* cached requests */
-};
-
-static void g_uzip_done(struct bio *bp);
+static void g_uzip_read_done(struct bio *bp);
+static void g_uzip_do(struct g_uzip_softc *, struct bio *bp);
 
 static void
 g_uzip_softc_free(struct g_uzip_softc *sc, struct g_geom *gp)
 {
 
 	if (gp != NULL) {
-		DPRINTF(("%s: %d requests, %d cached\n",
+		DPRINTF(GUZ_DBG_INFO, ("%s: %d requests, %d cached\n",
 		    gp->name, sc->req_total, sc->req_cached));
 	}
-	if (sc->offsets != NULL) {
-		free(sc->offsets, M_GEOM_UZIP);
-		sc->offsets = NULL;
-	}
+
+	mtx_lock(&sc->queue_mtx);
+	sc->wrkthr_flags |= GUZ_SHUTDOWN;
+	wakeup(sc);
+	while (!(sc->wrkthr_flags & GUZ_EXITING)) {
+		msleep(sc->procp, &sc->queue_mtx, PRIBIO, "guzfree",
+		    hz / 10);
+	}
+	mtx_unlock(&sc->queue_mtx);
+
+	sc->dcp->free(sc->dcp);
+	free(sc->toc, M_GEOM_UZIP);
+	mtx_destroy(&sc->queue_mtx);
 	mtx_destroy(&sc->last_mtx);
 	free(sc->last_buf, M_GEOM_UZIP);
 	free(sc, M_GEOM_UZIP);
 }
 
-static void *
-z_alloc(void *nil, u_int type, u_int size)
-{
-	void *ptr;
-
-	ptr = malloc(type * size, M_GEOM_UZIP, M_NOWAIT);
-
-	return (ptr);
-}
-
-static void
-z_free(void *nil, void *ptr)
-{
-
-	free(ptr, M_GEOM_UZIP);
-}
-
 static int
 g_uzip_cached(struct g_geom *gp, struct bio *bp)
 {
@@ -144,8 +170,9 @@ g_uzip_cached(struct g_geom *gp, struct 
 		sc->req_cached++;
 		mtx_unlock(&sc->last_mtx);
 
-		DPRINTF(("%s/%s: %p: offset=%jd: got %jd bytes from cache\n",
-		    __func__, gp->name, bp, (intmax_t)ofs, (intmax_t)usz));
+		DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: offset=%jd: got %jd bytes "
+		    "from cache\n", __func__, gp->name, bp, (intmax_t)ofs,
+		    (intmax_t)usz));
 
 		bp->bio_completed += usz;
 		bp->bio_resid -= usz;
@@ -160,6 +187,19 @@ g_uzip_cached(struct g_geom *gp, struct 
 	return (0);
 }
 
+#define BLK_ENDS(sc, bi)	((sc)->toc[(bi)].offset + \
+    (sc)->toc[(bi)].blen)
+
+#define BLK_IS_CONT(sc, bi)	(BLK_ENDS((sc), (bi) - 1) == \
+    (sc)->toc[(bi)].offset)
+#define	BLK_IS_NIL(sc, bi)	((sc)->toc[(bi)].blen == 0)
+
+#define TOFF_2_BOFF(sc, pp, bi)	    ((sc)->toc[(bi)].offset - \
+    (sc)->toc[(bi)].offset % (pp)->sectorsize)
+#define TLEN_2_BLEN(sc, pp, bp, ei) ((BLK_ENDS((sc), (ei)) - \
+    (bp)->bio_offset + (pp)->sectorsize - 1) / \
+    (pp)->sectorsize * (pp)->sectorsize)
+
 static int
 g_uzip_request(struct g_geom *gp, struct bio *bp)
 {
@@ -167,21 +207,14 @@ g_uzip_request(struct g_geom *gp, struct
 	struct bio *bp2;
 	struct g_consumer *cp;
 	struct g_provider *pp;
-	off_t ofs;
-	size_t start_blk, end_blk;
+	off_t ofs, start_blk_ofs;
+	size_t i, start_blk, end_blk, zsize;
 
 	if (g_uzip_cached(gp, bp) != 0)
 		return (1);
 
 	sc = gp->softc;
 
-	bp2 = g_clone_bio(bp);
-	if (bp2 == NULL) {
-		g_io_deliver(bp, ENOMEM);
-		return (1);
-	}
-	bp2->bio_done = g_uzip_done;
-
 	cp = LIST_FIRST(&gp->consumer);
 	pp = cp->provider;
 
@@ -191,17 +224,54 @@ g_uzip_request(struct g_geom *gp, struct
 	end_blk = (ofs + bp->bio_resid + sc->blksz - 1) / sc->blksz;
 	KASSERT(end_blk <= sc->nblocks, ("end_blk out of range"));
 
-	DPRINTF(("%s/%s: %p: start=%u (%jd), end=%u (%jd)\n",
-	    __func__, gp->name, bp,
-	    (u_int)start_blk, (intmax_t)sc->offsets[start_blk],
-	    (u_int)end_blk, (intmax_t)sc->offsets[end_blk]));
+	for (; BLK_IS_NIL(sc, start_blk) && start_blk < end_blk; start_blk++) {
+		/* Fill in any leading Nil blocks */
+		start_blk_ofs = ofs % sc->blksz;
+		zsize = MIN(sc->blksz - start_blk_ofs, bp->bio_resid);
+		DPRINTF_BLK(GUZ_DBG_IO, start_blk, ("%s/%s: %p/%ju: "
+		    "filling %ju zero bytes\n", __func__, gp->name, gp,
+		    (uintmax_t)bp->bio_completed, (uintmax_t)zsize));
+		bzero(bp->bio_data + bp->bio_completed, zsize);
+		bp->bio_completed += zsize;
+		bp->bio_resid -= zsize;
+		ofs += zsize;
+	}
+
+	if (start_blk == end_blk) {
+		KASSERT(bp->bio_resid == 0, ("bp->bio_resid is invalid"));
+		/*
+		 * No non-Nil data is left, complete request immediately.
+		 */
+		DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: all done returning %ju "
+		    "bytes\n", __func__, gp->name, gp,
+		    (uintmax_t)bp->bio_completed));
+		g_io_deliver(bp, 0);
+		return (1);
+	}
 
-	bp2->bio_offset = sc->offsets[start_blk] - 
-	    sc->offsets[start_blk] % pp->sectorsize;
+	for (i = start_blk + 1; i < end_blk; i++) {
+		/* Trim discontinuous areas if any */
+		if (!BLK_IS_CONT(sc, i)) {
+			end_blk = i;
+			break;
+		}
+	}
+
+	DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: "
+	    "start=%u (%ju), end=%u (%ju)\n", __func__, gp->name, bp,
+	    (u_int)start_blk, (uintmax_t)sc->toc[start_blk].offset,
+	    (u_int)end_blk, (uintmax_t)BLK_ENDS(sc, end_blk - 1)));
+
+	bp2 = g_clone_bio(bp);
+	if (bp2 == NULL) {
+		g_io_deliver(bp, ENOMEM);
+		return (1);
+	}
+	bp2->bio_done = g_uzip_read_done;
+
+	bp2->bio_offset = TOFF_2_BOFF(sc, pp, start_blk);
 	while (1) {
-		bp2->bio_length = sc->offsets[end_blk] - bp2->bio_offset;
-		bp2->bio_length = (bp2->bio_length + pp->sectorsize - 1) /
-		    pp->sectorsize * pp->sectorsize;
+		bp2->bio_length = TLEN_2_BLEN(sc, pp, bp2, end_blk - 1);
 		if (bp2->bio_length <= MAXPHYS)
 			break;
 
@@ -215,8 +285,8 @@ g_uzip_request(struct g_geom *gp, struct
 		return (1);
 	}
 
-	DPRINTF(("%s/%s: %p: reading %jd bytes from offset %jd\n",
-	    __func__, gp->name, bp,
+	DPRINTF_BRNG(GUZ_DBG_IO, start_blk, end_blk, ("%s/%s: %p: "
+	    "reading %jd bytes from offset %jd\n", __func__, gp->name, bp,
 	    (intmax_t)bp2->bio_length, (intmax_t)bp2->bio_offset));
 
 	g_io_request(bp2, cp);
@@ -224,21 +294,36 @@ g_uzip_request(struct g_geom *gp, struct
 }
 
 static void
-g_uzip_done(struct bio *bp)
+g_uzip_read_done(struct bio *bp)
+{
+	struct bio *bp2;
+	struct g_geom *gp;
+	struct g_uzip_softc *sc;
+
+	bp2 = bp->bio_parent;
+	gp = bp2->bio_to->geom;
+	sc = gp->softc;
+
+	mtx_lock(&sc->queue_mtx);
+	bioq_disksort(&sc->bio_queue, bp);
+	mtx_unlock(&sc->queue_mtx);
+	wakeup(sc);
+}
+
+static void
+g_uzip_do(struct g_uzip_softc *sc, struct bio *bp)
 {
-	z_stream zs;
 	struct bio *bp2;
 	struct g_provider *pp;
 	struct g_consumer *cp;
 	struct g_geom *gp;
-	struct g_uzip_softc *sc;
 	char *data, *data2;
 	off_t ofs;
-	size_t blk, blkofs, len, ulen;
+	size_t blk, blkofs, len, ulen, firstblk;
+	int err;
 
 	bp2 = bp->bio_parent;
 	gp = bp2->bio_to->geom;
-	sc = gp->softc;
 
 	cp = LIST_FIRST(&gp->consumer);
 	pp = cp->provider;
@@ -253,46 +338,47 @@ g_uzip_done(struct bio *bp)
 		goto done;
 	}
 
-	zs.zalloc = z_alloc;
-	zs.zfree = z_free;
-	if (inflateInit(&zs) != Z_OK) {
-		bp2->bio_error = EILSEQ;
-		goto done;
-	}
-
 	ofs = bp2->bio_offset + bp2->bio_completed;
-	blk = ofs / sc->blksz;
+	firstblk = blk = ofs / sc->blksz;
 	blkofs = ofs % sc->blksz;
-	data = bp->bio_data + sc->offsets[blk] % pp->sectorsize;
+	data = bp->bio_data + sc->toc[blk].offset % pp->sectorsize;
 	data2 = bp2->bio_data + bp2->bio_completed;
 	while (bp->bio_completed && bp2->bio_resid) {
+		if (blk > firstblk && !BLK_IS_CONT(sc, blk)) {
+			DPRINTF_BLK(GUZ_DBG_IO, blk, ("%s/%s: %p: backref'ed "
+			    "cluster #%u requested, looping around\n",
+			    __func__, gp->name, bp2, (u_int)blk));
+			goto done;
+		}
 		ulen = MIN(sc->blksz - blkofs, bp2->bio_resid);
-		len = sc->offsets[blk + 1] - sc->offsets[blk];
-		DPRINTF(("%s/%s: %p/%ju: data2=%p, ulen=%u, data=%p, len=%u\n",
-		    __func__, gp->name, gp, bp->bio_completed,
-		    data2, (u_int)ulen, data, (u_int)len));
+		len = sc->toc[blk].blen;
+		DPRINTF(GUZ_DBG_IO, ("%s/%s: %p/%ju: data2=%p, ulen=%u, "
+		    "data=%p, len=%u\n", __func__, gp->name, gp,
+		    bp->bio_completed, data2, (u_int)ulen, data, (u_int)len));
 		if (len == 0) {
 			/* All zero block: no cache update */
 			bzero(data2, ulen);
 		} else if (len <= bp->bio_completed) {
-			zs.next_in = data;
-			zs.avail_in = len;
-			zs.next_out = sc->last_buf;
-			zs.avail_out = sc->blksz;
 			mtx_lock(&sc->last_mtx);
-			if (inflate(&zs, Z_FINISH) != Z_STREAM_END) {
+			err = sc->dcp->decompress(sc->dcp, gp->name, data,
+			    len, sc->last_buf);
+			if (err != 0) {
 				sc->last_blk = -1;
 				mtx_unlock(&sc->last_mtx);
-				inflateEnd(&zs);
 				bp2->bio_error = EILSEQ;
+				DPRINTF(GUZ_DBG_ERR, ("%s/%s: decompress"
+				    "(%p) failed\n", __func__, gp->name,
+				    sc->dcp));
 				goto done;
 			}
 			sc->last_blk = blk;
 			memcpy(data2, sc->last_buf + blkofs, ulen);
 			mtx_unlock(&sc->last_mtx);
-			if (inflateReset(&zs) != Z_OK) {
-				inflateEnd(&zs);
+			err = sc->dcp->rewind(sc->dcp, gp->name);
+			if (err != 0) {
 				bp2->bio_error = EILSEQ;
+				DPRINTF(GUZ_DBG_ERR, ("%s/%s: rewind(%p) "
+				    "failed\n", __func__, gp->name, sc->dcp));
 				goto done;
 			}
 			data += len;
@@ -307,9 +393,6 @@ g_uzip_done(struct bio *bp)
 		blk++;
 	}
 
-	if (inflateEnd(&zs) != Z_OK)
-		bp2->bio_error = EILSEQ;
-
 done:
 	/* Finish processing the request. */
 	free(bp->bio_data, M_GEOM_UZIP);
@@ -330,9 +413,9 @@ g_uzip_start(struct bio *bp)
 	pp = bp->bio_to;
 	gp = pp->geom;
 
-	DPRINTF(("%s/%s: %p: cmd=%d, offset=%jd, length=%jd, buffer=%p\n",
-	    __func__, gp->name, bp, bp->bio_cmd, (intmax_t)bp->bio_offset,
-	    (intmax_t)bp->bio_length, bp->bio_data));
+	DPRINTF(GUZ_DBG_IO, ("%s/%s: %p: cmd=%d, offset=%jd, length=%jd, "
+	    "buffer=%p\n", __func__, gp->name, bp, bp->bio_cmd,
+	    (intmax_t)bp->bio_offset, (intmax_t)bp->bio_length, bp->bio_data));
 
 	sc = gp->softc;
 	sc->req_total++;
@@ -392,6 +475,92 @@ g_uzip_spoiled(struct g_consumer *cp)
 	g_wither_geom(gp, ENXIO);
 }
 
+static int
+g_uzip_parse_toc(struct g_uzip_softc *sc, struct g_provider *pp,
+    struct g_geom *gp)
+{
+	uint32_t i, j, backref_to;
+	uint64_t max_offset, min_offset;
+
+	min_offset = sizeof(struct cloop_header) +
+	    (sc->nblocks + 1) * sizeof(uint64_t);
+	max_offset = sc->toc[0].offset - 1;
+	for (i = 0; i < sc->nblocks; i++) {
+		/* First do some bounds checking */
+		if ((sc->toc[i].offset < min_offset) ||
+		    (sc->toc[i].offset >= pp->mediasize)) {
+			goto error_offset;
+		}
+		DPRINTF_BLK(GUZ_DBG_IO, i, ("%s: cluster #%u "
+		    "sc->toc[i].offset=%ju max_offset=%ju\n", gp->name,
+		    (u_int)i, (uintmax_t)sc->toc[i].offset,
+		    (uintmax_t)max_offset));
+		backref_to = BLEN_UNDEF;
+		if (sc->toc[i].offset < max_offset) {
+			/*
+			 * For the backref'ed blocks search already parsed
+			 * TOC entries for the matching offset and copy the
+			 * size from matched entry.
+			 */
+			for (j = 0; j <= i; j++) {
+                                if (sc->toc[j].offset == sc->toc[i].offset &&
+				    !BLK_IS_NIL(sc, j)) {
+                                        break;
+                                }
+                                if (j != i) {
+					continue;
+				}
+				DPRINTF(GUZ_DBG_ERR, ("%s: cannot match "
+				    "backref'ed offset at cluster #%u\n",
+				    gp->name, i));
+				return (-1);
+			}
+			sc->toc[i].blen = sc->toc[j].blen;
+			backref_to = j;
+		} else {
+			/*
+			 * For the "normal blocks" seek forward until we hit
+			 * block whose offset is larger than ours and assume
+			 * it's going to be the next one.
+			 */
+			for (j = i + 1; j < sc->nblocks; j++) {
+				if (sc->toc[j].offset > max_offset) {
+					break;
+				}
+			}
+			sc->toc[i].blen = sc->toc[j].offset -
+			    sc->toc[i].offset;
+			if (BLK_ENDS(sc, i) > pp->mediasize) {
+				DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u "
+				    "extends past media boundary (%ju > %ju)\n",
+				    gp->name, (u_int)i,
+				    (uintmax_t)BLK_ENDS(sc, i),
+				    (intmax_t)pp->mediasize));
+				return (-1);
+			}
+			KASSERT(max_offset <= sc->toc[i].offset, (
+			    "%s: max_offset is incorrect: %ju",
+			    gp->name, (uintmax_t)max_offset));
+			max_offset = BLK_ENDS(sc, i) - 1;
+		}
+		DPRINTF_BLK(GUZ_DBG_TOC, i, ("%s: cluster #%u, original %u "
+		    "bytes, in %u bytes", gp->name, i, sc->blksz,
+		    sc->toc[i].blen));
+		if (backref_to != BLEN_UNDEF) {
+			DPRINTF_BLK(GUZ_DBG_TOC, i, (" (->#%u)",
+			    (u_int)backref_to));
+		}
+		DPRINTF_BLK(GUZ_DBG_TOC, i, ("\n"));
+	}
+	return (0);
+
+error_offset:
+	DPRINTF(GUZ_DBG_ERR, ("%s: cluster #%u: invalid offset %ju, "
+	    "min_offset=%ju mediasize=%jd\n", gp->name, (u_int)i,
+	    sc->toc[i].offset, min_offset, pp->mediasize));
+	return (-1);
+}
+
 static struct g_geom *
 g_uzip_taste(struct g_class *mp, struct g_provider *pp, int flags)
 {
@@ -403,6 +572,10 @@ g_uzip_taste(struct g_class *mp, struct 
 	struct g_geom *gp;
 	struct g_provider *pp2;
 	struct g_uzip_softc *sc;
+	enum {
+		GEOM_UZIP = 1,
+		GEOM_ULZMA
+	} type;
 
 	g_trace(G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name);
 	g_topology_assert();
@@ -422,10 +595,7 @@ g_uzip_taste(struct g_class *mp, struct 
 	if (error == 0)
 		error = g_access(cp, 1, 0, 0);
 	if (error) {
-		g_detach(cp);
-		g_destroy_consumer(cp);
-		g_destroy_geom(gp);
-		return (NULL);
+		goto e1;
 	}
 	g_topology_unlock();
 
@@ -433,22 +603,47 @@ g_uzip_taste(struct g_class *mp, struct 
 	 * Read cloop header, look for CLOOP magic, perform
 	 * other validity checks.
 	 */
-	DPRINTF(("%s: media sectorsize %u, mediasize %jd\n",
+	DPRINTF(GUZ_DBG_INFO, ("%s: media sectorsize %u, mediasize %jd\n",
 	    gp->name, pp->sectorsize, (intmax_t)pp->mediasize));
 	buf = g_read_data(cp, 0, pp->sectorsize, NULL);
 	if (buf == NULL)
-		goto err;
+		goto e2;
 	header = (struct cloop_header *) buf;
 	if (strncmp(header->magic, CLOOP_MAGIC_START,
 	    sizeof(CLOOP_MAGIC_START) - 1) != 0) {
-		DPRINTF(("%s: no CLOOP magic\n", gp->name));
-		goto err;
-	}
-	if (header->magic[0x0b] != 'V' || header->magic[0x0c] < '2') {
-		DPRINTF(("%s: image version too old\n", gp->name));
-		goto err;
+		DPRINTF(GUZ_DBG_ERR, ("%s: no CLOOP magic\n", gp->name));
+		goto e3;
 	}
 
+	switch (header->magic[CLOOP_OFS_COMPR]) {
+	case CLOOP_COMP_LZMA:
+	case CLOOP_COMP_LZMA_DDP:
+		type = GEOM_ULZMA;
+		if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_LZMA) {
+			DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n",
+			    gp->name));
+			goto e3;
+		}
+		DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_LZMA image found\n",
+		    gp->name));
+		break;
+	case CLOOP_COMP_LIBZ:
+	case CLOOP_COMP_LIBZ_DDP:
+		type = GEOM_UZIP;
+		if (header->magic[CLOOP_OFS_VERSN] < CLOOP_MINVER_ZLIB) {
+			DPRINTF(GUZ_DBG_ERR, ("%s: image version too old\n",
+			    gp->name));
+			goto e3;
+		}
+		DPRINTF(GUZ_DBG_INFO, ("%s: GEOM_UZIP_ZLIB image found\n",
+		    gp->name));
+		break;
+	default:
+		DPRINTF(GUZ_DBG_ERR, ("%s: unsupported image type\n",
+		    gp->name));
+                goto e3;
+        }
+
 	/*
 	 * Initialize softc and read offsets.
 	 */
@@ -459,7 +654,7 @@ g_uzip_taste(struct g_class *mp, struct 
 	if (sc->blksz % 512 != 0) {
 		printf("%s: block size (%u) should be multiple of 512.\n",
 		    gp->name, sc->blksz);
-		goto err;
+		goto e4;
 	}
 	if (sc->blksz > MAX_BLKSZ) {
 		printf("%s: block size (%u) should not be larger than %d.\n",
@@ -470,15 +665,17 @@ g_uzip_taste(struct g_class *mp, struct 
 	    total_offsets * sizeof(uint64_t) > pp->mediasize) {
 		printf("%s: media too small for %u blocks\n",
 		    gp->name, sc->nblocks);
-		goto err;
+		goto e4;
 	}
-	sc->offsets = malloc(
-	    total_offsets * sizeof(uint64_t), M_GEOM_UZIP, M_WAITOK);
+	sc->toc = malloc(total_offsets * sizeof(struct g_uzip_blk),
+	    M_GEOM_UZIP, M_WAITOK | M_ZERO);
 	offsets_read = MIN(total_offsets,
 	    (pp->sectorsize - sizeof(*header)) / sizeof(uint64_t));
-	for (i = 0; i < offsets_read; i++)
-		sc->offsets[i] = be64toh(((uint64_t *) (header + 1))[i]);
-	DPRINTF(("%s: %u offsets in the first sector\n",
+	for (i = 0; i < offsets_read; i++) {
+		sc->toc[i].offset = be64toh(((uint64_t *) (header + 1))[i]);
+		sc->toc[i].blen = BLEN_UNDEF;
+	}
+	DPRINTF(GUZ_DBG_INFO, ("%s: %u offsets in the first sector\n",
 	       gp->name, offsets_read));
 	for (blk = 1; offsets_read < total_offsets; blk++) {
 		uint32_t nread;
@@ -487,25 +684,59 @@ g_uzip_taste(struct g_class *mp, struct 
 		buf = g_read_data(
 		    cp, blk * pp->sectorsize, pp->sectorsize, NULL);
 		if (buf == NULL)
-			goto err;
+			goto e5;
 		nread = MIN(total_offsets - offsets_read,
 		     pp->sectorsize / sizeof(uint64_t));
-		DPRINTF(("%s: %u offsets read from sector %d\n",
+		DPRINTF(GUZ_DBG_TOC, ("%s: %u offsets read from sector %d\n",
 		    gp->name, nread, blk));
 		for (i = 0; i < nread; i++) {
-			sc->offsets[offsets_read + i] =
+			sc->toc[offsets_read + i].offset =
 			    be64toh(((uint64_t *) buf)[i]);
+			sc->toc[offsets_read + i].blen = BLEN_UNDEF;
 		}
 		offsets_read += nread;
 	}
 	free(buf, M_GEOM);
-	DPRINTF(("%s: done reading offsets\n", gp->name));
+	buf = NULL;
+	offsets_read -= 1;
+	DPRINTF(GUZ_DBG_INFO, ("%s: done reading %u block offsets from %u "
+	    "sectors\n", gp->name, offsets_read, blk));
+	if (sc->nblocks != offsets_read) {
+		DPRINTF(GUZ_DBG_ERR, ("%s: read %s offsets than expected "
+		    "blocks\n", gp->name,
+		    sc->nblocks < offsets_read ? "more" : "less"));
+		goto e5;
+	}
+	/* Massage TOC (table of contents), make sure it is sound */
+	if (g_uzip_parse_toc(sc, pp, gp) != 0) {
+		DPRINTF(GUZ_DBG_ERR, ("%s: TOC error\n", gp->name));
+		goto e5;
+	}
 	mtx_init(&sc->last_mtx, "geom_uzip cache", NULL, MTX_DEF);
+	mtx_init(&sc->queue_mtx, "geom_uzip wrkthread", NULL, MTX_DEF);
+	bioq_init(&sc->bio_queue);
 	sc->last_blk = -1;
 	sc->last_buf = malloc(sc->blksz, M_GEOM_UZIP, M_WAITOK);
 	sc->req_total = 0;
 	sc->req_cached = 0;
 
+	if (type == GEOM_UZIP) {
+		sc->dcp = g_uzip_zlib_ctor(sc->blksz);
+	} else {
+		sc->dcp = g_uzip_lzma_ctor(sc->blksz);
+	}
+	if (sc->dcp == NULL) {
+		goto e6;
+	}
+
+	sc->uzip_do = &g_uzip_do;
+
+	error = kproc_create(g_uzip_wrkthr, sc, &sc->procp, 0, 0, "%s",
+	    gp->name);
+	if (error != 0) {
+		goto e7;
+	}
+
 	g_topology_lock();
 	pp2 = g_new_providerf(gp, "%s", gp->name);
 	pp2->sectorsize = 512;
@@ -515,22 +746,31 @@ g_uzip_taste(struct g_class *mp, struct 
 	g_error_provider(pp2, 0);
 	g_access(cp, -1, 0, 0);
 
-	DPRINTF(("%s: taste ok (%d, %jd), (%d, %d), %x\n",
-	    gp->name,
-	    pp2->sectorsize, (intmax_t)pp2->mediasize,
+	DPRINTF(GUZ_DBG_INFO, ("%s: taste ok (%d, %jd), (%d, %d), %x\n",
+	    gp->name, pp2->sectorsize, (intmax_t)pp2->mediasize,
 	    pp2->stripeoffset, pp2->stripesize, pp2->flags));
-	DPRINTF(("%s: %u x %u blocks\n", gp->name, sc->nblocks, sc->blksz));
+	DPRINTF(GUZ_DBG_INFO, ("%s: %u x %u blocks\n", gp->name, sc->nblocks,
+	    sc->blksz));
 	return (gp);
 
-err:
-	g_topology_lock();
-	g_access(cp, -1, 0, 0);
-	if (buf != NULL)
+e7:
+	sc->dcp->free(sc->dcp);
+e6:
+	free(sc->last_buf, M_GEOM);
+	mtx_destroy(&sc->queue_mtx);
+	mtx_destroy(&sc->last_mtx);
+e5:
+	free(sc->toc, M_GEOM);
+e4:
+	free(gp->softc, M_GEOM_UZIP);
+e3:
+	if (buf != NULL) {
 		free(buf, M_GEOM);
-	if (gp->softc != NULL) {
-		g_uzip_softc_free(gp->softc, NULL);
-		gp->softc = NULL;
 	}
+e2:
+	g_topology_lock();
+	g_access(cp, -1, 0, 0);
+e1:
 	g_detach(cp);
 	g_destroy_consumer(cp);
 	g_destroy_geom(gp);
@@ -547,7 +787,8 @@ g_uzip_destroy_geom(struct gctl_req *req
 	g_topology_assert();
 
 	if (gp->softc == NULL) {
-		DPRINTF(("%s(%s): gp->softc == NULL\n", __func__, gp->name));
+		DPRINTF(GUZ_DBG_ERR, ("%s(%s): gp->softc == NULL\n", __func__,
+		    gp->name));
 		return (ENXIO);
 	}
 

Added: head/sys/geom/uzip/g_uzip.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/geom/uzip/g_uzip.h	Tue Feb 23 23:59:08 2016	(r295943)
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2004 Max Khon
+ * Copyright (c) 2014 Juniper Networks, Inc.
+ * Copyright (c) 2006-2016 Maxim Sobolev <sobomax at FreeBSD.org>
+ * 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. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#if !defined(M_GEOM_UZIP)
+MALLOC_DECLARE(M_GEOM_UZIP);
+#endif
+
+#define DEFINE_RAW_METHOD(func, rval, args...) typedef rval (*func##_t)(args)

Added: head/sys/geom/uzip/g_uzip_cloop.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/geom/uzip/g_uzip_cloop.h	Tue Feb 23 23:59:08 2016	(r295943)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2004-2016 Maxim Sobolev <sobomax at FreeBSD.org>
+ * 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. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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)

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list