Hi, On 2020/2/12 16:47, Jan Kara wrote: > On Tue 11-02-20 14:51:10, zhangyi (F) wrote: >> On 2020/2/6 19:46, Jan Kara wrote: >>> On Mon 03-02-20 22:04:58, zhangyi (F) wrote: >>>> Commit 904cdbd41d74 ("jbd2: clear dirty flag when revoking a buffer from >>>> an older transaction") set the BH_Freed flag when forgetting a metadata >>>> buffer which belongs to the committing transaction, it indicate the >>>> committing process clear dirty bits when it is done with the buffer. But >>>> it also clear the BH_Mapped flag at the same time, which may trigger >>>> below NULL pointer oops when block_size < PAGE_SIZE. >>>> >>>> rmdir 1 kjournald2 mkdir 2 >>>> jbd2_journal_commit_transaction >>>> commit transaction N >>>> jbd2_journal_forget >>>> set_buffer_freed(bh1) >>>> jbd2_journal_commit_transaction >>>> commit transaction N+1 >>>> ... >>>> clear_buffer_mapped(bh1) >>>> ext4_getblk(bh2 ummapped) >>>> ... >>>> grow_dev_page >>>> init_page_buffers >>>> bh1->b_private=NULL >>>> bh2->b_private=NULL >>>> jbd2_journal_put_journal_head(jh1) >>>> __journal_remove_journal_head(hb1) >>>> jh1 is NULL and trigger oops >>>> >>>> *) Dir entry block bh1 and bh2 belongs to one page, and the bh2 has >>>> already been unmapped. >>>> >>>> For the metadata buffer we forgetting, clear the dirty flags is enough, >>>> so this patch add BH_Unmap flag for the journal_unmap_buffer() case and >>>> keep the mapped flag for the metadata buffer. >>>> >>>> Fixes: 904cdbd41d74 ("jbd2: clear dirty flag when revoking a buffer from an older transaction") >>>> Signed-off-by: zhangyi (F) <yi.zhang@xxxxxxxxxx> >> [..] >>> >>> Also rather than introducing this new buffer_unmap bit, I'd use the fact >>> this special treatment is needed only for buffers coming from the block device >>> mapping. And we can check for that like: >>> >>> /* >>> * We can (and need to) unmap buffer only for normal mappings. >>> * Block device buffers need to stay mapped all the time. >>> * We need to be careful about the check because the page >>> * mapping can get cleared under our hands. >>> */ >>> mapping = READ_ONCE(bh->b_page->mapping); >>> if (mapping && !sb_is_blkdev_sb(mapping->host->i_sb)) { >>> ... >>> } >> >> Think about it again, it may missing clearing of mapped flag if 'mapping' >> of journalled data page was cleared, and finally trigger exception if >> we reuse the buffer again. So I think it should be: >> >> if (!(mapping && sb_is_blkdev_sb(mapping->host->i_sb))) { >> ... >> } > > Well, if b_page->mapping got cleared, it means the page got fully truncated > and in such case buffers can never be reused - the page and buffers will be > freed once we are done with them. So what you are concerned about cannot > happen. But you're right it is good to explain this in the comment. > Yes, you are right, the page and buffer will be freed in release_buffer_page() and it seems there is no exception, I will send V3 to back to use the judgement condition as you suggested and add comments after tests. Thanks, Yi.