[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