HOWTO for the DouBle device driver Version 0.3          Sep 1994
----------------------------------------------

1   INTRODUCTION
1.1 WHAT IS DOUBLE
1.2 WHAT DOUBLE IS NOT
1.3 QUICK INTERNAL OVERVIEW

2   INSTALLATION
2.1 KERNEL PATCHING
2.2 UTILITIES AND DEVICES

3   USAGE
3.1 MAKING A COMPRESSED DEVICE
3.2 MOUNTING
3.3 USING COMPRESSED DEVICES
3.4 DISMOUNTING
3.5 CHECKING
3.6 OTHER UTILITIES

4   OPTIMIZATION
4.1 CHECKING AND MOUNTING
4.2 COMPRESSION
4.3 DEFAULT VALUES
4.4 SECURITY
4.5 SPEED

5   COMPATIBILITY
5.1 BUGS
5.2 ACKNOWLEDGEMENTS
5.3 AUTHORS
5.4 FINAL REMARKS


1   INTRODUCTION
    ------------
Despite its different purpose, DouBle was originally inspired from the
loop driver, written and copyright by Theodore Ts'o. A few versions later,
it probably doesn't look quite the same tough...
This HOWTO will attempt to guide you through installation, usage and
optimization.


1.1 WHAT IS DOUBLE
    --------------
Double is a block device driver for Linux, that does on-the-fly compression.
It's also been called a "fs-independent on-the-fly compression device driver".
This sometimes leads to confusion (see section 1.2).

Applications include swapspace compression (it does not make too much
sense in most cases because it's slow), or virtually anything eating block
space, but most importantly transparent filesystem compression.


1.2 WHAT DOUBLE IS NOT
    ------------------
Double is _only_ a block device driver. It is not a filesystem, and knows
(almost) nothing about filesystems and their structure. Therefore if
you use it for filesystem compression, the driver will make no difference
between files, inodes, superblocks or whatever (I don't really know about
filesystems myself).


1.3 QUICK INTERNAL OVERVIEW
    -----------------------
The DouBle device driver acts as an interface between the physical
(compressed) data on a device or a regular file, and a pseudo
"DouBle" block device, where non compressed data is written/read:

<client> <-> <double device> <-> <double driver> <-> <regular device>

<regular device> is either a block device (such as a disk partition) or
a regular file on an existing file system (see also 5.0).
Before it can be used, this device (or file) needs to be formatted (see 3.1).
Data on the device (or file) is compressed and cannot be accessed directly.
It is divided in small blocks (often 512 or 1024 bytes).

<double device> is a virtual block device, where non compressed data can be
read/written - usually written first :). The major number for DouBle devices is
currently 19.
The double device needs to be "mounted" beforehand (see 3.2).

<client> is either a filesystem, or any block device "client" (One could
do a tar on a double device). 
Any request going to <double device> will be (de)compressed and piped to/from
the <regular device> transparently:

The <double driver> does all the witchcraft.

The driver:
-----------
The driver transparently divides the <double device> in clusters (for
instance 16 blocks or 8192 bytes). Each cluster is compressed and written to
as few blocks as necessary to hold either the compressed cluster or the non
compressed cluster (in case of overrun). For that purpose, compressed devices
contain several tables (hence the formating step): 

The physical file/device contains a header (a), a bitmap (b) and a
block allocation table (c) - referred to as BAT. In addition, regular files
used as devices also contain a block map table (d).

(a) The header contains a Magic number and relevant informations on
the size and number of blocks and clusters.

(b) The bitmap tries to remember which blocks have been allocated.

(c) The BAT contains information on the type of compression, and on
which blocks clusters are made of: each cluster is made of
as many smaller blocks as necessary (1 to cluster_size/block_size).

(d) The block map table is used by the driver to access a real block
from the offset on a regular file.


2  INSTALLATION
   ------------

WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
!                                                                     !
!   Improper installation may yield unexpected results.               !
!   I take no responsibility for data lost because of this driver.    !
!                                                                     !
WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING

The DouBle device driver for Linux is usually available from:

ftp://sunsite.unc.edu/pub/Linux/kernel/patches/diskdrives/double-X.X.tar.gz
ftp://tsx-11.mit.edu/pub/linux/ALPHA/double/double-X.X.tar.gz

The latest alpha release can usually be obtained from:

ftp://achaz.saclay.cea.fr/pub/double/double-X.X.tar.gz (I'm running this
site, please don't mess with it).
ftp://julia.physik.fu-berlin.de/pub/double/double-X.X.tar.gz

 This distribution includes:
- patches against kernel 1.1.52.
- A set of utilities.

IMPORTANT NOTE:
---------------
Some data structure have changed between version 0.2 and 0.3. Therefore,
most utilities, particularly "mkdble" and "dbck" must be updated with this
release.
In addition, you may not be able to go back to version 0.2x of the driver
once you have started using version 0.3.

Install as you like, but you could do it as follows.


2.1 KERNEL PATCHING
    ---------------

1 cd to /usr/src and tar xvfz double-0.3.tar.gz
-----------------------------------------------

2 Patch the kernel (Linux > 1.1.30), from /usr/src:
---------------------------------------------------
patch -p0 <double/double-0.3.patch

You may have to update config.in manually.

The driver uses 2 definitions in config.in:
if CONFIG_DOUBLE is defined, the driver will be compiled into the kernel.
It won't work without it.

if CONFIG_DBLE_FS is defined, mount/umount/df support for DouBle devices
will be enabled. CONFIG_DBLE_FS is recommended, but not necessary to the
driver. The utilities dbmount and dbumount can still be used instead.

3 make config, clean and zlilo (or whatever)
------------------------------
There are some warnings I didn't get rid of, but it should work.

4 reboot
--------
You should see "DouBle: 8 devices, 48 buffers 152K." or something
similar. If not check previous steps.

2.2 UTILITIES AND DEVICES
    ---------------------

5 Make devices with major=19, minor=0-7:
-----------------------------------------
mknod -m 611 /dev/double0 b 19 0
mknod -m 611 /dev/double1 b 19 1
........
mknod -m 611 /dev/double7 b 19 7
(you don't have to make all of them, unless you actually need 8 devices)

6 IF you have used the CONFIG_DBLE_FS option
---------------------------------------------
If you want to use that option, you need to make the following devices as well:
major = 19, minor = 128-135 i.e.:
mknod -m 611 /dev/cdouble0 b 19 128
mknod -m 611 /dev/cdouble1 b 19 129
........
mknod -m 611 /dev/cdouble7 b 19 135

"cdouble" devices actually mirror the "doubleN" devices with minors 0 to 7.
These devices should not be used for accessing data on the DouBle device.
They are useful only with mount/umount (see 3.2 & 4.1).


7 Make the utilities in ./double
--------------------------------
make
make install, or
mv mkdble dbmount dbumount dbck dbmode /sbin

The dbstat and dbdebug utilities should not be necessary.

Note that there are now man pages, with ".man" "extensions, for most of
these utilities. You can move them to /usr/man/man1/XXXX.1.

You're ready to go !


3  USAGE
   -----

DouBle 0.3 comes with a choice of 5 different compression algorithms.

- LZW is still included, but I won't support it. It is really too slow.
- A derivative of the predictor used in PPP (by Dave Rand) has been
chosen as the default algorithm. Although it does not give the best
compression, it is faster than LZW.
I'll include an improved version when I have time.
- LZRW2 and LZRW3A, written by Ross Williams.  They both yield a good
speed*compression. LZRW2 is faster, LZRW3A compresses better...
- LZV, by Hermann Vogt. This algorithm has similarities with LZRW, it
is fast and compresses well.

- Version 0.3 provides optional support for some fs-like operations - but the
driver is not a filesystem !
There is support for write-super, put_super, read_super and statfs, with a
fs type named "dble" (see also 3.2 & 4.1).
This involves patches to the kernel that I was not really happy with when
I wrote them. Therefore, this is only an option, and the driver can also be
used without fs support.
Now that I have forgotten how ugly these patches really are I would strongly
recommend using this option which is not only more convenient but also makes
the driver more efficient.


3.1 MAKING A COMPRESSED DEVICE
    --------------------------

This formatting step is required whether you are going to use a block device
or a regular file. The "mkdble" utility will take care of that - check out
its man page.

Because I know some will be eager to give it a quick, I'm including an example:
 
mkdble testfile 1000

This will create a 1Mb file, with default values for compression, block and
cluster sizes.
After the creation of headers, there will be around 1.9MB of virtual disk
space available in that file. Headers would be smaller on a real device
(as opposed to a regular file). See "mkdble.man" for more details.
From my experience, (see section 4), it is almost always better to use a
smaller compression factor, such as 1.7 and a good compression algorithm.


3.2 MOUNTING
    --------

Once the compressed device has been formatted, one needs a way to let
the driver have access to it. That's what I'm calling "mounting". When the
device is mounted, non compressed data will be read/written on a "doubleN"
pseudo block device and compressed data will transparently be read/written
on the compressed device.

There are two ways one can mount a compressed device. Choose one or the
other.

- dbmount
---------
The syntax for dbmount is fairly straightforward. Check the man page.
With our previous example in mind it would be:
dbmount testfile /dev/double0

This will work whether or not the CONFIG_DBLE_FS option was enabled in the
kernel.
Note: "cdouble" devices, with minor numbers >= 128 are not intended for use
with db(u)mount.


- with the CONFIG_DBLE_FS option enabled
----------------------------------------

This is where the cdouble[0-7] devices come up.
This option was designed to mimic filesystem mounting. It is also described
int the "dbmount" man page. Briefly, there is support for a _pseudo_
filesystem named "dble". This was only designed for using mount and umount.

mount -t dble ./testfile /dev/cdouble0

So what's the advantage of using mount over dbmount ? There are several:
- the mount entry can be included in /etc/fstab. See also section 4.1 
- df will report mounted devices and the amount of space available.
- the driver has been optimized for devices with "mount".

Note: mount -t dble will not work correctly with /dev/double[0-7].


3.3 USING COMPRESSED DEVICES
    ------------------------

Once a compressed device is mounted on a pseudo block device (doubleN, or
cdoubleN), the doubleN block device will behave just like any other block
device. Most Linux users will want to make a filesystem:

mkfs -t ext2 [options] /dev/double0 [size]
The optional "size" argument is the value reported by dbmount.

DO NOT USE "cdouble" devices here. THEY SHOULD ONLY BE USED FOR:
mount -t dble (3.2) and umount (3.4).

Then, you'll probably want to mount this new filesystem:
mount -t ext2 /dev/double0 /mnt

Next thing, you'll probably want to run df:

If you have used mount -t dble, you'll see 2 new entries:
testfile      .... /dev/cdouble0
/dev/double0 ..... /mnt
Figures on "cdouble0" are relevant to the compressed side of the device.
You just have to make sure that "space used" never comes too close to 100%.
If it does, the compression factor you have chosen in "mkdble" was too high.
Quite naturally, figures on "double0" will show usage on the filesystem.

If you have used dbmount, there will only be one new entry:
/dev/double0 ..... /mnt
In this case, you'll have to use "dbck" to follow disk usage on the compressed
side of the device.

Enjoy !


3.4 DISMOUNTING
    -----------
As one would expect, dismounting is the reverse procedure.

- You need to dismount the filesystem first:

umount /dev/double0     or umount /mnt

-> This is where you'll want to "fsck" it, just for fun: fsck /dev/double0

- Then you need to dismount with the same (u) command that you used for
mounting. Thet's either:
dbumount /dev/double0  (if you have used dbmount)
or
umount /dev/cdouble0  or umount testfile (if you have used mount -t dble).

-> This is where you'll want to try "dbck"


3.5 CHECKING
    --------

- dbck checks a compressed device/file for errors, much like fsck would
check a filesystem:
dbck testfile

It is recommended to dbumount (or umount) before running dbck.
dbck still does not fix problems.
dbck has a couple of options - check the man page.

3.6 OTHER UTILITIES
    ---------------

Very briefly, dbmode is a utility to change current and/or default compression
algorithms. Check the man page.

The following two utilities are included for debugging purposes and should
not need to be used other than for fun or bug reports.
- dbdebug changes debugging level on a DouBle device (resulting in MANY
kernel messages kernel):
dbdebug /dev/double0 5

- dbstat reports the number of read/write accesses made and how often buffers
were already loaded when requested:
dbstat /dev/double0   (dbstat will cumulate information on all devices).


4  OPTIMIZATION
   ------------

The following paragraphs will focus on ways to fine tune the DouBle driver.

Before going into specifics, bear in mind that the CONFIG_DBLE_FS option will
help improve both efficiency and ease of use. 


4.1 CHECKING AND MOUNTING
    ---------------------

Once you have everything under control, you'll probably want to have
compressed devices mounted at startup.
On most Linux systems this is a two step process.

1 - The root partition is mounted read-only and partitions are checked by
one of the /etc/rc??? scripts, with a command like: fsck -A -av
If you want partitions on compressed devices checked, they need to be mounted
with "dbmount" (not mount !) first. That's an example:
dbmount testfile /dev/double0
dbmount /dev/hda3 /dev/double1
...
fsck -A -av

2 - Then, the root partition is remounted read-write and partitions in
/etc/fstab are mounted.
With the DBLE_FS option set, DouBle devices can be put in /etc/fstab.
If DBLE_FS is disabled, you'll have to use dbmount.

a - If you want to stick to dbmount, it's been done in step 1.
All you need to do is to include one entry for each filesystem on compressed
devices in /etc/fstab. Nothing unusual:
/dev/double0	/mnt1	ext2	defaults
/dev/double1	/mnt2	ext2	defaults

b - You'd rather take advantage of the full benefit of using the DBLE_FS
option. In this case, you need to dismount compressed devices first (remember
they were "db"mounted for the fsck).
dbumount /dev/double0
dbumount /dev/double1
...
Then, new entries in /etc/fstab are needed in order to:
- mount the compressed device as a "dble" fs.
- mount the "double" device as an ordinary filesystem. Example:
testfile	/dev/cdouble0	dble	defaults
/dev/double0	/mnt1		ext2	defaults
/dev/hda3	/dev/cdouble1	dble	defaults
/dev/double1	/mnt2		ext2	defaults

The first line will do the same as: mount -t dble testfile /dev/cdouble0
Obviously the second line will do: mount -t ext2 /dev/double0 /mnt
Lines must be in this order !
dismounting will be automagically done in the reverse order when you shudown.

Finally, this is what I have added to my /etc/rc file:

# check fs before mounting
dbmount /dev/hdb2 /dev/double2 # I have one compressed partition on /dev/hdb2
# This will check all filesystems in my /etc/fstab, including /dev/double2
fsck -A -av
# dbumount /dev/double2, so that it can be mounted with mount
dbumount /dev/double2
...

Because the dbck utility is not to fsck standards, you may want to
link /bin/true to /etc/fs/fsck.dble.


4.2 COMPRESSION
    -----------

The default compression factor of 2.0 is optimistic
---------------------------------------------------
It is absolutely necessary to use a value SMALLER than the average
compression factor expected from the data and the choice of an algorithm.

The default algorithm is not the best compressor
------------------------------------------------
I used to believe that the predictor algorithm, also used in PPP, was in
the public domain. I've been told that I was wrong.
According to benchmarks I have done a while back, this is how the algorithms
included approximately compare (as far as compression goes):
best : LZW = LZV >= LZRW3A > LZRW2 > PREDICTOR : worst
Note: Before choosing an algorithm, have a look at the speed comparison and
check patent restrictions that apply to you.
It is easy to make a DouBle device use a different algorithm (see mkdble
arguments and dbmode) and the default value could even be changed in the source
file: mkdble.c (replace code 10 with 12 or 13).

There is no other mean to measure the compression factor than trial and
error. This is what I recommend:
- Make a device with a compression factor of 1.6.
- Start using it and after some time, use dbck to see what compression
was achieved. If it is less than 1.6, make it again, with a lower value.
With LZ* algorithms however, chances are you could do better by moving
compressed files (such as .zip, .gz, .jpg ...) to another partition. 
If it is more than 1.7, you can make it again as well with a larger
compression factor, but it is safe to leave it unchanged.


- when the mount -t dble option is used, df will report block usage on the
double device. It is safe to have more blocks used on the device than on
the filesystem that uses it, as long as the compression ratio (reported by
dbck) remains satisfactory. It is dangerous however, to let the block usage
on the compressed device reach 100%.
In the beginning, you'll see usage increase steadily. You'll also notice
that it does not decrease when you delete files. That's perfectly normal.
It will eventually stabilize, hopefully before it's full. If not, make it
again with a smaller compression factor value.

If you are not interested in understanding why that is, skip to the next
section.

The reason for that strange behavior is that most filesystems don't actually
clear blocks when files are removed. File contents are still on the device,
but are dereferenced from inodes and directories. The filesystem will not
overwrite these blocks as long as it does not need space. Therefore, blocks
that are free from the filesystem point of view no longer are from the device
point of view.
The reason it's OK is that the filesystem _will_ eventually use this space
when new files are written. Eventually, there'll be a steady state when each
filesystem block has been allocated at least once. All you need to make sure
of is that state is below 100% usage on the compressed device.

In the ext2 filesystem, there is actually an option to make the filesystem
clear blocks with a random value when files are deleted. Using this option
should make usage report on compressed devices drop when files are deleted.

Practically, one can also make usage report drop manually (use with care):
Let's say after some time using a compressed device,
df reports 1000 available blocks on /dev/cdouble0 (compressed)
and 2000 available blocks on /dev/double0, mounted on /mnt (non compressed).
Chances are that after:
dd if=/dev/zero of=foobar bs=1024 count=1500
df will report more than 1000 blocks on the compressed device (/dev/cdouble0)
df will report ~500 available blocks on /mnt (/dev/double0).

Indeed, dd will create a 1500 Kb file "foobar" on the filesystem. This file
is filled with zeros, which compress very well. For that purpose, the
filesystem will use blocks that were previously allocated then released.
Data previously written to these filesystem blocks was presumably more complex
and was using more compressed blocks. Therefore the compression driver will
use fewer blocks than before for "foobar". Finally, there will be more
compressed blocks available than before!
If you try the above example yourself make sure that:
- foobar is smaller than available space on the filesystem (here 1500 vs 2000).
-> fobbar is smaller than ~2 x available space on the compressed device.
don't forget to delete foobar after the experiment.



4.3 DEFAULT VALUES
    --------------
These are the default values used by mkdble:
compression factor: 2.0
cluster size: 10240 bytes
block size: 512 bytes
compression algorithm: predictor (code 10)

Default block and cluster size are only a speed/compression compromise.
Speed can be increased by using block size = 1024, compression can be
increased by using a block size of 256. In most cases, the cluster size
should have little effect on speed and thus the bigger, the better.
10240 is currently the maximum.

Note: the cluster size is not related to filesystem block size. I know
it might be confusing, but one should not be tempted to reduce its size
on a filesystem containing small files. You may want to decrease the 
cluster size if you are going to do mainly random access.


4.4 DATA SECURITY
    -------------

The most important thing with a compressed device is to never let it run
out of space. In that purpose:
- Choose an appropriate compression factor and algorithm when creating
the device.
- Do not store too many compressed files (.gz, .zip, .Z, .jpg...) on it.
- run dbck (df is easier if you are using mount -t dble ...) frequently,
to check available space on compressed devices.

In this release, the driver still recovers poorly from errors resulting
from running out of space. I swear the next alpha release will help improve
that.



4.5 SPEED
    -----

The purpose of this driver is to transparently make more disk space
available. Access to a compressed device is generally slower than to a normal
block device. However, there are ways to increase speed.

Because the default block size under Linux is 1024 bytes, compressed
devices with a block size of 1024 are faster. The default value of 512
bytes emphasizes compression.

Buffer caches within the driver will compensate for speed differences due
to block size. For better performance, the number of internal buffers
(particularly DB_NR_BH in linux/include/linux/double.h) can be increased.
Unfortunately, the driver buffers are not shared with kernel buffers -
They are allocated for the sole benefit of the driver.

Block devices are faster than regular files used as compressed devices.
The reason is that any read/write operation on a regular file goes through
additional (filesystem dependent) layers.

Finally, speed is also dependent on the compression algorithm used. According
to benchmark I have done:
fastest - LZRW2 > LZV > PREDICTOR > LZRW3A >> LZW - slowest
LZW is particularly slow in reading.


5.0 COMPATIBILITY
    -------------

- Double is not compatible with any other compression driver.
- As far as I have been told, Double is not compatible with umsdos.
- Double is not yet compatible with filesystems that do not implement the "bmap"
system call - most notably MSDOS filesystems with blocks bigger than 1024 bytes.
Practically, this means that in most cases, one can't use a compressed device
located on an MSDOS filesystem.


5.1 BUGS
    ----

- This is not really a bug, but will look like one from a user point of view:
The driver will behave poorly if the compressed side of a device runs out
of space while writing - anything a filesystem will attempt to write in this
case will result in an I/O error and will not be written. It may become
critical when the fs attempts to update directories or inode blocks.

- None, except those I have mentionned, those I forgot, those I don't know
about yet. To the best of my knowledge I have done what I could to remove
them all.



5.2 ACKNOWLEDGMENTS
    ---------------

Leo Broukhis <leo@fuso.zycad.com> and Jean-Loup Gailly <jloup@chorus.fr>
have contributed a kind and precious expertise in the choice of new
compression algorithms.
Thomas Graichen <graichen@sirius.physik.fu-berlin.de> has been most
helpful in making suggestions and comprehensive tests of this driver,
which have resulted in several improvements.
list of improvements that I haven't yet included in this version, but
I still want to thank him.
Michael Beck <beck@informatik.hu-berlin.de> has made useful suggestions.
Richard <kunze@informatik.uni-hannover.de> has provided the LZV
algorithm written by  Hermann Vogt.
Didier Genard <dig@swn.sni.be> has contributed most improvements,
to this version and the next few versions. 
Many other people have provided useful reports; I hope you will continue
to help me inprove the DouBle driver.


5.3 AUTHORS
    -------

Jean-Marc Verbavatz (Jan 1994) <verbavatz@dsvidf.cea.fr>

Parts of the driver source were adapted from the loop driver copyright
by Theodore Ts'o.

LZV was written by Hermann Vogt.

The LZRW2, LZRW3-A algorithms were written by Ross Williams.

The predictor algorithm was originally written by Dave Rand, updated by
a couple of other people (see file pred.c), most recently for by
Peter Trattler <peter@sztma.tu-graz.ac.at>

5.4 FINAL REMARKS
    -------------

A while back, I created a mailing list called 'COMPRESSION' on niksula.hut.fi,
where I thought all compression issues could be discussed. It has not been
used very widely - most people have been talking to me, and presumably to
each other either directly or through other channels.
That's fine with me, but I still believe that more people could benefit
from questions/answers, suggestions or contributions posted on a mailing list.

In any case as usual, feel free to get in touch with me for any question,
suggestion or bug report. I always try to help and to consider suggestions.

Thank you for using the DouBle device driver; compress safely !
Good Luck,

Jean-Marc Verbavatz <jmv@achaz.saclay.cea.fr>

(verbavatz@dsvidf.cea.fr is my "official" e-mail address, but I don't check it
too often).
