|  | Introduction | 
|  | ============ | 
|  |  | 
|  | dm-cache is a device mapper target written by Joe Thornber, Heinz | 
|  | Mauelshagen, and Mike Snitzer. | 
|  |  | 
|  | It aims to improve performance of a block device (eg, a spindle) by | 
|  | dynamically migrating some of its data to a faster, smaller device | 
|  | (eg, an SSD). | 
|  |  | 
|  | This device-mapper solution allows us to insert this caching at | 
|  | different levels of the dm stack, for instance above the data device for | 
|  | a thin-provisioning pool.  Caching solutions that are integrated more | 
|  | closely with the virtual memory system should give better performance. | 
|  |  | 
|  | The target reuses the metadata library used in the thin-provisioning | 
|  | library. | 
|  |  | 
|  | The decision as to what data to migrate and when is left to a plug-in | 
|  | policy module.  Several of these have been written as we experiment, | 
|  | and we hope other people will contribute others for specific io | 
|  | scenarios (eg. a vm image server). | 
|  |  | 
|  | Glossary | 
|  | ======== | 
|  |  | 
|  | Migration -  Movement of the primary copy of a logical block from one | 
|  | device to the other. | 
|  | Promotion -  Migration from slow device to fast device. | 
|  | Demotion  -  Migration from fast device to slow device. | 
|  |  | 
|  | The origin device always contains a copy of the logical block, which | 
|  | may be out of date or kept in sync with the copy on the cache device | 
|  | (depending on policy). | 
|  |  | 
|  | Design | 
|  | ====== | 
|  |  | 
|  | Sub-devices | 
|  | ----------- | 
|  |  | 
|  | The target is constructed by passing three devices to it (along with | 
|  | other parameters detailed later): | 
|  |  | 
|  | 1. An origin device - the big, slow one. | 
|  |  | 
|  | 2. A cache device - the small, fast one. | 
|  |  | 
|  | 3. A small metadata device - records which blocks are in the cache, | 
|  | which are dirty, and extra hints for use by the policy object. | 
|  | This information could be put on the cache device, but having it | 
|  | separate allows the volume manager to configure it differently, | 
|  | e.g. as a mirror for extra robustness. | 
|  |  | 
|  | Fixed block size | 
|  | ---------------- | 
|  |  | 
|  | The origin is divided up into blocks of a fixed size.  This block size | 
|  | is configurable when you first create the cache.  Typically we've been | 
|  | using block sizes of 256k - 1024k. | 
|  |  | 
|  | Having a fixed block size simplifies the target a lot.  But it is | 
|  | something of a compromise.  For instance, a small part of a block may be | 
|  | getting hit a lot, yet the whole block will be promoted to the cache. | 
|  | So large block sizes are bad because they waste cache space.  And small | 
|  | block sizes are bad because they increase the amount of metadata (both | 
|  | in core and on disk). | 
|  |  | 
|  | Writeback/writethrough | 
|  | ---------------------- | 
|  |  | 
|  | The cache has two modes, writeback and writethrough. | 
|  |  | 
|  | If writeback, the default, is selected then a write to a block that is | 
|  | cached will go only to the cache and the block will be marked dirty in | 
|  | the metadata. | 
|  |  | 
|  | If writethrough is selected then a write to a cached block will not | 
|  | complete until it has hit both the origin and cache devices.  Clean | 
|  | blocks should remain clean. | 
|  |  | 
|  | A simple cleaner policy is provided, which will clean (write back) all | 
|  | dirty blocks in a cache.  Useful for decommissioning a cache. | 
|  |  | 
|  | Migration throttling | 
|  | -------------------- | 
|  |  | 
|  | Migrating data between the origin and cache device uses bandwidth. | 
|  | The user can set a throttle to prevent more than a certain amount of | 
|  | migration occuring at any one time.  Currently we're not taking any | 
|  | account of normal io traffic going to the devices.  More work needs | 
|  | doing here to avoid migrating during those peak io moments. | 
|  |  | 
|  | For the time being, a message "migration_threshold <#sectors>" | 
|  | can be used to set the maximum number of sectors being migrated, | 
|  | the default being 204800 sectors (or 100MB). | 
|  |  | 
|  | Updating on-disk metadata | 
|  | ------------------------- | 
|  |  | 
|  | On-disk metadata is committed every time a REQ_SYNC or REQ_FUA bio is | 
|  | written.  If no such requests are made then commits will occur every | 
|  | second.  This means the cache behaves like a physical disk that has a | 
|  | write cache (the same is true of the thin-provisioning target).  If | 
|  | power is lost you may lose some recent writes.  The metadata should | 
|  | always be consistent in spite of any crash. | 
|  |  | 
|  | The 'dirty' state for a cache block changes far too frequently for us | 
|  | to keep updating it on the fly.  So we treat it as a hint.  In normal | 
|  | operation it will be written when the dm device is suspended.  If the | 
|  | system crashes all cache blocks will be assumed dirty when restarted. | 
|  |  | 
|  | Per-block policy hints | 
|  | ---------------------- | 
|  |  | 
|  | Policy plug-ins can store a chunk of data per cache block.  It's up to | 
|  | the policy how big this chunk is, but it should be kept small.  Like the | 
|  | dirty flags this data is lost if there's a crash so a safe fallback | 
|  | value should always be possible. | 
|  |  | 
|  | For instance, the 'mq' policy, which is currently the default policy, | 
|  | uses this facility to store the hit count of the cache blocks.  If | 
|  | there's a crash this information will be lost, which means the cache | 
|  | may be less efficient until those hit counts are regenerated. | 
|  |  | 
|  | Policy hints affect performance, not correctness. | 
|  |  | 
|  | Policy messaging | 
|  | ---------------- | 
|  |  | 
|  | Policies will have different tunables, specific to each one, so we | 
|  | need a generic way of getting and setting these.  Device-mapper | 
|  | messages are used.  Refer to cache-policies.txt. | 
|  |  | 
|  | Discard bitset resolution | 
|  | ------------------------- | 
|  |  | 
|  | We can avoid copying data during migration if we know the block has | 
|  | been discarded.  A prime example of this is when mkfs discards the | 
|  | whole block device.  We store a bitset tracking the discard state of | 
|  | blocks.  However, we allow this bitset to have a different block size | 
|  | from the cache blocks.  This is because we need to track the discard | 
|  | state for all of the origin device (compare with the dirty bitset | 
|  | which is just for the smaller cache device). | 
|  |  | 
|  | Target interface | 
|  | ================ | 
|  |  | 
|  | Constructor | 
|  | ----------- | 
|  |  | 
|  | cache <metadata dev> <cache dev> <origin dev> <block size> | 
|  | <#feature args> [<feature arg>]* | 
|  | <policy> <#policy args> [policy args]* | 
|  |  | 
|  | metadata dev    : fast device holding the persistent metadata | 
|  | cache dev	 : fast device holding cached data blocks | 
|  | origin dev	 : slow device holding original data blocks | 
|  | block size      : cache unit size in sectors | 
|  |  | 
|  | #feature args   : number of feature arguments passed | 
|  | feature args    : writethrough.  (The default is writeback.) | 
|  |  | 
|  | policy          : the replacement policy to use | 
|  | #policy args    : an even number of arguments corresponding to | 
|  | key/value pairs passed to the policy | 
|  | policy args     : key/value pairs passed to the policy | 
|  | E.g. 'sequential_threshold 1024' | 
|  | See cache-policies.txt for details. | 
|  |  | 
|  | Optional feature arguments are: | 
|  | writethrough  : write through caching that prohibits cache block | 
|  | content from being different from origin block content. | 
|  | Without this argument, the default behaviour is to write | 
|  | back cache block contents later for performance reasons, | 
|  | so they may differ from the corresponding origin blocks. | 
|  |  | 
|  | A policy called 'default' is always registered.  This is an alias for | 
|  | the policy we currently think is giving best all round performance. | 
|  |  | 
|  | As the default policy could vary between kernels, if you are relying on | 
|  | the characteristics of a specific policy, always request it by name. | 
|  |  | 
|  | Status | 
|  | ------ | 
|  |  | 
|  | <#used metadata blocks>/<#total metadata blocks> <#read hits> <#read misses> | 
|  | <#write hits> <#write misses> <#demotions> <#promotions> <#blocks in cache> | 
|  | <#dirty> <#features> <features>* <#core args> <core args>* <#policy args> | 
|  | <policy args>* | 
|  |  | 
|  | #used metadata blocks    : Number of metadata blocks used | 
|  | #total metadata blocks   : Total number of metadata blocks | 
|  | #read hits               : Number of times a READ bio has been mapped | 
|  | to the cache | 
|  | #read misses             : Number of times a READ bio has been mapped | 
|  | to the origin | 
|  | #write hits              : Number of times a WRITE bio has been mapped | 
|  | to the cache | 
|  | #write misses            : Number of times a WRITE bio has been | 
|  | mapped to the origin | 
|  | #demotions               : Number of times a block has been removed | 
|  | from the cache | 
|  | #promotions              : Number of times a block has been moved to | 
|  | the cache | 
|  | #blocks in cache         : Number of blocks resident in the cache | 
|  | #dirty                   : Number of blocks in the cache that differ | 
|  | from the origin | 
|  | #feature args            : Number of feature args to follow | 
|  | feature args             : 'writethrough' (optional) | 
|  | #core args               : Number of core arguments (must be even) | 
|  | core args                : Key/value pairs for tuning the core | 
|  | e.g. migration_threshold | 
|  | #policy args             : Number of policy arguments to follow (must be even) | 
|  | policy args              : Key/value pairs | 
|  | e.g. 'sequential_threshold 1024 | 
|  |  | 
|  | Messages | 
|  | -------- | 
|  |  | 
|  | Policies will have different tunables, specific to each one, so we | 
|  | need a generic way of getting and setting these.  Device-mapper | 
|  | messages are used.  (A sysfs interface would also be possible.) | 
|  |  | 
|  | The message format is: | 
|  |  | 
|  | <key> <value> | 
|  |  | 
|  | E.g. | 
|  | dmsetup message my_cache 0 sequential_threshold 1024 | 
|  |  | 
|  | Examples | 
|  | ======== | 
|  |  | 
|  | The test suite can be found here: | 
|  |  | 
|  | https://github.com/jthornber/thinp-test-suite | 
|  |  | 
|  | dmsetup create my_cache --table '0 41943040 cache /dev/mapper/metadata \ | 
|  | /dev/mapper/ssd /dev/mapper/origin 512 1 writeback default 0' | 
|  | dmsetup create my_cache --table '0 41943040 cache /dev/mapper/metadata \ | 
|  | /dev/mapper/ssd /dev/mapper/origin 1024 1 writeback \ | 
|  | mq 4 sequential_threshold 1024 random_threshold 8' |