<html><head><meta name="qrichtext" content="1" /></head><body style="font-size:11pt;font-family:Monospace">
<p>Here is a prototype of block redirect for btree nodes.  It has not been tested at all, it just compiles (patch attached).  It has one severe deficiency: it can't handle redirecting the root of a btree because the btree cursor does not know where the btree root is cached.</p>
<p></p>
<p>To place this in conext, redirect on write comes in two distinctly different flavors:</p>
<p></p>
<p>  Redirect a data extent in file page cache:</p>
<p>    - Logically mapped</p>
<p>        - no need to relocate data blocks in cache</p>
<p>        - just need to update pointer in parent (dleaf)</p>
<p>    - Handled by map_region</p>
<p></p>
<p>  Redirect a btree node in volume cache:</p>
<p>    - Physically mapped</p>
<p>        - need to move data to a new buffer and release old</p>
<p>    - Handled various functions in btree.c</p>
<p></p>
<p>In either case, we are releasing some data blocks and allocating new ones, because the contents of cache has changed and we need to place it on disk without overwriting any part of the existing, stable filesystem image.</p>
<p></p>
<p>int redirect(struct cursor *cursor, unsigned level)</p>
<p>{</p>
<p>       struct btree *btree = cursor->btree;</p>
<p>       struct sb *sb = btree->sb;</p>
<p>       struct buffer_head *buffer = cursor->path[level].buffer;</p>
<p>       assert(buffer->state >= BUFFER_DIRTY);</p>
<p>       block_t child;</p>
<p>       int err = balloc(sb, 1, &child);</p>
<p>       if (err)</p>
<p>               return err;</p>
<p>       struct buffer_head *shadow = blockget(mapping(sb->volmap), child);</p>
<p>       if (!shadow)</p>
<p>               return -ENOMEM; // ERR_PTR me!!!</p>
<p>       memcpy(bufdata(shadow), bufdata(buffer), sb->blocksize);</p>
<p>       cursor->path[level].buffer = shadow;</p>
<p>       cursor->path[level].next += bufdata(shadow) - bufdata(buffer);</p>
<p>       brelse(buffer);</p>
<p></p>
<p>       // what about redirecting root??? cursor needs to know where root is.</p>
<p>       struct index_entry *entry = cursor->path[level - 1].next - 1;</p>
<p>       block_t parent = bufindex(cursor->path[level - 1].buffer);</p>
<p>       log_alloc(sb, from_be_u64(entry->block), 1, 0);</p>
<p>       log_alloc(sb, child, 1, 1); // implied by update???</p>
<p>       log_update(sb, child, parent, from_be_u64(entry->key));</p>
<p>       entry->block = to_be_u64(child);</p>
<p>       // add old block to free-at-end-of-delta list</p>
<p>       // set_buffer_state(shadow, sb->delta & (BUFFER_DIRTY_STATES - 1));</p>
<p>       return 0;</p>
<p>}</p>
<p></p>
</body></html>