0001 .. SPDX-License-Identifier: GPL-2.0
0002
0003 ==================
0004 XFS Logging Design
0005 ==================
0006
0007 Preamble
0008 ========
0009
0010 This document describes the design and algorithms that the XFS journalling
0011 subsystem is based on. This document describes the design and algorithms that
0012 the XFS journalling subsystem is based on so that readers may familiarize
0013 themselves with the general concepts of how transaction processing in XFS works.
0014
0015 We begin with an overview of transactions in XFS, followed by describing how
0016 transaction reservations are structured and accounted, and then move into how we
0017 guarantee forwards progress for long running transactions with finite initial
0018 reservations bounds. At this point we need to explain how relogging works. With
0019 the basic concepts covered, the design of the delayed logging mechanism is
0020 documented.
0021
0022
0023 Introduction
0024 ============
0025
0026 XFS uses Write Ahead Logging for ensuring changes to the filesystem metadata
0027 are atomic and recoverable. For reasons of space and time efficiency, the
0028 logging mechanisms are varied and complex, combining intents, logical and
0029 physical logging mechanisms to provide the necessary recovery guarantees the
0030 filesystem requires.
0031
0032 Some objects, such as inodes and dquots, are logged in logical format where the
0033 details logged are made up of the changes to in-core structures rather than
0034 on-disk structures. Other objects - typically buffers - have their physical
0035 changes logged. Long running atomic modifications have individual changes
0036 chained together by intents, ensuring that journal recovery can restart and
0037 finish an operation that was only partially done when the system stopped
0038 functioning.
0039
0040 The reason for these differences is to keep the amount of log space and CPU time
0041 required to process objects being modified as small as possible and hence the
0042 logging overhead as low as possible. Some items are very frequently modified,
0043 and some parts of objects are more frequently modified than others, so keeping
0044 the overhead of metadata logging low is of prime importance.
0045
0046 The method used to log an item or chain modifications together isn't
0047 particularly important in the scope of this document. It suffices to know that
0048 the method used for logging a particular object or chaining modifications
0049 together are different and are dependent on the object and/or modification being
0050 performed. The logging subsystem only cares that certain specific rules are
0051 followed to guarantee forwards progress and prevent deadlocks.
0052
0053
0054 Transactions in XFS
0055 ===================
0056
0057 XFS has two types of high level transactions, defined by the type of log space
0058 reservation they take. These are known as "one shot" and "permanent"
0059 transactions. Permanent transaction reservations can take reservations that span
0060 commit boundaries, whilst "one shot" transactions are for a single atomic
0061 modification.
0062
0063 The type and size of reservation must be matched to the modification taking
0064 place. This means that permanent transactions can be used for one-shot
0065 modifications, but one-shot reservations cannot be used for permanent
0066 transactions.
0067
0068 In the code, a one-shot transaction pattern looks somewhat like this::
0069
0070 tp = xfs_trans_alloc(<reservation>)
0071 <lock items>
0072 <join item to transaction>
0073 <do modification>
0074 xfs_trans_commit(tp);
0075
0076 As items are modified in the transaction, the dirty regions in those items are
0077 tracked via the transaction handle. Once the transaction is committed, all
0078 resources joined to it are released, along with the remaining unused reservation
0079 space that was taken at the transaction allocation time.
0080
0081 In contrast, a permanent transaction is made up of multiple linked individual
0082 transactions, and the pattern looks like this::
0083
0084 tp = xfs_trans_alloc(<reservation>)
0085 xfs_ilock(ip, XFS_ILOCK_EXCL)
0086
0087 loop {
0088 xfs_trans_ijoin(tp, 0);
0089 <do modification>
0090 xfs_trans_log_inode(tp, ip);
0091 xfs_trans_roll(&tp);
0092 }
0093
0094 xfs_trans_commit(tp);
0095 xfs_iunlock(ip, XFS_ILOCK_EXCL);
0096
0097 While this might look similar to a one-shot transaction, there is an important
0098 difference: xfs_trans_roll() performs a specific operation that links two
0099 transactions together::
0100
0101 ntp = xfs_trans_dup(tp);
0102 xfs_trans_commit(tp);
0103 xfs_log_reserve(ntp);
0104
0105 This results in a series of "rolling transactions" where the inode is locked
0106 across the entire chain of transactions. Hence while this series of rolling
0107 transactions is running, nothing else can read from or write to the inode and
0108 this provides a mechanism for complex changes to appear atomic from an external
0109 observer's point of view.
0110
0111 It is important to note that a series of rolling transactions in a permanent
0112 transaction does not form an atomic change in the journal. While each
0113 individual modification is atomic, the chain is *not atomic*. If we crash half
0114 way through, then recovery will only replay up to the last transactional
0115 modification the loop made that was committed to the journal.
0116
0117 This affects long running permanent transactions in that it is not possible to
0118 predict how much of a long running operation will actually be recovered because
0119 there is no guarantee of how much of the operation reached stale storage. Hence
0120 if a long running operation requires multiple transactions to fully complete,
0121 the high level operation must use intents and deferred operations to guarantee
0122 recovery can complete the operation once the first transactions is persisted in
0123 the on-disk journal.
0124
0125
0126 Transactions are Asynchronous
0127 =============================
0128
0129 In XFS, all high level transactions are asynchronous by default. This means that
0130 xfs_trans_commit() does not guarantee that the modification has been committed
0131 to stable storage when it returns. Hence when a system crashes, not all the
0132 completed transactions will be replayed during recovery.
0133
0134 However, the logging subsystem does provide global ordering guarantees, such
0135 that if a specific change is seen after recovery, all metadata modifications
0136 that were committed prior to that change will also be seen.
0137
0138 For single shot operations that need to reach stable storage immediately, or
0139 ensuring that a long running permanent transaction is fully committed once it is
0140 complete, we can explicitly tag a transaction as synchronous. This will trigger
0141 a "log force" to flush the outstanding committed transactions to stable storage
0142 in the journal and wait for that to complete.
0143
0144 Synchronous transactions are rarely used, however, because they limit logging
0145 throughput to the IO latency limitations of the underlying storage. Instead, we
0146 tend to use log forces to ensure modifications are on stable storage only when
0147 a user operation requires a synchronisation point to occur (e.g. fsync).
0148
0149
0150 Transaction Reservations
0151 ========================
0152
0153 It has been mentioned a number of times now that the logging subsystem needs to
0154 provide a forwards progress guarantee so that no modification ever stalls
0155 because it can't be written to the journal due to a lack of space in the
0156 journal. This is achieved by the transaction reservations that are made when
0157 a transaction is first allocated. For permanent transactions, these reservations
0158 are maintained as part of the transaction rolling mechanism.
0159
0160 A transaction reservation provides a guarantee that there is physical log space
0161 available to write the modification into the journal before we start making
0162 modifications to objects and items. As such, the reservation needs to be large
0163 enough to take into account the amount of metadata that the change might need to
0164 log in the worst case. This means that if we are modifying a btree in the
0165 transaction, we have to reserve enough space to record a full leaf-to-root split
0166 of the btree. As such, the reservations are quite complex because we have to
0167 take into account all the hidden changes that might occur.
0168
0169 For example, a user data extent allocation involves allocating an extent from
0170 free space, which modifies the free space trees. That's two btrees. Inserting
0171 the extent into the inode's extent map might require a split of the extent map
0172 btree, which requires another allocation that can modify the free space trees
0173 again. Then we might have to update reverse mappings, which modifies yet
0174 another btree which might require more space. And so on. Hence the amount of
0175 metadata that a "simple" operation can modify can be quite large.
0176
0177 This "worst case" calculation provides us with the static "unit reservation"
0178 for the transaction that is calculated at mount time. We must guarantee that the
0179 log has this much space available before the transaction is allowed to proceed
0180 so that when we come to write the dirty metadata into the log we don't run out
0181 of log space half way through the write.
0182
0183 For one-shot transactions, a single unit space reservation is all that is
0184 required for the transaction to proceed. For permanent transactions, however, we
0185 also have a "log count" that affects the size of the reservation that is to be
0186 made.
0187
0188 While a permanent transaction can get by with a single unit of space
0189 reservation, it is somewhat inefficient to do this as it requires the
0190 transaction rolling mechanism to re-reserve space on every transaction roll. We
0191 know from the implementation of the permanent transactions how many transaction
0192 rolls are likely for the common modifications that need to be made.
0193
0194 For example, and inode allocation is typically two transactions - one to
0195 physically allocate a free inode chunk on disk, and another to allocate an inode
0196 from an inode chunk that has free inodes in it. Hence for an inode allocation
0197 transaction, we might set the reservation log count to a value of 2 to indicate
0198 that the common/fast path transaction will commit two linked transactions in a
0199 chain. Each time a permanent transaction rolls, it consumes an entire unit
0200 reservation.
0201
0202 Hence when the permanent transaction is first allocated, the log space
0203 reservation is increases from a single unit reservation to multiple unit
0204 reservations. That multiple is defined by the reservation log count, and this
0205 means we can roll the transaction multiple times before we have to re-reserve
0206 log space when we roll the transaction. This ensures that the common
0207 modifications we make only need to reserve log space once.
0208
0209 If the log count for a permanent transaction reaches zero, then it needs to
0210 re-reserve physical space in the log. This is somewhat complex, and requires
0211 an understanding of how the log accounts for space that has been reserved.
0212
0213
0214 Log Space Accounting
0215 ====================
0216
0217 The position in the log is typically referred to as a Log Sequence Number (LSN).
0218 The log is circular, so the positions in the log are defined by the combination
0219 of a cycle number - the number of times the log has been overwritten - and the
0220 offset into the log. A LSN carries the cycle in the upper 32 bits and the
0221 offset in the lower 32 bits. The offset is in units of "basic blocks" (512
0222 bytes). Hence we can do realtively simple LSN based math to keep track of
0223 available space in the log.
0224
0225 Log space accounting is done via a pair of constructs called "grant heads". The
0226 position of the grant heads is an absolute value, so the amount of space
0227 available in the log is defined by the distance between the position of the
0228 grant head and the current log tail. That is, how much space can be
0229 reserved/consumed before the grant heads would fully wrap the log and overtake
0230 the tail position.
0231
0232 The first grant head is the "reserve" head. This tracks the byte count of the
0233 reservations currently held by active transactions. It is a purely in-memory
0234 accounting of the space reservation and, as such, actually tracks byte offsets
0235 into the log rather than basic blocks. Hence it technically isn't using LSNs to
0236 represent the log position, but it is still treated like a split {cycle,offset}
0237 tuple for the purposes of tracking reservation space.
0238
0239 The reserve grant head is used to accurately account for exact transaction
0240 reservations amounts and the exact byte count that modifications actually make
0241 and need to write into the log. The reserve head is used to prevent new
0242 transactions from taking new reservations when the head reaches the current
0243 tail. It will block new reservations in a FIFO queue and as the log tail moves
0244 forward it will wake them in order once sufficient space is available. This FIFO
0245 mechanism ensures no transaction is starved of resources when log space
0246 shortages occur.
0247
0248 The other grant head is the "write" head. Unlike the reserve head, this grant
0249 head contains an LSN and it tracks the physical space usage in the log. While
0250 this might sound like it is accounting the same state as the reserve grant head
0251 - and it mostly does track exactly the same location as the reserve grant head -
0252 there are critical differences in behaviour between them that provides the
0253 forwards progress guarantees that rolling permanent transactions require.
0254
0255 These differences when a permanent transaction is rolled and the internal "log
0256 count" reaches zero and the initial set of unit reservations have been
0257 exhausted. At this point, we still require a log space reservation to continue
0258 the next transaction in the sequeunce, but we have none remaining. We cannot
0259 sleep during the transaction commit process waiting for new log space to become
0260 available, as we may end up on the end of the FIFO queue and the items we have
0261 locked while we sleep could end up pinning the tail of the log before there is
0262 enough free space in the log to fulfil all of the pending reservations and
0263 then wake up transaction commit in progress.
0264
0265 To take a new reservation without sleeping requires us to be able to take a
0266 reservation even if there is no reservation space currently available. That is,
0267 we need to be able to *overcommit* the log reservation space. As has already
0268 been detailed, we cannot overcommit physical log space. However, the reserve
0269 grant head does not track physical space - it only accounts for the amount of
0270 reservations we currently have outstanding. Hence if the reserve head passes
0271 over the tail of the log all it means is that new reservations will be throttled
0272 immediately and remain throttled until the log tail is moved forward far enough
0273 to remove the overcommit and start taking new reservations. In other words, we
0274 can overcommit the reserve head without violating the physical log head and tail
0275 rules.
0276
0277 As a result, permanent transactions only "regrant" reservation space during
0278 xfs_trans_commit() calls, while the physical log space reservation - tracked by
0279 the write head - is then reserved separately by a call to xfs_log_reserve()
0280 after the commit completes. Once the commit completes, we can sleep waiting for
0281 physical log space to be reserved from the write grant head, but only if one
0282 critical rule has been observed::
0283
0284 Code using permanent reservations must always log the items they hold
0285 locked across each transaction they roll in the chain.
0286
0287 "Re-logging" the locked items on every transaction roll ensures that the items
0288 attached to the transaction chain being rolled are always relocated to the
0289 physical head of the log and so do not pin the tail of the log. If a locked item
0290 pins the tail of the log when we sleep on the write reservation, then we will
0291 deadlock the log as we cannot take the locks needed to write back that item and
0292 move the tail of the log forwards to free up write grant space. Re-logging the
0293 locked items avoids this deadlock and guarantees that the log reservation we are
0294 making cannot self-deadlock.
0295
0296 If all rolling transactions obey this rule, then they can all make forwards
0297 progress independently because nothing will block the progress of the log
0298 tail moving forwards and hence ensuring that write grant space is always
0299 (eventually) made available to permanent transactions no matter how many times
0300 they roll.
0301
0302
0303 Re-logging Explained
0304 ====================
0305
0306 XFS allows multiple separate modifications to a single object to be carried in
0307 the log at any given time. This allows the log to avoid needing to flush each
0308 change to disk before recording a new change to the object. XFS does this via a
0309 method called "re-logging". Conceptually, this is quite simple - all it requires
0310 is that any new change to the object is recorded with a *new copy* of all the
0311 existing changes in the new transaction that is written to the log.
0312
0313 That is, if we have a sequence of changes A through to F, and the object was
0314 written to disk after change D, we would see in the log the following series
0315 of transactions, their contents and the log sequence number (LSN) of the
0316 transaction::
0317
0318 Transaction Contents LSN
0319 A A X
0320 B A+B X+n
0321 C A+B+C X+n+m
0322 D A+B+C+D X+n+m+o
0323 <object written to disk>
0324 E E Y (> X+n+m+o)
0325 F E+F Y+p
0326
0327 In other words, each time an object is relogged, the new transaction contains
0328 the aggregation of all the previous changes currently held only in the log.
0329
0330 This relogging technique allows objects to be moved forward in the log so that
0331 an object being relogged does not prevent the tail of the log from ever moving
0332 forward. This can be seen in the table above by the changing (increasing) LSN
0333 of each subsequent transaction, and it's the technique that allows us to
0334 implement long-running, multiple-commit permanent transactions.
0335
0336 A typical example of a rolling transaction is the removal of extents from an
0337 inode which can only be done at a rate of two extents per transaction because
0338 of reservation size limitations. Hence a rolling extent removal transaction
0339 keeps relogging the inode and btree buffers as they get modified in each
0340 removal operation. This keeps them moving forward in the log as the operation
0341 progresses, ensuring that current operation never gets blocked by itself if the
0342 log wraps around.
0343
0344 Hence it can be seen that the relogging operation is fundamental to the correct
0345 working of the XFS journalling subsystem. From the above description, most
0346 people should be able to see why the XFS metadata operations writes so much to
0347 the log - repeated operations to the same objects write the same changes to
0348 the log over and over again. Worse is the fact that objects tend to get
0349 dirtier as they get relogged, so each subsequent transaction is writing more
0350 metadata into the log.
0351
0352 It should now also be obvious how relogging and asynchronous transactions go
0353 hand in hand. That is, transactions don't get written to the physical journal
0354 until either a log buffer is filled (a log buffer can hold multiple
0355 transactions) or a synchronous operation forces the log buffers holding the
0356 transactions to disk. This means that XFS is doing aggregation of transactions
0357 in memory - batching them, if you like - to minimise the impact of the log IO on
0358 transaction throughput.
0359
0360 The limitation on asynchronous transaction throughput is the number and size of
0361 log buffers made available by the log manager. By default there are 8 log
0362 buffers available and the size of each is 32kB - the size can be increased up
0363 to 256kB by use of a mount option.
0364
0365 Effectively, this gives us the maximum bound of outstanding metadata changes
0366 that can be made to the filesystem at any point in time - if all the log
0367 buffers are full and under IO, then no more transactions can be committed until
0368 the current batch completes. It is now common for a single current CPU core to
0369 be to able to issue enough transactions to keep the log buffers full and under
0370 IO permanently. Hence the XFS journalling subsystem can be considered to be IO
0371 bound.
0372
0373 Delayed Logging: Concepts
0374 =========================
0375
0376 The key thing to note about the asynchronous logging combined with the
0377 relogging technique XFS uses is that we can be relogging changed objects
0378 multiple times before they are committed to disk in the log buffers. If we
0379 return to the previous relogging example, it is entirely possible that
0380 transactions A through D are committed to disk in the same log buffer.
0381
0382 That is, a single log buffer may contain multiple copies of the same object,
0383 but only one of those copies needs to be there - the last one "D", as it
0384 contains all the changes from the previous changes. In other words, we have one
0385 necessary copy in the log buffer, and three stale copies that are simply
0386 wasting space. When we are doing repeated operations on the same set of
0387 objects, these "stale objects" can be over 90% of the space used in the log
0388 buffers. It is clear that reducing the number of stale objects written to the
0389 log would greatly reduce the amount of metadata we write to the log, and this
0390 is the fundamental goal of delayed logging.
0391
0392 From a conceptual point of view, XFS is already doing relogging in memory (where
0393 memory == log buffer), only it is doing it extremely inefficiently. It is using
0394 logical to physical formatting to do the relogging because there is no
0395 infrastructure to keep track of logical changes in memory prior to physically
0396 formatting the changes in a transaction to the log buffer. Hence we cannot avoid
0397 accumulating stale objects in the log buffers.
0398
0399 Delayed logging is the name we've given to keeping and tracking transactional
0400 changes to objects in memory outside the log buffer infrastructure. Because of
0401 the relogging concept fundamental to the XFS journalling subsystem, this is
0402 actually relatively easy to do - all the changes to logged items are already
0403 tracked in the current infrastructure. The big problem is how to accumulate
0404 them and get them to the log in a consistent, recoverable manner.
0405 Describing the problems and how they have been solved is the focus of this
0406 document.
0407
0408 One of the key changes that delayed logging makes to the operation of the
0409 journalling subsystem is that it disassociates the amount of outstanding
0410 metadata changes from the size and number of log buffers available. In other
0411 words, instead of there only being a maximum of 2MB of transaction changes not
0412 written to the log at any point in time, there may be a much greater amount
0413 being accumulated in memory. Hence the potential for loss of metadata on a
0414 crash is much greater than for the existing logging mechanism.
0415
0416 It should be noted that this does not change the guarantee that log recovery
0417 will result in a consistent filesystem. What it does mean is that as far as the
0418 recovered filesystem is concerned, there may be many thousands of transactions
0419 that simply did not occur as a result of the crash. This makes it even more
0420 important that applications that care about their data use fsync() where they
0421 need to ensure application level data integrity is maintained.
0422
0423 It should be noted that delayed logging is not an innovative new concept that
0424 warrants rigorous proofs to determine whether it is correct or not. The method
0425 of accumulating changes in memory for some period before writing them to the
0426 log is used effectively in many filesystems including ext3 and ext4. Hence
0427 no time is spent in this document trying to convince the reader that the
0428 concept is sound. Instead it is simply considered a "solved problem" and as
0429 such implementing it in XFS is purely an exercise in software engineering.
0430
0431 The fundamental requirements for delayed logging in XFS are simple:
0432
0433 1. Reduce the amount of metadata written to the log by at least
0434 an order of magnitude.
0435 2. Supply sufficient statistics to validate Requirement #1.
0436 3. Supply sufficient new tracing infrastructure to be able to debug
0437 problems with the new code.
0438 4. No on-disk format change (metadata or log format).
0439 5. Enable and disable with a mount option.
0440 6. No performance regressions for synchronous transaction workloads.
0441
0442 Delayed Logging: Design
0443 =======================
0444
0445 Storing Changes
0446 ---------------
0447
0448 The problem with accumulating changes at a logical level (i.e. just using the
0449 existing log item dirty region tracking) is that when it comes to writing the
0450 changes to the log buffers, we need to ensure that the object we are formatting
0451 is not changing while we do this. This requires locking the object to prevent
0452 concurrent modification. Hence flushing the logical changes to the log would
0453 require us to lock every object, format them, and then unlock them again.
0454
0455 This introduces lots of scope for deadlocks with transactions that are already
0456 running. For example, a transaction has object A locked and modified, but needs
0457 the delayed logging tracking lock to commit the transaction. However, the
0458 flushing thread has the delayed logging tracking lock already held, and is
0459 trying to get the lock on object A to flush it to the log buffer. This appears
0460 to be an unsolvable deadlock condition, and it was solving this problem that
0461 was the barrier to implementing delayed logging for so long.
0462
0463 The solution is relatively simple - it just took a long time to recognise it.
0464 Put simply, the current logging code formats the changes to each item into an
0465 vector array that points to the changed regions in the item. The log write code
0466 simply copies the memory these vectors point to into the log buffer during
0467 transaction commit while the item is locked in the transaction. Instead of
0468 using the log buffer as the destination of the formatting code, we can use an
0469 allocated memory buffer big enough to fit the formatted vector.
0470
0471 If we then copy the vector into the memory buffer and rewrite the vector to
0472 point to the memory buffer rather than the object itself, we now have a copy of
0473 the changes in a format that is compatible with the log buffer writing code.
0474 that does not require us to lock the item to access. This formatting and
0475 rewriting can all be done while the object is locked during transaction commit,
0476 resulting in a vector that is transactionally consistent and can be accessed
0477 without needing to lock the owning item.
0478
0479 Hence we avoid the need to lock items when we need to flush outstanding
0480 asynchronous transactions to the log. The differences between the existing
0481 formatting method and the delayed logging formatting can be seen in the
0482 diagram below.
0483
0484 Current format log vector::
0485
0486 Object +---------------------------------------------+
0487 Vector 1 +----+
0488 Vector 2 +----+
0489 Vector 3 +----------+
0490
0491 After formatting::
0492
0493 Log Buffer +-V1-+-V2-+----V3----+
0494
0495 Delayed logging vector::
0496
0497 Object +---------------------------------------------+
0498 Vector 1 +----+
0499 Vector 2 +----+
0500 Vector 3 +----------+
0501
0502 After formatting::
0503
0504 Memory Buffer +-V1-+-V2-+----V3----+
0505 Vector 1 +----+
0506 Vector 2 +----+
0507 Vector 3 +----------+
0508
0509 The memory buffer and associated vector need to be passed as a single object,
0510 but still need to be associated with the parent object so if the object is
0511 relogged we can replace the current memory buffer with a new memory buffer that
0512 contains the latest changes.
0513
0514 The reason for keeping the vector around after we've formatted the memory
0515 buffer is to support splitting vectors across log buffer boundaries correctly.
0516 If we don't keep the vector around, we do not know where the region boundaries
0517 are in the item, so we'd need a new encapsulation method for regions in the log
0518 buffer writing (i.e. double encapsulation). This would be an on-disk format
0519 change and as such is not desirable. It also means we'd have to write the log
0520 region headers in the formatting stage, which is problematic as there is per
0521 region state that needs to be placed into the headers during the log write.
0522
0523 Hence we need to keep the vector, but by attaching the memory buffer to it and
0524 rewriting the vector addresses to point at the memory buffer we end up with a
0525 self-describing object that can be passed to the log buffer write code to be
0526 handled in exactly the same manner as the existing log vectors are handled.
0527 Hence we avoid needing a new on-disk format to handle items that have been
0528 relogged in memory.
0529
0530
0531 Tracking Changes
0532 ----------------
0533
0534 Now that we can record transactional changes in memory in a form that allows
0535 them to be used without limitations, we need to be able to track and accumulate
0536 them so that they can be written to the log at some later point in time. The
0537 log item is the natural place to store this vector and buffer, and also makes sense
0538 to be the object that is used to track committed objects as it will always
0539 exist once the object has been included in a transaction.
0540
0541 The log item is already used to track the log items that have been written to
0542 the log but not yet written to disk. Such log items are considered "active"
0543 and as such are stored in the Active Item List (AIL) which is a LSN-ordered
0544 double linked list. Items are inserted into this list during log buffer IO
0545 completion, after which they are unpinned and can be written to disk. An object
0546 that is in the AIL can be relogged, which causes the object to be pinned again
0547 and then moved forward in the AIL when the log buffer IO completes for that
0548 transaction.
0549
0550 Essentially, this shows that an item that is in the AIL can still be modified
0551 and relogged, so any tracking must be separate to the AIL infrastructure. As
0552 such, we cannot reuse the AIL list pointers for tracking committed items, nor
0553 can we store state in any field that is protected by the AIL lock. Hence the
0554 committed item tracking needs it's own locks, lists and state fields in the log
0555 item.
0556
0557 Similar to the AIL, tracking of committed items is done through a new list
0558 called the Committed Item List (CIL). The list tracks log items that have been
0559 committed and have formatted memory buffers attached to them. It tracks objects
0560 in transaction commit order, so when an object is relogged it is removed from
0561 it's place in the list and re-inserted at the tail. This is entirely arbitrary
0562 and done to make it easy for debugging - the last items in the list are the
0563 ones that are most recently modified. Ordering of the CIL is not necessary for
0564 transactional integrity (as discussed in the next section) so the ordering is
0565 done for convenience/sanity of the developers.
0566
0567
0568 Delayed Logging: Checkpoints
0569 ----------------------------
0570
0571 When we have a log synchronisation event, commonly known as a "log force",
0572 all the items in the CIL must be written into the log via the log buffers.
0573 We need to write these items in the order that they exist in the CIL, and they
0574 need to be written as an atomic transaction. The need for all the objects to be
0575 written as an atomic transaction comes from the requirements of relogging and
0576 log replay - all the changes in all the objects in a given transaction must
0577 either be completely replayed during log recovery, or not replayed at all. If
0578 a transaction is not replayed because it is not complete in the log, then
0579 no later transactions should be replayed, either.
0580
0581 To fulfill this requirement, we need to write the entire CIL in a single log
0582 transaction. Fortunately, the XFS log code has no fixed limit on the size of a
0583 transaction, nor does the log replay code. The only fundamental limit is that
0584 the transaction cannot be larger than just under half the size of the log. The
0585 reason for this limit is that to find the head and tail of the log, there must
0586 be at least one complete transaction in the log at any given time. If a
0587 transaction is larger than half the log, then there is the possibility that a
0588 crash during the write of a such a transaction could partially overwrite the
0589 only complete previous transaction in the log. This will result in a recovery
0590 failure and an inconsistent filesystem and hence we must enforce the maximum
0591 size of a checkpoint to be slightly less than a half the log.
0592
0593 Apart from this size requirement, a checkpoint transaction looks no different
0594 to any other transaction - it contains a transaction header, a series of
0595 formatted log items and a commit record at the tail. From a recovery
0596 perspective, the checkpoint transaction is also no different - just a lot
0597 bigger with a lot more items in it. The worst case effect of this is that we
0598 might need to tune the recovery transaction object hash size.
0599
0600 Because the checkpoint is just another transaction and all the changes to log
0601 items are stored as log vectors, we can use the existing log buffer writing
0602 code to write the changes into the log. To do this efficiently, we need to
0603 minimise the time we hold the CIL locked while writing the checkpoint
0604 transaction. The current log write code enables us to do this easily with the
0605 way it separates the writing of the transaction contents (the log vectors) from
0606 the transaction commit record, but tracking this requires us to have a
0607 per-checkpoint context that travels through the log write process through to
0608 checkpoint completion.
0609
0610 Hence a checkpoint has a context that tracks the state of the current
0611 checkpoint from initiation to checkpoint completion. A new context is initiated
0612 at the same time a checkpoint transaction is started. That is, when we remove
0613 all the current items from the CIL during a checkpoint operation, we move all
0614 those changes into the current checkpoint context. We then initialise a new
0615 context and attach that to the CIL for aggregation of new transactions.
0616
0617 This allows us to unlock the CIL immediately after transfer of all the
0618 committed items and effectively allow new transactions to be issued while we
0619 are formatting the checkpoint into the log. It also allows concurrent
0620 checkpoints to be written into the log buffers in the case of log force heavy
0621 workloads, just like the existing transaction commit code does. This, however,
0622 requires that we strictly order the commit records in the log so that
0623 checkpoint sequence order is maintained during log replay.
0624
0625 To ensure that we can be writing an item into a checkpoint transaction at
0626 the same time another transaction modifies the item and inserts the log item
0627 into the new CIL, then checkpoint transaction commit code cannot use log items
0628 to store the list of log vectors that need to be written into the transaction.
0629 Hence log vectors need to be able to be chained together to allow them to be
0630 detached from the log items. That is, when the CIL is flushed the memory
0631 buffer and log vector attached to each log item needs to be attached to the
0632 checkpoint context so that the log item can be released. In diagrammatic form,
0633 the CIL would look like this before the flush::
0634
0635 CIL Head
0636 |
0637 V
0638 Log Item <-> log vector 1 -> memory buffer
0639 | -> vector array
0640 V
0641 Log Item <-> log vector 2 -> memory buffer
0642 | -> vector array
0643 V
0644 ......
0645 |
0646 V
0647 Log Item <-> log vector N-1 -> memory buffer
0648 | -> vector array
0649 V
0650 Log Item <-> log vector N -> memory buffer
0651 -> vector array
0652
0653 And after the flush the CIL head is empty, and the checkpoint context log
0654 vector list would look like::
0655
0656 Checkpoint Context
0657 |
0658 V
0659 log vector 1 -> memory buffer
0660 | -> vector array
0661 | -> Log Item
0662 V
0663 log vector 2 -> memory buffer
0664 | -> vector array
0665 | -> Log Item
0666 V
0667 ......
0668 |
0669 V
0670 log vector N-1 -> memory buffer
0671 | -> vector array
0672 | -> Log Item
0673 V
0674 log vector N -> memory buffer
0675 -> vector array
0676 -> Log Item
0677
0678 Once this transfer is done, the CIL can be unlocked and new transactions can
0679 start, while the checkpoint flush code works over the log vector chain to
0680 commit the checkpoint.
0681
0682 Once the checkpoint is written into the log buffers, the checkpoint context is
0683 attached to the log buffer that the commit record was written to along with a
0684 completion callback. Log IO completion will call that callback, which can then
0685 run transaction committed processing for the log items (i.e. insert into AIL
0686 and unpin) in the log vector chain and then free the log vector chain and
0687 checkpoint context.
0688
0689 Discussion Point: I am uncertain as to whether the log item is the most
0690 efficient way to track vectors, even though it seems like the natural way to do
0691 it. The fact that we walk the log items (in the CIL) just to chain the log
0692 vectors and break the link between the log item and the log vector means that
0693 we take a cache line hit for the log item list modification, then another for
0694 the log vector chaining. If we track by the log vectors, then we only need to
0695 break the link between the log item and the log vector, which means we should
0696 dirty only the log item cachelines. Normally I wouldn't be concerned about one
0697 vs two dirty cachelines except for the fact I've seen upwards of 80,000 log
0698 vectors in one checkpoint transaction. I'd guess this is a "measure and
0699 compare" situation that can be done after a working and reviewed implementation
0700 is in the dev tree....
0701
0702 Delayed Logging: Checkpoint Sequencing
0703 --------------------------------------
0704
0705 One of the key aspects of the XFS transaction subsystem is that it tags
0706 committed transactions with the log sequence number of the transaction commit.
0707 This allows transactions to be issued asynchronously even though there may be
0708 future operations that cannot be completed until that transaction is fully
0709 committed to the log. In the rare case that a dependent operation occurs (e.g.
0710 re-using a freed metadata extent for a data extent), a special, optimised log
0711 force can be issued to force the dependent transaction to disk immediately.
0712
0713 To do this, transactions need to record the LSN of the commit record of the
0714 transaction. This LSN comes directly from the log buffer the transaction is
0715 written into. While this works just fine for the existing transaction
0716 mechanism, it does not work for delayed logging because transactions are not
0717 written directly into the log buffers. Hence some other method of sequencing
0718 transactions is required.
0719
0720 As discussed in the checkpoint section, delayed logging uses per-checkpoint
0721 contexts, and as such it is simple to assign a sequence number to each
0722 checkpoint. Because the switching of checkpoint contexts must be done
0723 atomically, it is simple to ensure that each new context has a monotonically
0724 increasing sequence number assigned to it without the need for an external
0725 atomic counter - we can just take the current context sequence number and add
0726 one to it for the new context.
0727
0728 Then, instead of assigning a log buffer LSN to the transaction commit LSN
0729 during the commit, we can assign the current checkpoint sequence. This allows
0730 operations that track transactions that have not yet completed know what
0731 checkpoint sequence needs to be committed before they can continue. As a
0732 result, the code that forces the log to a specific LSN now needs to ensure that
0733 the log forces to a specific checkpoint.
0734
0735 To ensure that we can do this, we need to track all the checkpoint contexts
0736 that are currently committing to the log. When we flush a checkpoint, the
0737 context gets added to a "committing" list which can be searched. When a
0738 checkpoint commit completes, it is removed from the committing list. Because
0739 the checkpoint context records the LSN of the commit record for the checkpoint,
0740 we can also wait on the log buffer that contains the commit record, thereby
0741 using the existing log force mechanisms to execute synchronous forces.
0742
0743 It should be noted that the synchronous forces may need to be extended with
0744 mitigation algorithms similar to the current log buffer code to allow
0745 aggregation of multiple synchronous transactions if there are already
0746 synchronous transactions being flushed. Investigation of the performance of the
0747 current design is needed before making any decisions here.
0748
0749 The main concern with log forces is to ensure that all the previous checkpoints
0750 are also committed to disk before the one we need to wait for. Therefore we
0751 need to check that all the prior contexts in the committing list are also
0752 complete before waiting on the one we need to complete. We do this
0753 synchronisation in the log force code so that we don't need to wait anywhere
0754 else for such serialisation - it only matters when we do a log force.
0755
0756 The only remaining complexity is that a log force now also has to handle the
0757 case where the forcing sequence number is the same as the current context. That
0758 is, we need to flush the CIL and potentially wait for it to complete. This is a
0759 simple addition to the existing log forcing code to check the sequence numbers
0760 and push if required. Indeed, placing the current sequence checkpoint flush in
0761 the log force code enables the current mechanism for issuing synchronous
0762 transactions to remain untouched (i.e. commit an asynchronous transaction, then
0763 force the log at the LSN of that transaction) and so the higher level code
0764 behaves the same regardless of whether delayed logging is being used or not.
0765
0766 Delayed Logging: Checkpoint Log Space Accounting
0767 ------------------------------------------------
0768
0769 The big issue for a checkpoint transaction is the log space reservation for the
0770 transaction. We don't know how big a checkpoint transaction is going to be
0771 ahead of time, nor how many log buffers it will take to write out, nor the
0772 number of split log vector regions are going to be used. We can track the
0773 amount of log space required as we add items to the commit item list, but we
0774 still need to reserve the space in the log for the checkpoint.
0775
0776 A typical transaction reserves enough space in the log for the worst case space
0777 usage of the transaction. The reservation accounts for log record headers,
0778 transaction and region headers, headers for split regions, buffer tail padding,
0779 etc. as well as the actual space for all the changed metadata in the
0780 transaction. While some of this is fixed overhead, much of it is dependent on
0781 the size of the transaction and the number of regions being logged (the number
0782 of log vectors in the transaction).
0783
0784 An example of the differences would be logging directory changes versus logging
0785 inode changes. If you modify lots of inode cores (e.g. ``chmod -R g+w *``), then
0786 there are lots of transactions that only contain an inode core and an inode log
0787 format structure. That is, two vectors totaling roughly 150 bytes. If we modify
0788 10,000 inodes, we have about 1.5MB of metadata to write in 20,000 vectors. Each
0789 vector is 12 bytes, so the total to be logged is approximately 1.75MB. In
0790 comparison, if we are logging full directory buffers, they are typically 4KB
0791 each, so we in 1.5MB of directory buffers we'd have roughly 400 buffers and a
0792 buffer format structure for each buffer - roughly 800 vectors or 1.51MB total
0793 space. From this, it should be obvious that a static log space reservation is
0794 not particularly flexible and is difficult to select the "optimal value" for
0795 all workloads.
0796
0797 Further, if we are going to use a static reservation, which bit of the entire
0798 reservation does it cover? We account for space used by the transaction
0799 reservation by tracking the space currently used by the object in the CIL and
0800 then calculating the increase or decrease in space used as the object is
0801 relogged. This allows for a checkpoint reservation to only have to account for
0802 log buffer metadata used such as log header records.
0803
0804 However, even using a static reservation for just the log metadata is
0805 problematic. Typically log record headers use at least 16KB of log space per
0806 1MB of log space consumed (512 bytes per 32k) and the reservation needs to be
0807 large enough to handle arbitrary sized checkpoint transactions. This
0808 reservation needs to be made before the checkpoint is started, and we need to
0809 be able to reserve the space without sleeping. For a 8MB checkpoint, we need a
0810 reservation of around 150KB, which is a non-trivial amount of space.
0811
0812 A static reservation needs to manipulate the log grant counters - we can take a
0813 permanent reservation on the space, but we still need to make sure we refresh
0814 the write reservation (the actual space available to the transaction) after
0815 every checkpoint transaction completion. Unfortunately, if this space is not
0816 available when required, then the regrant code will sleep waiting for it.
0817
0818 The problem with this is that it can lead to deadlocks as we may need to commit
0819 checkpoints to be able to free up log space (refer back to the description of
0820 rolling transactions for an example of this). Hence we *must* always have
0821 space available in the log if we are to use static reservations, and that is
0822 very difficult and complex to arrange. It is possible to do, but there is a
0823 simpler way.
0824
0825 The simpler way of doing this is tracking the entire log space used by the
0826 items in the CIL and using this to dynamically calculate the amount of log
0827 space required by the log metadata. If this log metadata space changes as a
0828 result of a transaction commit inserting a new memory buffer into the CIL, then
0829 the difference in space required is removed from the transaction that causes
0830 the change. Transactions at this level will *always* have enough space
0831 available in their reservation for this as they have already reserved the
0832 maximal amount of log metadata space they require, and such a delta reservation
0833 will always be less than or equal to the maximal amount in the reservation.
0834
0835 Hence we can grow the checkpoint transaction reservation dynamically as items
0836 are added to the CIL and avoid the need for reserving and regranting log space
0837 up front. This avoids deadlocks and removes a blocking point from the
0838 checkpoint flush code.
0839
0840 As mentioned early, transactions can't grow to more than half the size of the
0841 log. Hence as part of the reservation growing, we need to also check the size
0842 of the reservation against the maximum allowed transaction size. If we reach
0843 the maximum threshold, we need to push the CIL to the log. This is effectively
0844 a "background flush" and is done on demand. This is identical to
0845 a CIL push triggered by a log force, only that there is no waiting for the
0846 checkpoint commit to complete. This background push is checked and executed by
0847 transaction commit code.
0848
0849 If the transaction subsystem goes idle while we still have items in the CIL,
0850 they will be flushed by the periodic log force issued by the xfssyncd. This log
0851 force will push the CIL to disk, and if the transaction subsystem stays idle,
0852 allow the idle log to be covered (effectively marked clean) in exactly the same
0853 manner that is done for the existing logging method. A discussion point is
0854 whether this log force needs to be done more frequently than the current rate
0855 which is once every 30s.
0856
0857
0858 Delayed Logging: Log Item Pinning
0859 ---------------------------------
0860
0861 Currently log items are pinned during transaction commit while the items are
0862 still locked. This happens just after the items are formatted, though it could
0863 be done any time before the items are unlocked. The result of this mechanism is
0864 that items get pinned once for every transaction that is committed to the log
0865 buffers. Hence items that are relogged in the log buffers will have a pin count
0866 for every outstanding transaction they were dirtied in. When each of these
0867 transactions is completed, they will unpin the item once. As a result, the item
0868 only becomes unpinned when all the transactions complete and there are no
0869 pending transactions. Thus the pinning and unpinning of a log item is symmetric
0870 as there is a 1:1 relationship with transaction commit and log item completion.
0871
0872 For delayed logging, however, we have an asymmetric transaction commit to
0873 completion relationship. Every time an object is relogged in the CIL it goes
0874 through the commit process without a corresponding completion being registered.
0875 That is, we now have a many-to-one relationship between transaction commit and
0876 log item completion. The result of this is that pinning and unpinning of the
0877 log items becomes unbalanced if we retain the "pin on transaction commit, unpin
0878 on transaction completion" model.
0879
0880 To keep pin/unpin symmetry, the algorithm needs to change to a "pin on
0881 insertion into the CIL, unpin on checkpoint completion". In other words, the
0882 pinning and unpinning becomes symmetric around a checkpoint context. We have to
0883 pin the object the first time it is inserted into the CIL - if it is already in
0884 the CIL during a transaction commit, then we do not pin it again. Because there
0885 can be multiple outstanding checkpoint contexts, we can still see elevated pin
0886 counts, but as each checkpoint completes the pin count will retain the correct
0887 value according to it's context.
0888
0889 Just to make matters more slightly more complex, this checkpoint level context
0890 for the pin count means that the pinning of an item must take place under the
0891 CIL commit/flush lock. If we pin the object outside this lock, we cannot
0892 guarantee which context the pin count is associated with. This is because of
0893 the fact pinning the item is dependent on whether the item is present in the
0894 current CIL or not. If we don't pin the CIL first before we check and pin the
0895 object, we have a race with CIL being flushed between the check and the pin
0896 (or not pinning, as the case may be). Hence we must hold the CIL flush/commit
0897 lock to guarantee that we pin the items correctly.
0898
0899 Delayed Logging: Concurrent Scalability
0900 ---------------------------------------
0901
0902 A fundamental requirement for the CIL is that accesses through transaction
0903 commits must scale to many concurrent commits. The current transaction commit
0904 code does not break down even when there are transactions coming from 2048
0905 processors at once. The current transaction code does not go any faster than if
0906 there was only one CPU using it, but it does not slow down either.
0907
0908 As a result, the delayed logging transaction commit code needs to be designed
0909 for concurrency from the ground up. It is obvious that there are serialisation
0910 points in the design - the three important ones are:
0911
0912 1. Locking out new transaction commits while flushing the CIL
0913 2. Adding items to the CIL and updating item space accounting
0914 3. Checkpoint commit ordering
0915
0916 Looking at the transaction commit and CIL flushing interactions, it is clear
0917 that we have a many-to-one interaction here. That is, the only restriction on
0918 the number of concurrent transactions that can be trying to commit at once is
0919 the amount of space available in the log for their reservations. The practical
0920 limit here is in the order of several hundred concurrent transactions for a
0921 128MB log, which means that it is generally one per CPU in a machine.
0922
0923 The amount of time a transaction commit needs to hold out a flush is a
0924 relatively long period of time - the pinning of log items needs to be done
0925 while we are holding out a CIL flush, so at the moment that means it is held
0926 across the formatting of the objects into memory buffers (i.e. while memcpy()s
0927 are in progress). Ultimately a two pass algorithm where the formatting is done
0928 separately to the pinning of objects could be used to reduce the hold time of
0929 the transaction commit side.
0930
0931 Because of the number of potential transaction commit side holders, the lock
0932 really needs to be a sleeping lock - if the CIL flush takes the lock, we do not
0933 want every other CPU in the machine spinning on the CIL lock. Given that
0934 flushing the CIL could involve walking a list of tens of thousands of log
0935 items, it will get held for a significant time and so spin contention is a
0936 significant concern. Preventing lots of CPUs spinning doing nothing is the
0937 main reason for choosing a sleeping lock even though nothing in either the
0938 transaction commit or CIL flush side sleeps with the lock held.
0939
0940 It should also be noted that CIL flushing is also a relatively rare operation
0941 compared to transaction commit for asynchronous transaction workloads - only
0942 time will tell if using a read-write semaphore for exclusion will limit
0943 transaction commit concurrency due to cache line bouncing of the lock on the
0944 read side.
0945
0946 The second serialisation point is on the transaction commit side where items
0947 are inserted into the CIL. Because transactions can enter this code
0948 concurrently, the CIL needs to be protected separately from the above
0949 commit/flush exclusion. It also needs to be an exclusive lock but it is only
0950 held for a very short time and so a spin lock is appropriate here. It is
0951 possible that this lock will become a contention point, but given the short
0952 hold time once per transaction I think that contention is unlikely.
0953
0954 The final serialisation point is the checkpoint commit record ordering code
0955 that is run as part of the checkpoint commit and log force sequencing. The code
0956 path that triggers a CIL flush (i.e. whatever triggers the log force) will enter
0957 an ordering loop after writing all the log vectors into the log buffers but
0958 before writing the commit record. This loop walks the list of committing
0959 checkpoints and needs to block waiting for checkpoints to complete their commit
0960 record write. As a result it needs a lock and a wait variable. Log force
0961 sequencing also requires the same lock, list walk, and blocking mechanism to
0962 ensure completion of checkpoints.
0963
0964 These two sequencing operations can use the mechanism even though the
0965 events they are waiting for are different. The checkpoint commit record
0966 sequencing needs to wait until checkpoint contexts contain a commit LSN
0967 (obtained through completion of a commit record write) while log force
0968 sequencing needs to wait until previous checkpoint contexts are removed from
0969 the committing list (i.e. they've completed). A simple wait variable and
0970 broadcast wakeups (thundering herds) has been used to implement these two
0971 serialisation queues. They use the same lock as the CIL, too. If we see too
0972 much contention on the CIL lock, or too many context switches as a result of
0973 the broadcast wakeups these operations can be put under a new spinlock and
0974 given separate wait lists to reduce lock contention and the number of processes
0975 woken by the wrong event.
0976
0977
0978 Lifecycle Changes
0979 -----------------
0980
0981 The existing log item life cycle is as follows::
0982
0983 1. Transaction allocate
0984 2. Transaction reserve
0985 3. Lock item
0986 4. Join item to transaction
0987 If not already attached,
0988 Allocate log item
0989 Attach log item to owner item
0990 Attach log item to transaction
0991 5. Modify item
0992 Record modifications in log item
0993 6. Transaction commit
0994 Pin item in memory
0995 Format item into log buffer
0996 Write commit LSN into transaction
0997 Unlock item
0998 Attach transaction to log buffer
0999
1000 <log buffer IO dispatched>
1001 <log buffer IO completes>
1002
1003 7. Transaction completion
1004 Mark log item committed
1005 Insert log item into AIL
1006 Write commit LSN into log item
1007 Unpin log item
1008 8. AIL traversal
1009 Lock item
1010 Mark log item clean
1011 Flush item to disk
1012
1013 <item IO completion>
1014
1015 9. Log item removed from AIL
1016 Moves log tail
1017 Item unlocked
1018
1019 Essentially, steps 1-6 operate independently from step 7, which is also
1020 independent of steps 8-9. An item can be locked in steps 1-6 or steps 8-9
1021 at the same time step 7 is occurring, but only steps 1-6 or 8-9 can occur
1022 at the same time. If the log item is in the AIL or between steps 6 and 7
1023 and steps 1-6 are re-entered, then the item is relogged. Only when steps 8-9
1024 are entered and completed is the object considered clean.
1025
1026 With delayed logging, there are new steps inserted into the life cycle::
1027
1028 1. Transaction allocate
1029 2. Transaction reserve
1030 3. Lock item
1031 4. Join item to transaction
1032 If not already attached,
1033 Allocate log item
1034 Attach log item to owner item
1035 Attach log item to transaction
1036 5. Modify item
1037 Record modifications in log item
1038 6. Transaction commit
1039 Pin item in memory if not pinned in CIL
1040 Format item into log vector + buffer
1041 Attach log vector and buffer to log item
1042 Insert log item into CIL
1043 Write CIL context sequence into transaction
1044 Unlock item
1045
1046 <next log force>
1047
1048 7. CIL push
1049 lock CIL flush
1050 Chain log vectors and buffers together
1051 Remove items from CIL
1052 unlock CIL flush
1053 write log vectors into log
1054 sequence commit records
1055 attach checkpoint context to log buffer
1056
1057 <log buffer IO dispatched>
1058 <log buffer IO completes>
1059
1060 8. Checkpoint completion
1061 Mark log item committed
1062 Insert item into AIL
1063 Write commit LSN into log item
1064 Unpin log item
1065 9. AIL traversal
1066 Lock item
1067 Mark log item clean
1068 Flush item to disk
1069 <item IO completion>
1070 10. Log item removed from AIL
1071 Moves log tail
1072 Item unlocked
1073
1074 From this, it can be seen that the only life cycle differences between the two
1075 logging methods are in the middle of the life cycle - they still have the same
1076 beginning and end and execution constraints. The only differences are in the
1077 committing of the log items to the log itself and the completion processing.
1078 Hence delayed logging should not introduce any constraints on log item
1079 behaviour, allocation or freeing that don't already exist.
1080
1081 As a result of this zero-impact "insertion" of delayed logging infrastructure
1082 and the design of the internal structures to avoid on disk format changes, we
1083 can basically switch between delayed logging and the existing mechanism with a
1084 mount option. Fundamentally, there is no reason why the log manager would not
1085 be able to swap methods automatically and transparently depending on load
1086 characteristics, but this should not be necessary if delayed logging works as
1087 designed.