Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

f2fs: fix to set PageUptodate in f2fs_write_end correctly

Previously, f2fs_write_begin sets PageUptodate all the time. But, when user
tries to update the entire page (i.e., len == PAGE_SIZE), we need to consider
that the page is able to be copied partially afterwards. In such the case,
we will lose the remaing region in the page.

This patch fixes this by setting PageUptodate in f2fs_write_end as given copied
result. In the short copy case, it returns zero to let generic_perform_write
retry copying user data again.

As a result, f2fs_write_end() works:
PageUptodate len copied return retry
1. no 4096 4096 4096 false -> return 4096
2. no 4096 1024 0 true -> goto #1 case
3. yes 2048 2048 2048 false -> return 2048
4. yes 2048 1024 1024 false -> return 1024

Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

+19 -10
+19 -10
fs/f2fs/data.c
··· 1642 1642 if (f2fs_encrypted_inode(inode) && S_ISREG(inode->i_mode)) 1643 1643 f2fs_wait_on_encrypted_page_writeback(sbi, blkaddr); 1644 1644 1645 - if (len == PAGE_SIZE) 1646 - goto out_update; 1647 - if (PageUptodate(page)) 1648 - goto out_clear; 1645 + if (len == PAGE_SIZE || PageUptodate(page)) 1646 + return 0; 1649 1647 1650 1648 if (blkaddr == NEW_ADDR) { 1651 1649 zero_user_segment(page, 0, PAGE_SIZE); 1650 + SetPageUptodate(page); 1652 1651 } else { 1653 1652 struct bio *bio; 1654 1653 ··· 1675 1676 goto fail; 1676 1677 } 1677 1678 } 1678 - out_update: 1679 - if (!PageUptodate(page)) 1680 - SetPageUptodate(page); 1681 - out_clear: 1682 - clear_cold_data(page); 1683 1679 return 0; 1684 1680 1685 1681 fail: ··· 1692 1698 1693 1699 trace_f2fs_write_end(inode, pos, len, copied); 1694 1700 1701 + /* 1702 + * This should be come from len == PAGE_SIZE, and we expect copied 1703 + * should be PAGE_SIZE. Otherwise, we treat it with zero copied and 1704 + * let generic_perform_write() try to copy data again through copied=0. 1705 + */ 1706 + if (!PageUptodate(page)) { 1707 + if (unlikely(copied != PAGE_SIZE)) 1708 + copied = 0; 1709 + else 1710 + SetPageUptodate(page); 1711 + } 1712 + if (!copied) 1713 + goto unlock_out; 1714 + 1695 1715 set_page_dirty(page); 1716 + clear_cold_data(page); 1696 1717 1697 1718 if (pos + copied > i_size_read(inode)) 1698 1719 f2fs_i_size_write(inode, pos + copied); 1699 - 1720 + unlock_out: 1700 1721 f2fs_put_page(page, 1); 1701 1722 f2fs_update_time(F2FS_I_SB(inode), REQ_TIME); 1702 1723 return copied;