Commit 96150606 authored by Prasad Joshi's avatar Prasad Joshi
Browse files

logfs: update page reference count for pined pages



LogFS sets PG_private flag to indicate a pined page. We assumed that
marking a page as private is enough to ensure its existence. But
instead it is necessary to hold a reference count to the page.

The change resolves the following BUG

BUG: Bad page state in process flush-253:16  pfn:6a6d0
page flags: 0x100000000000808(uptodate|private)

Suggested-and-Acked-by: default avatarJoern Engel <joern@logfs.org>
Signed-off-by: default avatarPrasad Joshi <prasadjoshi.linux@gmail.com>
parent f423fc62
Loading
Loading
Loading
Loading
+22 −7
Original line number Diff line number Diff line
@@ -560,8 +560,13 @@ static void inode_free_block(struct super_block *sb, struct logfs_block *block)
static void indirect_free_block(struct super_block *sb,
		struct logfs_block *block)
{
	ClearPagePrivate(block->page);
	block->page->private = 0;
	struct page *page = block->page;

	if (PagePrivate(page)) {
		ClearPagePrivate(page);
		page_cache_release(page);
		set_page_private(page, 0);
	}
	__free_block(sb, block);
}

@@ -650,8 +655,11 @@ static void alloc_data_block(struct inode *inode, struct page *page)
	logfs_unpack_index(page->index, &bix, &level);
	block = __alloc_block(inode->i_sb, inode->i_ino, bix, level);
	block->page = page;

	SetPagePrivate(page);
	page->private = (unsigned long)block;
	page_cache_get(page);
	set_page_private(page, (unsigned long) block);

	block->ops = &indirect_block_ops;
}

@@ -1901,8 +1909,11 @@ static void move_page_to_inode(struct inode *inode, struct page *page)
	li->li_block = block;

	block->page = NULL;
	page->private = 0;
	if (PagePrivate(page)) {
		ClearPagePrivate(page);
		page_cache_release(page);
		set_page_private(page, 0);
	}
}

static void move_inode_to_page(struct page *page, struct inode *inode)
@@ -1918,8 +1929,12 @@ static void move_inode_to_page(struct page *page, struct inode *inode)
	BUG_ON(PagePrivate(page));
	block->ops = &indirect_block_ops;
	block->page = page;
	page->private = (unsigned long)block;

	if (!PagePrivate(page)) {
		SetPagePrivate(page);
		page_cache_get(page);
		set_page_private(page, (unsigned long) block);
	}

	block->inode = NULL;
	li->li_block = NULL;
+29 −8
Original line number Diff line number Diff line
@@ -86,7 +86,11 @@ int __logfs_buf_write(struct logfs_area *area, u64 ofs, void *buf, size_t len,
		BUG_ON(!page); /* FIXME: reserve a pool */
		SetPageUptodate(page);
		memcpy(page_address(page) + offset, buf, copylen);

		if (!PagePrivate(page)) {
			SetPagePrivate(page);
			page_cache_get(page);
		}
		page_cache_release(page);

		buf += copylen;
@@ -110,7 +114,10 @@ static void pad_partial_page(struct logfs_area *area)
		page = get_mapping_page(sb, index, 0);
		BUG_ON(!page); /* FIXME: reserve a pool */
		memset(page_address(page) + offset, 0xff, len);
		if (!PagePrivate(page)) {
			SetPagePrivate(page);
			page_cache_get(page);
		}
		page_cache_release(page);
	}
}
@@ -130,7 +137,10 @@ static void pad_full_pages(struct logfs_area *area)
		BUG_ON(!page); /* FIXME: reserve a pool */
		SetPageUptodate(page);
		memset(page_address(page), 0xff, PAGE_CACHE_SIZE);
		if (!PagePrivate(page)) {
			SetPagePrivate(page);
			page_cache_get(page);
		}
		page_cache_release(page);
		index++;
		no_indizes--;
@@ -485,8 +495,12 @@ static void move_btree_to_page(struct inode *inode, struct page *page,
		mempool_free(item, super->s_alias_pool);
	}
	block->page = page;

	if (!PagePrivate(page)) {
		SetPagePrivate(page);
	page->private = (unsigned long)block;
		page_cache_get(page);
		set_page_private(page, (unsigned long) block);
	}
	block->ops = &indirect_block_ops;
	initialize_block_counters(page, block, data, 0);
}
@@ -536,8 +550,12 @@ void move_page_to_btree(struct page *page)
		list_add(&item->list, &block->item_list);
	}
	block->page = NULL;

	if (PagePrivate(page)) {
		ClearPagePrivate(page);
	page->private = 0;
		page_cache_release(page);
		set_page_private(page, 0);
	}
	block->ops = &btree_block_ops;
	err = alias_tree_insert(block->sb, block->ino, block->bix, block->level,
			block);
@@ -702,9 +720,12 @@ void freeseg(struct super_block *sb, u32 segno)
		page = find_get_page(mapping, ofs >> PAGE_SHIFT);
		if (!page)
			continue;
		if (PagePrivate(page)) {
			ClearPagePrivate(page);
			page_cache_release(page);
		}
		page_cache_release(page);
	}
}

int logfs_open_area(struct logfs_area *area, size_t bytes)