ext3/4的data journalling和direct I/O

ext3和ext4有3种日志模式,分别是journal, ordered(default), writeback,可以在mount的时候用 -o data=指定

journal: All data is committed into the journal prior to being written into the main filesystem.
ordered: All data is forced directly out to the main file system prior to its metadata being committed to the journal.
writeback: Data ordering is not preserved.

另外,如果想在没用 -o data=journal的情况下也做data journalling,还可以使用chattr(1)来更改某个文件的属性,比如

chattr +j testfile

用lsattr testfile就能看到属性已经更改,具体都有什么属性可以man chattr。

这几天遇到一个问题,就是当使用 -o data=journal或者给某个文件增加j属性之后,ext3上的文件就不能使用direct I/O了,具体点说就是open(2)的时候只要带O_DIRECT,open(2)就会返回EINVAL,而在ext4上则没问题。

经过搜索发现,ext3和ext4其实是都不支持在data jouralling情况下的direct I/O的。可以看open(2)的代码,在 __dentry_open()函数里有一段代码做了判断

fs/open.c
 706         /* NB: we're sure to have correct a_ops only after f_op->open */
 707         if (f->f_flags & O_DIRECT) {
 708                 if (!f->f_mapping->a_ops ||
 709                     ((!f->f_mapping->a_ops->direct_IO) &&
 710                     (!f->f_mapping->a_ops->get_xip_mem))) {
 711                         fput(f);
 712                         f = ERR_PTR(-EINVAL);
 713                 }
 714         }

如果有O_DIRECT标志,那么检查相应的a_ops(struct address_space_operations)中的direct_IO方法。ext3在data journalling模式下没有设置direct_IO这个方法,所以就返回了EINVAL。可以看ext3相应的代码(给每一种日志模式都设置了不同的aops)

fs/ext3/inode.c
2007 static const struct address_space_operations ext3_journalled_aops = {         
2008         .readpage               = ext3_readpage,                              
2009         .readpages              = ext3_readpages,                             
2010         .writepage              = ext3_journalled_writepage,                  
2011         .write_begin            = ext3_write_begin,                           
2012         .write_end              = ext3_journalled_write_end,                  
2013         .set_page_dirty         = ext3_journalled_set_page_dirty,             
2014         .bmap                   = ext3_bmap,                                  
2015         .invalidatepage         = ext3_invalidatepage,                        
2016         .releasepage            = ext3_releasepage,                           
2017         .is_partially_uptodate  = block_is_partially_uptodate,                
2018         .error_remove_page      = generic_error_remove_page,                  
2019 };

相应的,ext4的ext4_journalled_aops中设置了 .direct_IO = ext4_direct_IO,所以ext4看似可以用direct I/O。可是再看一下ext4_direct_IO()这个函数

fs/ext4/inode.c
2991         /*
2992          * If we are doing data journalling we don't support O_DIRECT
2993          */
2994         if (ext4_should_journal_data(inode))
2995                 return 0;

也就是如果判断是data journalling模式,ext4_direct_IO()直接返回0,表示写了0个字节。看 mm/filemap.c中的 __generic_file_aio_write()可以知道,->direct_IO返回0之后会用generic_file_buffered_write()写入剩余的字节(对dio来说也就是全部字节了)。所以ext4其实是fall back到了buffer I/O去了,实际上不是direct I/O,只不过没有显式的报错。

2 thoughts on “ext3/4的data journalling和direct I/O

  1. bluezd

    Hi, :)

    generic_file_buffered_write()
    -> generic_perform_write()
    -> a_ops->write_begin()

    在 nfs 中相当于 invoke nfs_write_begin(),这个函数完成的就是将用户空间的数据写到要写的文件的 page cache 中吧(假设现在 sys_write 操作),在本地文件系统中会过一会才会sync 到硬盘中,请问在 nfs 中的实现也类似于本地文件系统吧,那么最终完成将 page cache 中的数据同步到远端的 server 中是一个内核 thread 吗?

    Reply

Leave a Reply