[Tux3] Draft kernel version of buffer fork
Daniel Phillips
phillips at phunq.net
Tue Jan 13 22:36:18 PST 2009
Here is a version that compiles, with stubs filled in, and I should
probably remember to compile after editing code in an email.
Daniel
unsigned bufdelta(struct buffer_head *buffer)
{
return (buffer->b_state >> BH_PrivateStart) & DELTA_MASK;
}
void set_bufdelta(struct buffer_head *buffer, unsigned delta)
{
buffer->b_state = (buffer->b_state & ~DELTA_STATE_MASK) | (delta << BH_PrivateStart);
}
struct list_head delta_list[4];
int fork_buffer(struct buffer_head *buffer)
{
struct page *oldpage = buffer->b_page;
struct address_space *mapping = oldpage->mapping;
struct inode *inode = mapping->host;
struct sb *sb = tux_sb(inode->i_sb);
tuxnode_t *tuxnode = tux_inode(inode);
unsigned newdelta = sb->delta & DELTA_MASK;
struct list_head *inode_dirty_list = &tuxnode->dirty;
unsigned blocksize = sb->blocksize;
// Use read_mapping_page to bring the full page uptodate
// Take the page lock (protects the buffer list)
lock_page(oldpage);
while (!PageUptodate(oldpage)) {
unlock_page(oldpage);
oldpage = read_mapping_page(mapping, oldpage->index, NULL);
lock_page(oldpage);
}
// The fork happened while waiting for the page lock?
if (bufdelta(buffer) == newdelta) {
unlock_page(oldpage);
return 0;
}
// Allocate a new page and put buffers on it
struct page *newpage = alloc_pages(0, GFP_KERNEL);
create_empty_buffers(newpage, blocksize, 0);
// Copy page data
memcpy(page_address(newpage), page_address(oldpage), PAGE_CACHE_SIZE);
// Walk the two buffer lists together
struct buffer_head *oldbuf = (void *)oldpage->private, *oldlist = oldbuf;
struct buffer_head *newbuf = (void *)newpage->private;
do {
newbuf->b_state = oldbuf->b_state & (BH_Uptodate | BH_Dirty);
newbuf->b_page = oldpage;
oldbuf->b_page = newpage;
if (buffer_dirty(oldbuf)) {
unsigned olddelta = bufdelta(oldbuf);
assert(olddelta != newdelta);
// Set old buffer dirty in the current delta
list_move_tail(&oldbuf->b_assoc_buffers, inode_dirty_list);
set_bufdelta(oldbuf, newdelta);
// Add new buffer to the earlier delta list
list_move_tail(&newbuf->b_assoc_buffers, delta_list + newdelta);
set_bufdelta(newbuf, olddelta);
}
oldbuf = oldbuf->b_this_page;
newbuf = newbuf->b_this_page;
} while (oldbuf != oldlist);
// Swap the page buffer lists
oldpage->private = newpage->private;
newpage->private = (unsigned long)oldlist;
newpage->index = oldpage->index;
// Replace page in radix tree
spin_lock_irq(&mapping->tree_lock);
void **slot = radix_tree_lookup_slot(&mapping->page_tree, oldpage->index);
radix_tree_replace_slot(slot, newpage);
spin_unlock_irq(&mapping->tree_lock);
get_page(newpage);
put_page(oldpage);
unlock_page(oldpage);
return 0;
}
_______________________________________________
Tux3 mailing list
Tux3 at tux3.org
http://mailman.tux3.org/cgi-bin/mailman/listinfo/tux3
More information about the Tux3
mailing list