diff options
author | Chao Yu <yuchao0@huawei.com> | 2016-09-19 23:04:18 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-09-30 20:34:20 -0400 |
commit | aaec2b1d18792a5f27b69ff37f34f43f89f5aa3b (patch) | |
tree | d21e6ad4e210ad826580c03a55cb97230543c280 | |
parent | fadb2fb8af5348c1bc59cab17c6f8bf515e50d55 (diff) |
f2fs: introduce cp_lock to protect updating of ckpt_flags
This patch introduces spinlock to protect updating process of ckpt_flags
field in struct f2fs_checkpoint, it avoids incorrectly updating in race
condition.
Signed-off-by: Chao Yu <yuchao0@huawei.com>
[Jaegeuk Kim: add __is_set_ckpt_flags likewise __set_ckpt_flags]
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
-rw-r--r-- | fs/f2fs/checkpoint.c | 31 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 41 | ||||
-rw-r--r-- | fs/f2fs/node.h | 4 | ||||
-rw-r--r-- | fs/f2fs/recovery.c | 2 | ||||
-rw-r--r-- | fs/f2fs/segment.c | 4 | ||||
-rw-r--r-- | fs/f2fs/super.c | 5 |
6 files changed, 59 insertions, 28 deletions
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 9c6439b0e8d2..591db95222d1 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c | |||
@@ -28,7 +28,7 @@ struct kmem_cache *inode_entry_slab; | |||
28 | 28 | ||
29 | void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) | 29 | void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io) |
30 | { | 30 | { |
31 | set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); | 31 | set_ckpt_flags(sbi, CP_ERROR_FLAG); |
32 | sbi->sb->s_flags |= MS_RDONLY; | 32 | sbi->sb->s_flags |= MS_RDONLY; |
33 | if (!end_io) | 33 | if (!end_io) |
34 | f2fs_flush_merged_bios(sbi); | 34 | f2fs_flush_merged_bios(sbi); |
@@ -571,7 +571,7 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) | |||
571 | block_t start_blk, orphan_blocks, i, j; | 571 | block_t start_blk, orphan_blocks, i, j; |
572 | int err; | 572 | int err; |
573 | 573 | ||
574 | if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) | 574 | if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) |
575 | return 0; | 575 | return 0; |
576 | 576 | ||
577 | start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); | 577 | start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); |
@@ -595,7 +595,7 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) | |||
595 | f2fs_put_page(page, 1); | 595 | f2fs_put_page(page, 1); |
596 | } | 596 | } |
597 | /* clear Orphan Flag */ | 597 | /* clear Orphan Flag */ |
598 | clear_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG); | 598 | clear_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG); |
599 | return 0; | 599 | return 0; |
600 | } | 600 | } |
601 | 601 | ||
@@ -1043,10 +1043,12 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) | |||
1043 | 1043 | ||
1044 | /* 2 cp + n data seg summary + orphan inode blocks */ | 1044 | /* 2 cp + n data seg summary + orphan inode blocks */ |
1045 | data_sum_blocks = npages_for_summary_flush(sbi, false); | 1045 | data_sum_blocks = npages_for_summary_flush(sbi, false); |
1046 | spin_lock(&sbi->cp_lock); | ||
1046 | if (data_sum_blocks < NR_CURSEG_DATA_TYPE) | 1047 | if (data_sum_blocks < NR_CURSEG_DATA_TYPE) |
1047 | set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); | 1048 | __set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); |
1048 | else | 1049 | else |
1049 | clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); | 1050 | __clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); |
1051 | spin_unlock(&sbi->cp_lock); | ||
1050 | 1052 | ||
1051 | orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num); | 1053 | orphan_blocks = GET_ORPHAN_BLOCKS(orphan_num); |
1052 | ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + | 1054 | ckpt->cp_pack_start_sum = cpu_to_le32(1 + cp_payload_blks + |
@@ -1061,26 +1063,29 @@ static int do_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) | |||
1061 | cp_payload_blks + data_sum_blocks + | 1063 | cp_payload_blks + data_sum_blocks + |
1062 | orphan_blocks); | 1064 | orphan_blocks); |
1063 | 1065 | ||
1066 | spin_lock(&sbi->cp_lock); | ||
1064 | if (cpc->reason == CP_UMOUNT) | 1067 | if (cpc->reason == CP_UMOUNT) |
1065 | set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); | 1068 | __set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); |
1066 | else | 1069 | else |
1067 | clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); | 1070 | __clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); |
1068 | 1071 | ||
1069 | if (cpc->reason == CP_FASTBOOT) | 1072 | if (cpc->reason == CP_FASTBOOT) |
1070 | set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); | 1073 | __set_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); |
1071 | else | 1074 | else |
1072 | clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); | 1075 | __clear_ckpt_flags(ckpt, CP_FASTBOOT_FLAG); |
1073 | 1076 | ||
1074 | if (orphan_num) | 1077 | if (orphan_num) |
1075 | set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); | 1078 | __set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); |
1076 | else | 1079 | else |
1077 | clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); | 1080 | __clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); |
1078 | 1081 | ||
1079 | if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) | 1082 | if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) |
1080 | set_ckpt_flags(ckpt, CP_FSCK_FLAG); | 1083 | __set_ckpt_flags(ckpt, CP_FSCK_FLAG); |
1081 | 1084 | ||
1082 | /* set this flag to activate crc|cp_ver for recovery */ | 1085 | /* set this flag to activate crc|cp_ver for recovery */ |
1083 | set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); | 1086 | __set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG); |
1087 | |||
1088 | spin_unlock(&sbi->cp_lock); | ||
1084 | 1089 | ||
1085 | /* update SIT/NAT bitmap */ | 1090 | /* update SIT/NAT bitmap */ |
1086 | get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); | 1091 | get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); |
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 04e96181e928..2fb8c353ab4a 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -797,6 +797,7 @@ struct f2fs_sb_info { | |||
797 | 797 | ||
798 | /* for checkpoint */ | 798 | /* for checkpoint */ |
799 | struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ | 799 | struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ |
800 | spinlock_t cp_lock; /* for flag in ckpt */ | ||
800 | struct inode *meta_inode; /* cache meta blocks */ | 801 | struct inode *meta_inode; /* cache meta blocks */ |
801 | struct mutex cp_mutex; /* checkpoint procedure lock */ | 802 | struct mutex cp_mutex; /* checkpoint procedure lock */ |
802 | struct rw_semaphore cp_rwsem; /* blocking FS operations */ | 803 | struct rw_semaphore cp_rwsem; /* blocking FS operations */ |
@@ -1064,26 +1065,50 @@ static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) | |||
1064 | return le64_to_cpu(cp->checkpoint_ver); | 1065 | return le64_to_cpu(cp->checkpoint_ver); |
1065 | } | 1066 | } |
1066 | 1067 | ||
1067 | static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) | 1068 | static inline bool __is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) |
1068 | { | 1069 | { |
1069 | unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); | 1070 | unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); |
1071 | |||
1070 | return ckpt_flags & f; | 1072 | return ckpt_flags & f; |
1071 | } | 1073 | } |
1072 | 1074 | ||
1073 | static inline void set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) | 1075 | static inline bool is_set_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) |
1074 | { | 1076 | { |
1075 | unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); | 1077 | return __is_set_ckpt_flags(F2FS_CKPT(sbi), f); |
1078 | } | ||
1079 | |||
1080 | static inline void __set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) | ||
1081 | { | ||
1082 | unsigned int ckpt_flags; | ||
1083 | |||
1084 | ckpt_flags = le32_to_cpu(cp->ckpt_flags); | ||
1076 | ckpt_flags |= f; | 1085 | ckpt_flags |= f; |
1077 | cp->ckpt_flags = cpu_to_le32(ckpt_flags); | 1086 | cp->ckpt_flags = cpu_to_le32(ckpt_flags); |
1078 | } | 1087 | } |
1079 | 1088 | ||
1080 | static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) | 1089 | static inline void set_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) |
1081 | { | 1090 | { |
1082 | unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); | 1091 | spin_lock(&sbi->cp_lock); |
1092 | __set_ckpt_flags(F2FS_CKPT(sbi), f); | ||
1093 | spin_unlock(&sbi->cp_lock); | ||
1094 | } | ||
1095 | |||
1096 | static inline void __clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) | ||
1097 | { | ||
1098 | unsigned int ckpt_flags; | ||
1099 | |||
1100 | ckpt_flags = le32_to_cpu(cp->ckpt_flags); | ||
1083 | ckpt_flags &= (~f); | 1101 | ckpt_flags &= (~f); |
1084 | cp->ckpt_flags = cpu_to_le32(ckpt_flags); | 1102 | cp->ckpt_flags = cpu_to_le32(ckpt_flags); |
1085 | } | 1103 | } |
1086 | 1104 | ||
1105 | static inline void clear_ckpt_flags(struct f2fs_sb_info *sbi, unsigned int f) | ||
1106 | { | ||
1107 | spin_lock(&sbi->cp_lock); | ||
1108 | __clear_ckpt_flags(F2FS_CKPT(sbi), f); | ||
1109 | spin_unlock(&sbi->cp_lock); | ||
1110 | } | ||
1111 | |||
1087 | static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) | 1112 | static inline bool f2fs_discard_en(struct f2fs_sb_info *sbi) |
1088 | { | 1113 | { |
1089 | struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); | 1114 | struct request_queue *q = bdev_get_queue(sbi->sb->s_bdev); |
@@ -1129,8 +1154,8 @@ static inline bool __remain_node_summaries(int reason) | |||
1129 | 1154 | ||
1130 | static inline bool __exist_node_summaries(struct f2fs_sb_info *sbi) | 1155 | static inline bool __exist_node_summaries(struct f2fs_sb_info *sbi) |
1131 | { | 1156 | { |
1132 | return (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) || | 1157 | return (is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG) || |
1133 | is_set_ckpt_flags(F2FS_CKPT(sbi), CP_FASTBOOT_FLAG)); | 1158 | is_set_ckpt_flags(sbi, CP_FASTBOOT_FLAG)); |
1134 | } | 1159 | } |
1135 | 1160 | ||
1136 | /* | 1161 | /* |
@@ -1832,7 +1857,7 @@ static inline int f2fs_readonly(struct super_block *sb) | |||
1832 | 1857 | ||
1833 | static inline bool f2fs_cp_error(struct f2fs_sb_info *sbi) | 1858 | static inline bool f2fs_cp_error(struct f2fs_sb_info *sbi) |
1834 | { | 1859 | { |
1835 | return is_set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); | 1860 | return is_set_ckpt_flags(sbi, CP_ERROR_FLAG); |
1836 | } | 1861 | } |
1837 | 1862 | ||
1838 | static inline bool is_dot_dotdot(const struct qstr *str) | 1863 | static inline bool is_dot_dotdot(const struct qstr *str) |
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index e8ca64a70de0..868bec65e51c 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h | |||
@@ -293,7 +293,7 @@ static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) | |||
293 | size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); | 293 | size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); |
294 | __u64 cp_ver = le64_to_cpu(ckpt->checkpoint_ver); | 294 | __u64 cp_ver = le64_to_cpu(ckpt->checkpoint_ver); |
295 | 295 | ||
296 | if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { | 296 | if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { |
297 | __u64 crc = le32_to_cpu(*((__le32 *) | 297 | __u64 crc = le32_to_cpu(*((__le32 *) |
298 | ((unsigned char *)ckpt + crc_offset))); | 298 | ((unsigned char *)ckpt + crc_offset))); |
299 | cp_ver |= (crc << 32); | 299 | cp_ver |= (crc << 32); |
@@ -308,7 +308,7 @@ static inline bool is_recoverable_dnode(struct page *page) | |||
308 | size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); | 308 | size_t crc_offset = le32_to_cpu(ckpt->checksum_offset); |
309 | __u64 cp_ver = cur_cp_version(ckpt); | 309 | __u64 cp_ver = cur_cp_version(ckpt); |
310 | 310 | ||
311 | if (is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { | 311 | if (__is_set_ckpt_flags(ckpt, CP_CRC_RECOVERY_FLAG)) { |
312 | __u64 crc = le32_to_cpu(*((__le32 *) | 312 | __u64 crc = le32_to_cpu(*((__le32 *) |
313 | ((unsigned char *)ckpt + crc_offset))); | 313 | ((unsigned char *)ckpt + crc_offset))); |
314 | cp_ver |= (crc << 32); | 314 | cp_ver |= (crc << 32); |
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 509273a65157..2fc84a991325 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c | |||
@@ -627,7 +627,7 @@ out: | |||
627 | 627 | ||
628 | clear_sbi_flag(sbi, SBI_POR_DOING); | 628 | clear_sbi_flag(sbi, SBI_POR_DOING); |
629 | if (err) | 629 | if (err) |
630 | set_ckpt_flags(sbi->ckpt, CP_ERROR_FLAG); | 630 | set_ckpt_flags(sbi, CP_ERROR_FLAG); |
631 | mutex_unlock(&sbi->cp_mutex); | 631 | mutex_unlock(&sbi->cp_mutex); |
632 | 632 | ||
633 | /* let's drop all the directory inodes for clean checkpoint */ | 633 | /* let's drop all the directory inodes for clean checkpoint */ |
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 2d23d7b17d6c..e78501ca761e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c | |||
@@ -1801,7 +1801,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) | |||
1801 | int type = CURSEG_HOT_DATA; | 1801 | int type = CURSEG_HOT_DATA; |
1802 | int err; | 1802 | int err; |
1803 | 1803 | ||
1804 | if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { | 1804 | if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) { |
1805 | int npages = npages_for_summary_flush(sbi, true); | 1805 | int npages = npages_for_summary_flush(sbi, true); |
1806 | 1806 | ||
1807 | if (npages >= 2) | 1807 | if (npages >= 2) |
@@ -1898,7 +1898,7 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi, | |||
1898 | 1898 | ||
1899 | void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) | 1899 | void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) |
1900 | { | 1900 | { |
1901 | if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) | 1901 | if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) |
1902 | write_compacted_summaries(sbi, start_blk); | 1902 | write_compacted_summaries(sbi, start_blk); |
1903 | else | 1903 | else |
1904 | write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); | 1904 | write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); |
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 9649b79eefe8..95986a9aa615 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
@@ -736,7 +736,7 @@ static void f2fs_put_super(struct super_block *sb) | |||
736 | * clean checkpoint again. | 736 | * clean checkpoint again. |
737 | */ | 737 | */ |
738 | if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) || | 738 | if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) || |
739 | !is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG)) { | 739 | !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { |
740 | struct cp_control cpc = { | 740 | struct cp_control cpc = { |
741 | .reason = CP_UMOUNT, | 741 | .reason = CP_UMOUNT, |
742 | }; | 742 | }; |
@@ -1478,6 +1478,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi) | |||
1478 | mutex_init(&sbi->umount_mutex); | 1478 | mutex_init(&sbi->umount_mutex); |
1479 | mutex_init(&sbi->wio_mutex[NODE]); | 1479 | mutex_init(&sbi->wio_mutex[NODE]); |
1480 | mutex_init(&sbi->wio_mutex[DATA]); | 1480 | mutex_init(&sbi->wio_mutex[DATA]); |
1481 | spin_lock_init(&sbi->cp_lock); | ||
1481 | 1482 | ||
1482 | #ifdef CONFIG_F2FS_FS_ENCRYPTION | 1483 | #ifdef CONFIG_F2FS_FS_ENCRYPTION |
1483 | memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, | 1484 | memcpy(sbi->key_prefix, F2FS_KEY_DESC_PREFIX, |
@@ -1819,7 +1820,7 @@ try_onemore: | |||
1819 | * previous checkpoint was not done by clean system shutdown. | 1820 | * previous checkpoint was not done by clean system shutdown. |
1820 | */ | 1821 | */ |
1821 | if (bdev_read_only(sb->s_bdev) && | 1822 | if (bdev_read_only(sb->s_bdev) && |
1822 | !is_set_ckpt_flags(sbi->ckpt, CP_UMOUNT_FLAG)) { | 1823 | !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { |
1823 | err = -EROFS; | 1824 | err = -EROFS; |
1824 | goto free_kobj; | 1825 | goto free_kobj; |
1825 | } | 1826 | } |