1*f0ba4377SMauro Carvalho Chehab===== 2*f0ba4377SMauro Carvalho ChehabCache 3*f0ba4377SMauro Carvalho Chehab===== 4*f0ba4377SMauro Carvalho Chehab 5c6b4fcbaSJoe ThornberIntroduction 6c6b4fcbaSJoe Thornber============ 7c6b4fcbaSJoe Thornber 8c6b4fcbaSJoe Thornberdm-cache is a device mapper target written by Joe Thornber, Heinz 9c6b4fcbaSJoe ThornberMauelshagen, and Mike Snitzer. 10c6b4fcbaSJoe Thornber 11c6b4fcbaSJoe ThornberIt aims to improve performance of a block device (eg, a spindle) by 12c6b4fcbaSJoe Thornberdynamically migrating some of its data to a faster, smaller device 13c6b4fcbaSJoe Thornber(eg, an SSD). 14c6b4fcbaSJoe Thornber 15c6b4fcbaSJoe ThornberThis device-mapper solution allows us to insert this caching at 16c6b4fcbaSJoe Thornberdifferent levels of the dm stack, for instance above the data device for 17c6b4fcbaSJoe Thornbera thin-provisioning pool. Caching solutions that are integrated more 18c6b4fcbaSJoe Thornberclosely with the virtual memory system should give better performance. 19c6b4fcbaSJoe Thornber 20c6b4fcbaSJoe ThornberThe target reuses the metadata library used in the thin-provisioning 21c6b4fcbaSJoe Thornberlibrary. 22c6b4fcbaSJoe Thornber 23c6b4fcbaSJoe ThornberThe decision as to what data to migrate and when is left to a plug-in 24c6b4fcbaSJoe Thornberpolicy module. Several of these have been written as we experiment, 25c6b4fcbaSJoe Thornberand we hope other people will contribute others for specific io 26c6b4fcbaSJoe Thornberscenarios (eg. a vm image server). 27c6b4fcbaSJoe Thornber 28c6b4fcbaSJoe ThornberGlossary 29c6b4fcbaSJoe Thornber======== 30c6b4fcbaSJoe Thornber 31*f0ba4377SMauro Carvalho Chehab Migration 32*f0ba4377SMauro Carvalho Chehab Movement of the primary copy of a logical block from one 33c6b4fcbaSJoe Thornber device to the other. 34*f0ba4377SMauro Carvalho Chehab Promotion 35*f0ba4377SMauro Carvalho Chehab Migration from slow device to fast device. 36*f0ba4377SMauro Carvalho Chehab Demotion 37*f0ba4377SMauro Carvalho Chehab Migration from fast device to slow device. 38c6b4fcbaSJoe Thornber 39c6b4fcbaSJoe ThornberThe origin device always contains a copy of the logical block, which 40c6b4fcbaSJoe Thornbermay be out of date or kept in sync with the copy on the cache device 41c6b4fcbaSJoe Thornber(depending on policy). 42c6b4fcbaSJoe Thornber 43c6b4fcbaSJoe ThornberDesign 44c6b4fcbaSJoe Thornber====== 45c6b4fcbaSJoe Thornber 46c6b4fcbaSJoe ThornberSub-devices 47c6b4fcbaSJoe Thornber----------- 48c6b4fcbaSJoe Thornber 49c6b4fcbaSJoe ThornberThe target is constructed by passing three devices to it (along with 50c6b4fcbaSJoe Thornberother parameters detailed later): 51c6b4fcbaSJoe Thornber 52c6b4fcbaSJoe Thornber1. An origin device - the big, slow one. 53c6b4fcbaSJoe Thornber 54c6b4fcbaSJoe Thornber2. A cache device - the small, fast one. 55c6b4fcbaSJoe Thornber 56c6b4fcbaSJoe Thornber3. A small metadata device - records which blocks are in the cache, 57c6b4fcbaSJoe Thornber which are dirty, and extra hints for use by the policy object. 58c6b4fcbaSJoe Thornber This information could be put on the cache device, but having it 59c6b4fcbaSJoe Thornber separate allows the volume manager to configure it differently, 6066bb2644SMike Snitzer e.g. as a mirror for extra robustness. This metadata device may only 6166bb2644SMike Snitzer be used by a single cache device. 62c6b4fcbaSJoe Thornber 63c6b4fcbaSJoe ThornberFixed block size 64c6b4fcbaSJoe Thornber---------------- 65c6b4fcbaSJoe Thornber 66c6b4fcbaSJoe ThornberThe origin is divided up into blocks of a fixed size. This block size 67c6b4fcbaSJoe Thornberis configurable when you first create the cache. Typically we've been 6805473044SMike Snitzerusing block sizes of 256KB - 1024KB. The block size must be between 64 691346638eSmulhernsectors (32KB) and 2097152 sectors (1GB) and a multiple of 64 sectors (32KB). 70c6b4fcbaSJoe Thornber 71c6b4fcbaSJoe ThornberHaving a fixed block size simplifies the target a lot. But it is 72c6b4fcbaSJoe Thornbersomething of a compromise. For instance, a small part of a block may be 73c6b4fcbaSJoe Thornbergetting hit a lot, yet the whole block will be promoted to the cache. 74c6b4fcbaSJoe ThornberSo large block sizes are bad because they waste cache space. And small 75c6b4fcbaSJoe Thornberblock sizes are bad because they increase the amount of metadata (both 76c6b4fcbaSJoe Thornberin core and on disk). 77c6b4fcbaSJoe Thornber 782ee57d58SJoe ThornberCache operating modes 792ee57d58SJoe Thornber--------------------- 80c6b4fcbaSJoe Thornber 812ee57d58SJoe ThornberThe cache has three operating modes: writeback, writethrough and 822ee57d58SJoe Thornberpassthrough. 83c6b4fcbaSJoe Thornber 84c6b4fcbaSJoe ThornberIf writeback, the default, is selected then a write to a block that is 85c6b4fcbaSJoe Thornbercached will go only to the cache and the block will be marked dirty in 86c6b4fcbaSJoe Thornberthe metadata. 87c6b4fcbaSJoe Thornber 88c6b4fcbaSJoe ThornberIf writethrough is selected then a write to a cached block will not 89c6b4fcbaSJoe Thornbercomplete until it has hit both the origin and cache devices. Clean 90c6b4fcbaSJoe Thornberblocks should remain clean. 91c6b4fcbaSJoe Thornber 922ee57d58SJoe ThornberIf passthrough is selected, useful when the cache contents are not known 932ee57d58SJoe Thornberto be coherent with the origin device, then all reads are served from 942ee57d58SJoe Thornberthe origin device (all reads miss the cache) and all writes are 952ee57d58SJoe Thornberforwarded to the origin device; additionally, write hits cause cache 967b6b2bc9SMike Snitzerblock invalidates. To enable passthrough mode the cache must be clean. 977b6b2bc9SMike SnitzerPassthrough mode allows a cache device to be activated without having to 987b6b2bc9SMike Snitzerworry about coherency. Coherency that exists is maintained, although 997b6b2bc9SMike Snitzerthe cache will gradually cool as writes take place. If the coherency of 1007b6b2bc9SMike Snitzerthe cache can later be verified, or established through use of the 1017b6b2bc9SMike Snitzer"invalidate_cblocks" message, the cache device can be transitioned to 1027b6b2bc9SMike Snitzerwritethrough or writeback mode while still warm. Otherwise, the cache 1037b6b2bc9SMike Snitzercontents can be discarded prior to transitioning to the desired 1047b6b2bc9SMike Snitzeroperating mode. 1052ee57d58SJoe Thornber 106c6b4fcbaSJoe ThornberA simple cleaner policy is provided, which will clean (write back) all 1077b6b2bc9SMike Snitzerdirty blocks in a cache. Useful for decommissioning a cache or when 1087b6b2bc9SMike Snitzershrinking a cache. Shrinking the cache's fast device requires all cache 1097b6b2bc9SMike Snitzerblocks, in the area of the cache being removed, to be clean. If the 1107b6b2bc9SMike Snitzerarea being removed from the cache still contains dirty blocks the resize 1117b6b2bc9SMike Snitzerwill fail. Care must be taken to never reduce the volume used for the 1127b6b2bc9SMike Snitzercache's fast device until the cache is clean. This is of particular 1137b6b2bc9SMike Snitzerimportance if writeback mode is used. Writethrough and passthrough 1147b6b2bc9SMike Snitzermodes already maintain a clean cache. Future support to partially clean 1157b6b2bc9SMike Snitzerthe cache, above a specified threshold, will allow for keeping the cache 1167b6b2bc9SMike Snitzerwarm and in writeback mode during resize. 117c6b4fcbaSJoe Thornber 118c6b4fcbaSJoe ThornberMigration throttling 119c6b4fcbaSJoe Thornber-------------------- 120c6b4fcbaSJoe Thornber 121c6b4fcbaSJoe ThornberMigrating data between the origin and cache device uses bandwidth. 122c6b4fcbaSJoe ThornberThe user can set a throttle to prevent more than a certain amount of 123f884ab15SAnatol Pomozovmigration occurring at any one time. Currently we're not taking any 124c6b4fcbaSJoe Thornberaccount of normal io traffic going to the devices. More work needs 125c6b4fcbaSJoe Thornberdoing here to avoid migrating during those peak io moments. 126c6b4fcbaSJoe Thornber 127c6b4fcbaSJoe ThornberFor the time being, a message "migration_threshold <#sectors>" 128c6b4fcbaSJoe Thornbercan be used to set the maximum number of sectors being migrated, 1299614e2baSJohn Pittmanthe default being 2048 sectors (1MB). 130c6b4fcbaSJoe Thornber 131c6b4fcbaSJoe ThornberUpdating on-disk metadata 132c6b4fcbaSJoe Thornber------------------------- 133c6b4fcbaSJoe Thornber 13407f2b6e0SMike SnitzerOn-disk metadata is committed every time a FLUSH or FUA bio is written. 13507f2b6e0SMike SnitzerIf no such requests are made then commits will occur every second. This 13607f2b6e0SMike Snitzermeans the cache behaves like a physical disk that has a volatile write 13707f2b6e0SMike Snitzercache. If power is lost you may lose some recent writes. The metadata 13807f2b6e0SMike Snitzershould always be consistent in spite of any crash. 139c6b4fcbaSJoe Thornber 140c6b4fcbaSJoe ThornberThe 'dirty' state for a cache block changes far too frequently for us 141c6b4fcbaSJoe Thornberto keep updating it on the fly. So we treat it as a hint. In normal 142c6b4fcbaSJoe Thornberoperation it will be written when the dm device is suspended. If the 143c6b4fcbaSJoe Thornbersystem crashes all cache blocks will be assumed dirty when restarted. 144c6b4fcbaSJoe Thornber 145c6b4fcbaSJoe ThornberPer-block policy hints 146c6b4fcbaSJoe Thornber---------------------- 147c6b4fcbaSJoe Thornber 148c6b4fcbaSJoe ThornberPolicy plug-ins can store a chunk of data per cache block. It's up to 149c6b4fcbaSJoe Thornberthe policy how big this chunk is, but it should be kept small. Like the 150c6b4fcbaSJoe Thornberdirty flags this data is lost if there's a crash so a safe fallback 151c6b4fcbaSJoe Thornbervalue should always be possible. 152c6b4fcbaSJoe Thornber 153c6b4fcbaSJoe ThornberPolicy hints affect performance, not correctness. 154c6b4fcbaSJoe Thornber 155c6b4fcbaSJoe ThornberPolicy messaging 156c6b4fcbaSJoe Thornber---------------- 157c6b4fcbaSJoe Thornber 158c6b4fcbaSJoe ThornberPolicies will have different tunables, specific to each one, so we 159c6b4fcbaSJoe Thornberneed a generic way of getting and setting these. Device-mapper 160c6b4fcbaSJoe Thornbermessages are used. Refer to cache-policies.txt. 161c6b4fcbaSJoe Thornber 162c6b4fcbaSJoe ThornberDiscard bitset resolution 163c6b4fcbaSJoe Thornber------------------------- 164c6b4fcbaSJoe Thornber 165c6b4fcbaSJoe ThornberWe can avoid copying data during migration if we know the block has 166c6b4fcbaSJoe Thornberbeen discarded. A prime example of this is when mkfs discards the 167c6b4fcbaSJoe Thornberwhole block device. We store a bitset tracking the discard state of 168c6b4fcbaSJoe Thornberblocks. However, we allow this bitset to have a different block size 169c6b4fcbaSJoe Thornberfrom the cache blocks. This is because we need to track the discard 170c6b4fcbaSJoe Thornberstate for all of the origin device (compare with the dirty bitset 171c6b4fcbaSJoe Thornberwhich is just for the smaller cache device). 172c6b4fcbaSJoe Thornber 173c6b4fcbaSJoe ThornberTarget interface 174c6b4fcbaSJoe Thornber================ 175c6b4fcbaSJoe Thornber 176c6b4fcbaSJoe ThornberConstructor 177c6b4fcbaSJoe Thornber----------- 178c6b4fcbaSJoe Thornber 179*f0ba4377SMauro Carvalho Chehab :: 180*f0ba4377SMauro Carvalho Chehab 181c6b4fcbaSJoe Thornber cache <metadata dev> <cache dev> <origin dev> <block size> 182c6b4fcbaSJoe Thornber <#feature args> [<feature arg>]* 183c6b4fcbaSJoe Thornber <policy> <#policy args> [policy args]* 184c6b4fcbaSJoe Thornber 185*f0ba4377SMauro Carvalho Chehab ================ ======================================================= 186*f0ba4377SMauro Carvalho Chehab metadata dev fast device holding the persistent metadata 187*f0ba4377SMauro Carvalho Chehab cache dev fast device holding cached data blocks 188*f0ba4377SMauro Carvalho Chehab origin dev slow device holding original data blocks 189*f0ba4377SMauro Carvalho Chehab block size cache unit size in sectors 190c6b4fcbaSJoe Thornber 191*f0ba4377SMauro Carvalho Chehab #feature args number of feature arguments passed 192*f0ba4377SMauro Carvalho Chehab feature args writethrough or passthrough (The default is writeback.) 193c6b4fcbaSJoe Thornber 194*f0ba4377SMauro Carvalho Chehab policy the replacement policy to use 195*f0ba4377SMauro Carvalho Chehab #policy args an even number of arguments corresponding to 196c6b4fcbaSJoe Thornber key/value pairs passed to the policy 197*f0ba4377SMauro Carvalho Chehab policy args key/value pairs passed to the policy 198c6b4fcbaSJoe Thornber E.g. 'sequential_threshold 1024' 199c6b4fcbaSJoe Thornber See cache-policies.txt for details. 200*f0ba4377SMauro Carvalho Chehab ================ ======================================================= 201c6b4fcbaSJoe Thornber 202c6b4fcbaSJoe ThornberOptional feature arguments are: 203*f0ba4377SMauro Carvalho Chehab 204*f0ba4377SMauro Carvalho Chehab 205*f0ba4377SMauro Carvalho Chehab ==================== ======================================================== 206*f0ba4377SMauro Carvalho Chehab writethrough write through caching that prohibits cache block 207c6b4fcbaSJoe Thornber content from being different from origin block content. 208c6b4fcbaSJoe Thornber Without this argument, the default behaviour is to write 209c6b4fcbaSJoe Thornber back cache block contents later for performance reasons, 210c6b4fcbaSJoe Thornber so they may differ from the corresponding origin blocks. 211c6b4fcbaSJoe Thornber 212*f0ba4377SMauro Carvalho Chehab passthrough a degraded mode useful for various cache coherency 2137b6b2bc9SMike Snitzer situations (e.g., rolling back snapshots of 2147b6b2bc9SMike Snitzer underlying storage). Reads and writes always go to 2157b6b2bc9SMike Snitzer the origin. If a write goes to a cached origin 2167b6b2bc9SMike Snitzer block, then the cache block is invalidated. 2177b6b2bc9SMike Snitzer To enable passthrough mode the cache must be clean. 2187b6b2bc9SMike Snitzer 219*f0ba4377SMauro Carvalho Chehab metadata2 use version 2 of the metadata. This stores the dirty 220*f0ba4377SMauro Carvalho Chehab bits in a separate btree, which improves speed of 221*f0ba4377SMauro Carvalho Chehab shutting down the cache. 222629d0a8aSJoe Thornber 223*f0ba4377SMauro Carvalho Chehab no_discard_passdown disable passing down discards from the cache 224de7180ffSMike Snitzer to the origin's data device. 225*f0ba4377SMauro Carvalho Chehab ==================== ======================================================== 226de7180ffSMike Snitzer 227c6b4fcbaSJoe ThornberA policy called 'default' is always registered. This is an alias for 228c6b4fcbaSJoe Thornberthe policy we currently think is giving best all round performance. 229c6b4fcbaSJoe Thornber 230c6b4fcbaSJoe ThornberAs the default policy could vary between kernels, if you are relying on 231c6b4fcbaSJoe Thornberthe characteristics of a specific policy, always request it by name. 232c6b4fcbaSJoe Thornber 233c6b4fcbaSJoe ThornberStatus 234c6b4fcbaSJoe Thornber------ 235c6b4fcbaSJoe Thornber 236*f0ba4377SMauro Carvalho Chehab:: 237*f0ba4377SMauro Carvalho Chehab 2386a388618SMike Snitzer <metadata block size> <#used metadata blocks>/<#total metadata blocks> 2396a388618SMike Snitzer <cache block size> <#used cache blocks>/<#total cache blocks> 2406a388618SMike Snitzer <#read hits> <#read misses> <#write hits> <#write misses> 2416a388618SMike Snitzer <#demotions> <#promotions> <#dirty> <#features> <features>* 2422e68c4e6SMike Snitzer <#core args> <core args>* <policy name> <#policy args> <policy args>* 243028ae9f7SJoe Thornber <cache metadata mode> 244c6b4fcbaSJoe Thornber 245*f0ba4377SMauro Carvalho Chehab 246*f0ba4377SMauro Carvalho Chehab========================= ===================================================== 247*f0ba4377SMauro Carvalho Chehabmetadata block size Fixed block size for each metadata block in 2486a388618SMike Snitzer sectors 249*f0ba4377SMauro Carvalho Chehab#used metadata blocks Number of metadata blocks used 250*f0ba4377SMauro Carvalho Chehab#total metadata blocks Total number of metadata blocks 251*f0ba4377SMauro Carvalho Chehabcache block size Configurable block size for the cache device 2526a388618SMike Snitzer in sectors 253*f0ba4377SMauro Carvalho Chehab#used cache blocks Number of blocks resident in the cache 254*f0ba4377SMauro Carvalho Chehab#total cache blocks Total number of cache blocks 255*f0ba4377SMauro Carvalho Chehab#read hits Number of times a READ bio has been mapped 256c6b4fcbaSJoe Thornber to the cache 257*f0ba4377SMauro Carvalho Chehab#read misses Number of times a READ bio has been mapped 258c6b4fcbaSJoe Thornber to the origin 259*f0ba4377SMauro Carvalho Chehab#write hits Number of times a WRITE bio has been mapped 260c6b4fcbaSJoe Thornber to the cache 261*f0ba4377SMauro Carvalho Chehab#write misses Number of times a WRITE bio has been 262c6b4fcbaSJoe Thornber mapped to the origin 263*f0ba4377SMauro Carvalho Chehab#demotions Number of times a block has been removed 264c6b4fcbaSJoe Thornber from the cache 265*f0ba4377SMauro Carvalho Chehab#promotions Number of times a block has been moved to 266c6b4fcbaSJoe Thornber the cache 267*f0ba4377SMauro Carvalho Chehab#dirty Number of blocks in the cache that differ 268c6b4fcbaSJoe Thornber from the origin 269*f0ba4377SMauro Carvalho Chehab#feature args Number of feature args to follow 270*f0ba4377SMauro Carvalho Chehabfeature args 'writethrough' (optional) 271*f0ba4377SMauro Carvalho Chehab#core args Number of core arguments (must be even) 272*f0ba4377SMauro Carvalho Chehabcore args Key/value pairs for tuning the core 273c6b4fcbaSJoe Thornber e.g. migration_threshold 274*f0ba4377SMauro Carvalho Chehabpolicy name Name of the policy 275*f0ba4377SMauro Carvalho Chehab#policy args Number of policy arguments to follow (must be even) 276*f0ba4377SMauro Carvalho Chehabpolicy args Key/value pairs e.g. sequential_threshold 277*f0ba4377SMauro Carvalho Chehabcache metadata mode ro if read-only, rw if read-write 278*f0ba4377SMauro Carvalho Chehab 279*f0ba4377SMauro Carvalho Chehab In serious cases where even a read-only mode is 280*f0ba4377SMauro Carvalho Chehab deemed unsafe no further I/O will be permitted and 281*f0ba4377SMauro Carvalho Chehab the status will just contain the string 'Fail'. 282*f0ba4377SMauro Carvalho Chehab The userspace recovery tools should then be used. 283*f0ba4377SMauro Carvalho Chehabneeds_check 'needs_check' if set, '-' if not set 284*f0ba4377SMauro Carvalho Chehab A metadata operation has failed, resulting in the 285*f0ba4377SMauro Carvalho Chehab needs_check flag being set in the metadata's 286*f0ba4377SMauro Carvalho Chehab superblock. The metadata device must be 287*f0ba4377SMauro Carvalho Chehab deactivated and checked/repaired before the 288*f0ba4377SMauro Carvalho Chehab cache can be made fully operational again. 289*f0ba4377SMauro Carvalho Chehab '-' indicates needs_check is not set. 290*f0ba4377SMauro Carvalho Chehab========================= ===================================================== 291c6b4fcbaSJoe Thornber 292c6b4fcbaSJoe ThornberMessages 293c6b4fcbaSJoe Thornber-------- 294c6b4fcbaSJoe Thornber 295c6b4fcbaSJoe ThornberPolicies will have different tunables, specific to each one, so we 296c6b4fcbaSJoe Thornberneed a generic way of getting and setting these. Device-mapper 297c6b4fcbaSJoe Thornbermessages are used. (A sysfs interface would also be possible.) 298c6b4fcbaSJoe Thornber 299*f0ba4377SMauro Carvalho ChehabThe message format is:: 300c6b4fcbaSJoe Thornber 301c6b4fcbaSJoe Thornber <key> <value> 302c6b4fcbaSJoe Thornber 303*f0ba4377SMauro Carvalho ChehabE.g.:: 304*f0ba4377SMauro Carvalho Chehab 305c6b4fcbaSJoe Thornber dmsetup message my_cache 0 sequential_threshold 1024 306c6b4fcbaSJoe Thornber 30765790ff9SJoe Thornber 30865790ff9SJoe ThornberInvalidation is removing an entry from the cache without writing it 30965790ff9SJoe Thornberback. Cache blocks can be invalidated via the invalidate_cblocks 3107b6b2bc9SMike Snitzermessage, which takes an arbitrary number of cblock ranges. Each cblock 31183f539e1SMike Snitzerrange's end value is "one past the end", meaning 5-10 expresses a range 31283f539e1SMike Snitzerof values from 5 to 9. Each cblock must be expressed as a decimal 31383f539e1SMike Snitzervalue, in the future a variant message that takes cblock ranges 3143f816bacSsayli karnikexpressed in hexadecimal may be needed to better support efficient 31583f539e1SMike Snitzerinvalidation of larger caches. The cache must be in passthrough mode 316*f0ba4377SMauro Carvalho Chehabwhen invalidate_cblocks is used:: 31765790ff9SJoe Thornber 31865790ff9SJoe Thornber invalidate_cblocks [<cblock>|<cblock begin>-<cblock end>]* 31965790ff9SJoe Thornber 320*f0ba4377SMauro Carvalho ChehabE.g.:: 321*f0ba4377SMauro Carvalho Chehab 32265790ff9SJoe Thornber dmsetup message my_cache 0 invalidate_cblocks 2345 3456-4567 5678-6789 32365790ff9SJoe Thornber 324c6b4fcbaSJoe ThornberExamples 325c6b4fcbaSJoe Thornber======== 326c6b4fcbaSJoe Thornber 327c6b4fcbaSJoe ThornberThe test suite can be found here: 328c6b4fcbaSJoe Thornber 32965790ff9SJoe Thornberhttps://github.com/jthornber/device-mapper-test-suite 330c6b4fcbaSJoe Thornber 331*f0ba4377SMauro Carvalho Chehab:: 332*f0ba4377SMauro Carvalho Chehab 333c6b4fcbaSJoe Thornber dmsetup create my_cache --table '0 41943040 cache /dev/mapper/metadata \ 334c6b4fcbaSJoe Thornber /dev/mapper/ssd /dev/mapper/origin 512 1 writeback default 0' 335c6b4fcbaSJoe Thornber dmsetup create my_cache --table '0 41943040 cache /dev/mapper/metadata \ 336c6b4fcbaSJoe Thornber /dev/mapper/ssd /dev/mapper/origin 1024 1 writeback \ 337c6b4fcbaSJoe Thornber mq 4 sequential_threshold 1024 random_threshold 8' 338