Skip to content

Commit

Permalink
ext2: buffer related fixes
Browse files Browse the repository at this point in the history
Data corruption fix in cases where a buffer could be both in the disk's
page cache and a file's disk cache (this fix is not perfect). Zero out
file holes now that filemap feeds us unzeroed pages.

Signed-off-by: Pedro Falcato <[email protected]>
  • Loading branch information
heatd committed Nov 29, 2024
1 parent c655053 commit a635188
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 2 deletions.
6 changes: 6 additions & 0 deletions kernel/include/onyx/page.h
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,12 @@ PAGEFLAG_OPS(buffer, BUFFER);
struct vm_object *page_vmobj(struct page *page);
unsigned long page_pgoff(struct page *page);

static inline void page_zero_range(struct page *page, unsigned int off, unsigned int len)
{
u8 *ptr = (u8 *) PAGE_TO_VIRT(page);
memset(ptr + off, 0, len);
}

__END_CDECLS

#endif
21 changes: 21 additions & 0 deletions kernel/kernel/fs/buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,25 @@ void block_buf_tear_down_assoc(struct vm_object *object)
}
}

static void bforget(struct block_buf *buf)
{
/* De-dirty the buffer (and page) if possible */
struct page *page = buf->this_page;
spin_lock(&buf->pagestate_lock);
bb_clear_flag(buf, BLOCKBUF_FLAG_DIRTY);
bool isdirty = false;
for (struct block_buf *b = (struct block_buf *) page->priv; b; b = b->next)
{
if (bb_test_flag(b, BLOCKBUF_FLAG_DIRTY))
isdirty = true;
}

if (!isdirty)
page_clear_dirty(page);
spin_unlock(&buf->pagestate_lock);
block_buf_put(buf);
}

/**
* @brief Forget a block_buf's inode
* This will remove it from the assoc list
Expand All @@ -823,6 +842,8 @@ void block_buf_forget_inode(struct block_buf *buf)
buf->assoc_buffers_obj = nullptr;
break;
}

bforget(buf);
}

void buffer_free_page(struct vm_object *vmo, struct page *page)
Expand Down
5 changes: 4 additions & 1 deletion kernel/kernel/fs/ext2/ext2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ int ext2_map_page(struct page *page, size_t off, struct inode *ino)
if (block == EXT2_ERR_INV_BLOCK)
{
// Zero the block, since it's a hole
memset((char *) PAGE_TO_VIRT(page) + curr_off, 0, sb->block_size);
page_zero_range(page, b->page_off, sb->block_size);
bb_test_and_set(b, BLOCKBUF_FLAG_UPTODATE);
}
else
Expand Down Expand Up @@ -236,6 +236,9 @@ ssize_t ext2_readpage(struct page *page, size_t off, struct inode *ino)
for (struct block_buf *b = (struct block_buf *) page->priv; b != nullptr; b = b->next)
{
sector_t block = b->block_nr;
if (bb_test_flag(b, BLOCKBUF_FLAG_UPTODATE))
continue;

if (block != EXT2_ERR_INV_BLOCK)
{
/* TODO: Coalesce reads */
Expand Down
2 changes: 1 addition & 1 deletion kernel/kernel/fs/ext2/inode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ int ext2_truncate_branch(ext2_block_no block, ext2_block_coords &curr_coords, st
return EXT2_TRUNCATED_PARTIALLY;
/* Truncated fully, we can free this block */
/* Note: we must "forget" the inode block buf */
block_buf_forget_inode(buf);
block_buf_forget_inode(buf.release());
sb->free_block(block);
ino->i_blocks -= sb->block_size >> 9;
return EXT2_TRUNCATED_FULLY;
Expand Down

0 comments on commit a635188

Please sign in to comment.