2021SC@SDUSC
/*
* 强制为给定的 inode 分配所有延迟分配块。
*/
int ext4_alloc_da_blocks(struct inode *inode)
{
trace_ext4_alloc_da_blocks(inode);
if (!EXT4_I(inode)->i_reserved_data_blocks)
return 0;
/*
* 我们现在做一些简单的事情。 filemap_flush() 也将开始触发数据块的写入,严格来说这不是必需的(对于 notebook_mode 的用户来说,甚至是不可取的)。但是,否则需要在以下位置复制代码路径:
* ext4_writepages() ->
* write_cache_pages() --->(通过传入的回调函数)
* __mpage_da_writepage() -->
* mpage_add_bh_to_extent()
* mpage_da_map_blocks()
*
* 问题在于,位于 mm/page-writeback.c 中的 write_cache_pages() 将页面标记为干净以准备进行 I/O,如果我们根本不打算进行 I/O,则这是不可取的。
*
* 我们可以调用 write_cache_pages(),然后通过调用 redirty_page_for_writepage() 重脏所有页面,但这在极端情况下会很丑陋。
因此,我们需要复制上述函数中的部分代码,简化它们,因为我们实际上并不打算写出页面,而是只收集连续的逻辑块范围,调用多块分配器,然后更新具有块分配的缓冲区头。
*
* 现在,我们将通过调用 filemap_flush() 来作弊,它会映射块并启动 I/O,但实际上并不等待 I/O 完成。
*/
return filemap_flush(inode->i_mapping);
}
/*
* bmap() 很特别。 lilo 等应用程序和交换器使用它来查找特定数据的磁盘块。
*
* 当然,如果相关块仍在日志中,这很危险。
如果有人在 ext4 数据日志文件系统上创建一个交换文件并启用交换,
那么当交换到该交换文件的数据突然被先前写出到日志并等待内核写回的原始零覆盖时,他们可能会感到非常震惊 缓冲区缓存。
*
* 所以,如果我们在一个修改过的数据日志文件上看到任何 bmap 调用,请采取额外的步骤来刷新可能在缓存中的任何块。
*/
static sector_t ext4_bmap(struct address_space *mapping, sector_t block)
{
struct inode *inode = mapping->host;
journal_t *journal;
int err;
/*
* 我们可以通过 FIBMAP ioctl 获取内联文件
*/
if (ext4_has_inline_data(inode))
return 0;
if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) &&
test_opt(inode->i_sb, DELALLOC)) {
/*
* 使用 delalloc 我们想要同步文件,以便我们可以确保为文件分配块
*/
filemap_write_and_wait(mapping);
}
if (EXT4_JOURNAL(inode) &&
ext4_test_inode_state(inode, EXT4_STATE_JDATA)) {
/*
* 这是一个真正重量级的方法,但预计在脏文件上使用 bmap 是极其罕见的:只有当我们在新制作的文件上运行 lilo 或 swapon 时,我们才期望这种情况发生。
*
*(bmap 需要 CAP_SYS_RAWIO 所以这并不代表非特权用户 DOS 攻击——如果凡人用户可以随意触发这个路径,我们就会遇到麻烦。)
*
*注意。 EXT4_STATE_JDATA 未在常规文件以外的文件上设置。 如果有人想要对目录或符号链接进行 bmap 并因为缓冲区尚未刷新到磁盘而感到困惑,那么他们应该得到他们所得到的一切。
*/
ext4_clear_inode_state(inode, EXT4_STATE_JDATA);
journal = EXT4_JOURNAL(inode);
jbd2_journal_lock_updates(journal);
err = jbd2_journal_flush(journal, 0);
jbd2_journal_unlock_updates(journal);
if (err)
return 0;
}
return iomap_bmap(mapping, block, &ext4_iomap_ops);
}
static int ext4_readpage(struct file *file, struct page *page)
{
int ret = -EAGAIN;
struct inode *inode = page->mapping->host;
trace_ext4_readpage(page);
if (ext4_has_inline_data(inode))
ret = ext4_readpage_inline(inode, page);
if (ret == -EAGAIN)
return ext4_mpage_readpages(inode, NULL, page);
return ret;
}
static void ext4_readahead(struct readahead_control *rac)
{
struct inode *inode = rac->mapping->host;
/* 如果文件有内联数据,则无需预读。 */
if (ext4_has_inline_data(inode))
return;
ext4_mpage_readpages(inode, rac, NULL);
}
static void ext4_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
trace_ext4_invalidatepage(page, offset, length);
/* 使用此函数时,数据缓冲区不会发生日志记录 */
WARN_ON(page_has_buffers(page) && buffer_jbd(page_buffers(page)));
block_invalidatepage(page, offset, length);
}
static int __ext4_journalled_invalidatepage(struct page *page,
unsigned int offset,
unsigned int length)
{
journal_t *journal = EXT4_JOURNAL(page->mapping->host);
trace_ext4_journalled_invalidatepage(page, offset, length);
/*
* 如果它是一个完整的截断,我们只是忘记挂起的脏
*/
if (offset == 0 && length == PAGE_SIZE)
ClearPageChecked(page);
return jbd2_journal_invalidatepage(journal, page, offset, length);
}
/* aops的包装... */
static void ext4_journalled_invalidatepage(struct page *page,
unsigned int offset,
unsigned int length)
{
WARN_ON(__ext4_journalled_invalidatepage(page, offset, length) < 0);
}
static int ext4_releasepage(struct page *page, gfp_t wait)
{
journal_t *journal = EXT4_JOURNAL(page->mapping->host);
trace_ext4_releasepage(page);
/* 页面有脏日志数据 -> 无法释放 */
if (PageChecked(page))
return 0;
if (journal)
return jbd2_journal_try_to_free_buffers(journal, page);
else
return try_to_free_buffers(page);
}
static bool ext4_inode_datasync_dirty(struct inode *inode)
{
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
if (journal) {
if (jbd2_transaction_committed(journal,
EXT4_I(inode)->i_datasync_tid))
return false;
if (test_opt2(inode->i_sb, JOURNAL_FAST_COMMIT))
return !list_empty(&EXT4_I(inode)->i_fc_list);
return true;
}
/* 任何要写入的元数据缓冲区? */
if (!list_empty(&inode->i_mapping->private_list))
return true;
return inode->i_state & I_DIRTY_DATASYNC;
}
static void ext4_set_