diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-09-15 17:50:48 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-09-23 14:10:15 -0400 |
commit | 88bd02c9472a166b706284a34a84f1243322d782 (patch) | |
tree | a729dceea6c10c86b8c2a1e7b2fa1355d1143ba3 /fs | |
parent | 7ef35e3b9e7a99db4930b58b33a94455dbf53276 (diff) |
f2fs: fix conditions to remain recovery information in f2fs_sync_file
This patch revisited whole the recovery information during the f2fs_sync_file.
In this patch, there are three information to make a decision.
a) IS_CHECKPOINTED, /* is it checkpointed before? */
b) HAS_FSYNCED_INODE, /* is the inode fsynced before? */
c) HAS_LAST_FSYNC, /* has the latest node fsync mark? */
And, the scenarios for our rule are based on:
[Term] F: fsync_mark, D: dentry_mark
1. inode(x) | CP | inode(x) | dnode(F)
2. inode(x) | CP | inode(F) | dnode(F)
3. inode(x) | CP | dnode(F) | inode(x) | inode(F)
4. inode(x) | CP | dnode(F) | inode(F)
5. CP | inode(x) | dnode(F) | inode(DF)
6. CP | inode(DF) | dnode(F)
7. CP | dnode(F) | inode(DF)
8. CP | dnode(F) | inode(x) | inode(DF)
For example, #3, the three conditions should be changed as follows.
inode(x) | CP | dnode(F) | inode(x) | inode(F)
a) x o o o o
b) x x x x o
c) x o o x o
If f2fs_sync_file stops ------^,
it should write inode(F) --------------^
So, the need_inode_block_update should return true, since
c) get_nat_flag(e, HAS_LAST_FSYNC), is false.
For example, #8,
CP | alloc | dnode(F) | inode(x) | inode(DF)
a) o x x x x
b) x x x o
c) o o x o
If f2fs_sync_file stops -------^,
it should write inode(DF) --------------^
Note that, the roll-forward policy should follow this rule, which means,
if there are any missing blocks, we doesn't need to recover that inode.
Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/f2fs/data.c | 3 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 6 | ||||
-rw-r--r-- | fs/f2fs/file.c | 10 | ||||
-rw-r--r-- | fs/f2fs/node.c | 56 | ||||
-rw-r--r-- | fs/f2fs/node.h | 21 |
5 files changed, 56 insertions, 40 deletions
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 0e376585e29f..fdc3dbe677a1 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c | |||
@@ -1089,9 +1089,6 @@ static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, | |||
1089 | if (check_direct_IO(inode, rw, iter, offset)) | 1089 | if (check_direct_IO(inode, rw, iter, offset)) |
1090 | return 0; | 1090 | return 0; |
1091 | 1091 | ||
1092 | /* clear fsync mark to recover these blocks */ | ||
1093 | fsync_mark_clear(F2FS_I_SB(inode), inode->i_ino); | ||
1094 | |||
1095 | trace_f2fs_direct_IO_enter(inode, offset, count, rw); | 1092 | trace_f2fs_direct_IO_enter(inode, offset, count, rw); |
1096 | 1093 | ||
1097 | err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block); | 1094 | err = blockdev_direct_IO(rw, iocb, inode, iter, offset, get_data_block); |
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b6439c3d1744..dbe5f939b7e7 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -1224,9 +1224,9 @@ struct dnode_of_data; | |||
1224 | struct node_info; | 1224 | struct node_info; |
1225 | 1225 | ||
1226 | bool available_free_memory(struct f2fs_sb_info *, int); | 1226 | bool available_free_memory(struct f2fs_sb_info *, int); |
1227 | int is_checkpointed_node(struct f2fs_sb_info *, nid_t); | 1227 | bool is_checkpointed_node(struct f2fs_sb_info *, nid_t); |
1228 | bool fsync_mark_done(struct f2fs_sb_info *, nid_t); | 1228 | bool has_fsynced_inode(struct f2fs_sb_info *, nid_t); |
1229 | void fsync_mark_clear(struct f2fs_sb_info *, nid_t); | 1229 | bool need_inode_block_update(struct f2fs_sb_info *, nid_t); |
1230 | void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); | 1230 | void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); |
1231 | int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); | 1231 | int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); |
1232 | int truncate_inode_blocks(struct inode *, pgoff_t); | 1232 | int truncate_inode_blocks(struct inode *, pgoff_t); |
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index af06e22a0dbd..3035c791d934 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c | |||
@@ -207,15 +207,17 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) | |||
207 | up_write(&fi->i_sem); | 207 | up_write(&fi->i_sem); |
208 | } | 208 | } |
209 | } else { | 209 | } else { |
210 | /* if there is no written node page, write its inode page */ | 210 | sync_nodes: |
211 | while (!sync_node_pages(sbi, ino, &wbc)) { | 211 | sync_node_pages(sbi, ino, &wbc); |
212 | if (fsync_mark_done(sbi, ino)) | 212 | |
213 | goto out; | 213 | if (need_inode_block_update(sbi, ino)) { |
214 | mark_inode_dirty_sync(inode); | 214 | mark_inode_dirty_sync(inode); |
215 | ret = f2fs_write_inode(inode, NULL); | 215 | ret = f2fs_write_inode(inode, NULL); |
216 | if (ret) | 216 | if (ret) |
217 | goto out; | 217 | goto out; |
218 | goto sync_nodes; | ||
218 | } | 219 | } |
220 | |||
219 | ret = wait_on_node_pages_writeback(sbi, ino); | 221 | ret = wait_on_node_pages_writeback(sbi, ino); |
220 | if (ret) | 222 | if (ret) |
221 | goto out; | 223 | goto out; |
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index d19d6b18cd4e..7a2d9c980c96 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c | |||
@@ -123,44 +123,48 @@ static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e) | |||
123 | kmem_cache_free(nat_entry_slab, e); | 123 | kmem_cache_free(nat_entry_slab, e); |
124 | } | 124 | } |
125 | 125 | ||
126 | int is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) | 126 | bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) |
127 | { | 127 | { |
128 | struct f2fs_nm_info *nm_i = NM_I(sbi); | 128 | struct f2fs_nm_info *nm_i = NM_I(sbi); |
129 | struct nat_entry *e; | 129 | struct nat_entry *e; |
130 | int is_cp = 1; | 130 | bool is_cp = true; |
131 | 131 | ||
132 | read_lock(&nm_i->nat_tree_lock); | 132 | read_lock(&nm_i->nat_tree_lock); |
133 | e = __lookup_nat_cache(nm_i, nid); | 133 | e = __lookup_nat_cache(nm_i, nid); |
134 | if (e && !get_nat_flag(e, IS_CHECKPOINTED)) | 134 | if (e && !get_nat_flag(e, IS_CHECKPOINTED)) |
135 | is_cp = 0; | 135 | is_cp = false; |
136 | read_unlock(&nm_i->nat_tree_lock); | 136 | read_unlock(&nm_i->nat_tree_lock); |
137 | return is_cp; | 137 | return is_cp; |
138 | } | 138 | } |
139 | 139 | ||
140 | bool fsync_mark_done(struct f2fs_sb_info *sbi, nid_t nid) | 140 | bool has_fsynced_inode(struct f2fs_sb_info *sbi, nid_t ino) |
141 | { | 141 | { |
142 | struct f2fs_nm_info *nm_i = NM_I(sbi); | 142 | struct f2fs_nm_info *nm_i = NM_I(sbi); |
143 | struct nat_entry *e; | 143 | struct nat_entry *e; |
144 | bool fsync_done = false; | 144 | bool fsynced = false; |
145 | 145 | ||
146 | read_lock(&nm_i->nat_tree_lock); | 146 | read_lock(&nm_i->nat_tree_lock); |
147 | e = __lookup_nat_cache(nm_i, nid); | 147 | e = __lookup_nat_cache(nm_i, ino); |
148 | if (e) | 148 | if (e && get_nat_flag(e, HAS_FSYNCED_INODE)) |
149 | fsync_done = get_nat_flag(e, HAS_FSYNC_MARK); | 149 | fsynced = true; |
150 | read_unlock(&nm_i->nat_tree_lock); | 150 | read_unlock(&nm_i->nat_tree_lock); |
151 | return fsync_done; | 151 | return fsynced; |
152 | } | 152 | } |
153 | 153 | ||
154 | void fsync_mark_clear(struct f2fs_sb_info *sbi, nid_t nid) | 154 | bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino) |
155 | { | 155 | { |
156 | struct f2fs_nm_info *nm_i = NM_I(sbi); | 156 | struct f2fs_nm_info *nm_i = NM_I(sbi); |
157 | struct nat_entry *e; | 157 | struct nat_entry *e; |
158 | bool need_update = true; | ||
158 | 159 | ||
159 | write_lock(&nm_i->nat_tree_lock); | 160 | read_lock(&nm_i->nat_tree_lock); |
160 | e = __lookup_nat_cache(nm_i, nid); | 161 | e = __lookup_nat_cache(nm_i, ino); |
161 | if (e) | 162 | if (e && get_nat_flag(e, HAS_LAST_FSYNC) && |
162 | set_nat_flag(e, HAS_FSYNC_MARK, false); | 163 | (get_nat_flag(e, IS_CHECKPOINTED) || |
163 | write_unlock(&nm_i->nat_tree_lock); | 164 | get_nat_flag(e, HAS_FSYNCED_INODE))) |
165 | need_update = false; | ||
166 | read_unlock(&nm_i->nat_tree_lock); | ||
167 | return need_update; | ||
164 | } | 168 | } |
165 | 169 | ||
166 | static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) | 170 | static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) |
@@ -176,7 +180,7 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) | |||
176 | } | 180 | } |
177 | memset(new, 0, sizeof(struct nat_entry)); | 181 | memset(new, 0, sizeof(struct nat_entry)); |
178 | nat_set_nid(new, nid); | 182 | nat_set_nid(new, nid); |
179 | set_nat_flag(new, IS_CHECKPOINTED, true); | 183 | nat_reset_flag(new); |
180 | list_add_tail(&new->list, &nm_i->nat_entries); | 184 | list_add_tail(&new->list, &nm_i->nat_entries); |
181 | nm_i->nat_cnt++; | 185 | nm_i->nat_cnt++; |
182 | return new; | 186 | return new; |
@@ -244,12 +248,17 @@ retry: | |||
244 | 248 | ||
245 | /* change address */ | 249 | /* change address */ |
246 | nat_set_blkaddr(e, new_blkaddr); | 250 | nat_set_blkaddr(e, new_blkaddr); |
251 | if (new_blkaddr == NEW_ADDR || new_blkaddr == NULL_ADDR) | ||
252 | set_nat_flag(e, IS_CHECKPOINTED, false); | ||
247 | __set_nat_cache_dirty(nm_i, e); | 253 | __set_nat_cache_dirty(nm_i, e); |
248 | 254 | ||
249 | /* update fsync_mark if its inode nat entry is still alive */ | 255 | /* update fsync_mark if its inode nat entry is still alive */ |
250 | e = __lookup_nat_cache(nm_i, ni->ino); | 256 | e = __lookup_nat_cache(nm_i, ni->ino); |
251 | if (e) | 257 | if (e) { |
252 | set_nat_flag(e, HAS_FSYNC_MARK, fsync_done); | 258 | if (fsync_done && ni->nid == ni->ino) |
259 | set_nat_flag(e, HAS_FSYNCED_INODE, true); | ||
260 | set_nat_flag(e, HAS_LAST_FSYNC, fsync_done); | ||
261 | } | ||
253 | write_unlock(&nm_i->nat_tree_lock); | 262 | write_unlock(&nm_i->nat_tree_lock); |
254 | } | 263 | } |
255 | 264 | ||
@@ -1121,10 +1130,14 @@ continue_unlock: | |||
1121 | 1130 | ||
1122 | /* called by fsync() */ | 1131 | /* called by fsync() */ |
1123 | if (ino && IS_DNODE(page)) { | 1132 | if (ino && IS_DNODE(page)) { |
1124 | int mark = !is_checkpointed_node(sbi, ino); | ||
1125 | set_fsync_mark(page, 1); | 1133 | set_fsync_mark(page, 1); |
1126 | if (IS_INODE(page)) | 1134 | if (IS_INODE(page)) { |
1127 | set_dentry_mark(page, mark); | 1135 | if (!is_checkpointed_node(sbi, ino) && |
1136 | !has_fsynced_inode(sbi, ino)) | ||
1137 | set_dentry_mark(page, 1); | ||
1138 | else | ||
1139 | set_dentry_mark(page, 0); | ||
1140 | } | ||
1128 | nwritten++; | 1141 | nwritten++; |
1129 | } else { | 1142 | } else { |
1130 | set_fsync_mark(page, 0); | 1143 | set_fsync_mark(page, 0); |
@@ -1912,6 +1925,7 @@ void flush_nat_entries(struct f2fs_sb_info *sbi) | |||
1912 | write_unlock(&nm_i->nat_tree_lock); | 1925 | write_unlock(&nm_i->nat_tree_lock); |
1913 | } else { | 1926 | } else { |
1914 | write_lock(&nm_i->nat_tree_lock); | 1927 | write_lock(&nm_i->nat_tree_lock); |
1928 | nat_reset_flag(ne); | ||
1915 | __clear_nat_cache_dirty(nm_i, ne); | 1929 | __clear_nat_cache_dirty(nm_i, ne); |
1916 | write_unlock(&nm_i->nat_tree_lock); | 1930 | write_unlock(&nm_i->nat_tree_lock); |
1917 | } | 1931 | } |
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h index 3043778d805b..b8ba63c43b99 100644 --- a/fs/f2fs/node.h +++ b/fs/f2fs/node.h | |||
@@ -41,7 +41,8 @@ struct node_info { | |||
41 | 41 | ||
42 | enum { | 42 | enum { |
43 | IS_CHECKPOINTED, /* is it checkpointed before? */ | 43 | IS_CHECKPOINTED, /* is it checkpointed before? */ |
44 | HAS_FSYNC_MARK, /* has the latest node fsync mark? */ | 44 | HAS_FSYNCED_INODE, /* is the inode fsynced before? */ |
45 | HAS_LAST_FSYNC, /* has the latest node fsync mark? */ | ||
45 | }; | 46 | }; |
46 | 47 | ||
47 | struct nat_entry { | 48 | struct nat_entry { |
@@ -60,15 +61,9 @@ struct nat_entry { | |||
60 | #define nat_set_version(nat, v) (nat->ni.version = v) | 61 | #define nat_set_version(nat, v) (nat->ni.version = v) |
61 | 62 | ||
62 | #define __set_nat_cache_dirty(nm_i, ne) \ | 63 | #define __set_nat_cache_dirty(nm_i, ne) \ |
63 | do { \ | 64 | list_move_tail(&ne->list, &nm_i->dirty_nat_entries); |
64 | set_nat_flag(ne, IS_CHECKPOINTED, false); \ | ||
65 | list_move_tail(&ne->list, &nm_i->dirty_nat_entries); \ | ||
66 | } while (0) | ||
67 | #define __clear_nat_cache_dirty(nm_i, ne) \ | 65 | #define __clear_nat_cache_dirty(nm_i, ne) \ |
68 | do { \ | 66 | list_move_tail(&ne->list, &nm_i->nat_entries); |
69 | set_nat_flag(ne, IS_CHECKPOINTED, true); \ | ||
70 | list_move_tail(&ne->list, &nm_i->nat_entries); \ | ||
71 | } while (0) | ||
72 | #define inc_node_version(version) (++version) | 67 | #define inc_node_version(version) (++version) |
73 | 68 | ||
74 | static inline void set_nat_flag(struct nat_entry *ne, | 69 | static inline void set_nat_flag(struct nat_entry *ne, |
@@ -87,6 +82,14 @@ static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type) | |||
87 | return ne->flag & mask; | 82 | return ne->flag & mask; |
88 | } | 83 | } |
89 | 84 | ||
85 | static inline void nat_reset_flag(struct nat_entry *ne) | ||
86 | { | ||
87 | /* these states can be set only after checkpoint was done */ | ||
88 | set_nat_flag(ne, IS_CHECKPOINTED, true); | ||
89 | set_nat_flag(ne, HAS_FSYNCED_INODE, false); | ||
90 | set_nat_flag(ne, HAS_LAST_FSYNC, true); | ||
91 | } | ||
92 | |||
90 | static inline void node_info_from_raw_nat(struct node_info *ni, | 93 | static inline void node_info_from_raw_nat(struct node_info *ni, |
91 | struct f2fs_nat_entry *raw_ne) | 94 | struct f2fs_nat_entry *raw_ne) |
92 | { | 95 | { |