aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2
diff options
context:
space:
mode:
authorJiro SEKIBA <jir@unicus.jp>2010-06-28 04:49:33 -0400
committerRyusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>2010-07-22 21:02:11 -0400
commitb2ac86e1a8e3a3b0ab4449d062c582f07a078e7b (patch)
treeb681ea820685a278e06995ff23b58f7a85904b70 /fs/nilfs2
parentd26493b6f017c0b0063a15bf893411ddae85eee4 (diff)
nilfs2: sync super blocks in turns
This will sync super blocks in turns instead of syncing duplicate super blocks at the time. This will help searching valid super root when super block is written into disk before log is written, which is happen when barrier-less block devices are unmounted uncleanly. In the situation, old super block likely points to valid log. This patch introduces ns_sbwcount member to the nilfs object and adds nilfs_sb_will_flip() function; ns_sbwcount counts how many times super blocks write back to the disk. And, nilfs_sb_will_flip() decides whether flipping required or not based on the count of ns_sbwcount to sync super blocks asymmetrically. The following functions are also changed: - nilfs_prepare_super(): flips super blocks according to the argument. The argument is calculated by nilfs_sb_will_flip() function. - nilfs_cleanup_super(): sets "clean" flag to both super blocks if they point to the same checkpoint. To update both of super block information, caller of nilfs_commit_super must set the information on both super blocks. Signed-off-by: Jiro SEKIBA <jir@unicus.jp> Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Diffstat (limited to 'fs/nilfs2')
-rw-r--r--fs/nilfs2/nilfs.h11
-rw-r--r--fs/nilfs2/segment.c10
-rw-r--r--fs/nilfs2/super.c95
-rw-r--r--fs/nilfs2/the_nilfs.c4
-rw-r--r--fs/nilfs2/the_nilfs.h17
5 files changed, 91 insertions, 46 deletions
diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h
index 462651061b03..36998eaab02f 100644
--- a/fs/nilfs2/nilfs.h
+++ b/fs/nilfs2/nilfs.h
@@ -107,6 +107,14 @@ enum {
107}; 107};
108 108
109/* 109/*
110 * commit flags for nilfs_commit_super and nilfs_sync_super
111 */
112enum {
113 NILFS_SB_COMMIT = 0, /* Commit a super block alternately */
114 NILFS_SB_COMMIT_ALL /* Commit both super blocks */
115};
116
117/*
110 * Macros to check inode numbers 118 * Macros to check inode numbers
111 */ 119 */
112#define NILFS_MDT_INO_BITS \ 120#define NILFS_MDT_INO_BITS \
@@ -272,7 +280,8 @@ extern int nilfs_store_magic_and_option(struct super_block *,
272 struct nilfs_super_block *, char *); 280 struct nilfs_super_block *, char *);
273extern void nilfs_set_log_cursor(struct nilfs_super_block *, 281extern void nilfs_set_log_cursor(struct nilfs_super_block *,
274 struct the_nilfs *); 282 struct the_nilfs *);
275extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *); 283extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *,
284 int flip);
276extern int nilfs_commit_super(struct nilfs_sb_info *, int); 285extern int nilfs_commit_super(struct nilfs_sb_info *, int);
277extern int nilfs_cleanup_super(struct nilfs_sb_info *); 286extern int nilfs_cleanup_super(struct nilfs_sb_info *);
278extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64); 287extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64);
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 9e680a93b13a..04e04854a311 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -2425,10 +2425,12 @@ static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode)
2425 nilfs_discontinued(nilfs)) { 2425 nilfs_discontinued(nilfs)) {
2426 down_write(&nilfs->ns_sem); 2426 down_write(&nilfs->ns_sem);
2427 err = -EIO; 2427 err = -EIO;
2428 sbp = nilfs_prepare_super(sbi); 2428 sbp = nilfs_prepare_super(sbi,
2429 if (likely(sbp)) 2429 nilfs_sb_will_flip(nilfs));
2430 err = nilfs_commit_super( 2430 if (likely(sbp)) {
2431 sbi, nilfs_altsb_need_update(nilfs)); 2431 nilfs_set_log_cursor(sbp[0], nilfs);
2432 err = nilfs_commit_super(sbi, NILFS_SB_COMMIT);
2433 }
2432 up_write(&nilfs->ns_sem); 2434 up_write(&nilfs->ns_sem);
2433 } 2435 }
2434 } 2436 }
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index eb7de40828c7..f2cfbbab2346 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -82,10 +82,12 @@ static void nilfs_set_error(struct nilfs_sb_info *sbi)
82 down_write(&nilfs->ns_sem); 82 down_write(&nilfs->ns_sem);
83 if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { 83 if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) {
84 nilfs->ns_mount_state |= NILFS_ERROR_FS; 84 nilfs->ns_mount_state |= NILFS_ERROR_FS;
85 sbp = nilfs_prepare_super(sbi); 85 sbp = nilfs_prepare_super(sbi, 0);
86 if (likely(sbp)) { 86 if (likely(sbp)) {
87 sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS); 87 sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS);
88 nilfs_commit_super(sbi, 1); 88 if (sbp[1])
89 sbp[1]->s_state |= cpu_to_le16(NILFS_ERROR_FS);
90 nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL);
89 } 91 }
90 } 92 }
91 up_write(&nilfs->ns_sem); 93 up_write(&nilfs->ns_sem);
@@ -184,7 +186,7 @@ static void nilfs_clear_inode(struct inode *inode)
184 nilfs_btnode_cache_clear(&ii->i_btnode_cache); 186 nilfs_btnode_cache_clear(&ii->i_btnode_cache);
185} 187}
186 188
187static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb) 189static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag)
188{ 190{
189 struct the_nilfs *nilfs = sbi->s_nilfs; 191 struct the_nilfs *nilfs = sbi->s_nilfs;
190 int err; 192 int err;
@@ -210,12 +212,20 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb)
210 printk(KERN_ERR 212 printk(KERN_ERR
211 "NILFS: unable to write superblock (err=%d)\n", err); 213 "NILFS: unable to write superblock (err=%d)\n", err);
212 if (err == -EIO && nilfs->ns_sbh[1]) { 214 if (err == -EIO && nilfs->ns_sbh[1]) {
215 /*
216 * sbp[0] points to newer log than sbp[1],
217 * so copy sbp[0] to sbp[1] to take over sbp[0].
218 */
219 memcpy(nilfs->ns_sbp[1], nilfs->ns_sbp[0],
220 nilfs->ns_sbsize);
213 nilfs_fall_back_super_block(nilfs); 221 nilfs_fall_back_super_block(nilfs);
214 goto retry; 222 goto retry;
215 } 223 }
216 } else { 224 } else {
217 struct nilfs_super_block *sbp = nilfs->ns_sbp[0]; 225 struct nilfs_super_block *sbp = nilfs->ns_sbp[0];
218 226
227 nilfs->ns_sbwcount++;
228
219 /* 229 /*
220 * The latest segment becomes trailable from the position 230 * The latest segment becomes trailable from the position
221 * written in superblock. 231 * written in superblock.
@@ -224,20 +234,21 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb)
224 234
225 /* update GC protection for recent segments */ 235 /* update GC protection for recent segments */
226 if (nilfs->ns_sbh[1]) { 236 if (nilfs->ns_sbh[1]) {
227 sbp = NULL; 237 if (flag == NILFS_SB_COMMIT_ALL) {
228 if (dupsb) {
229 set_buffer_dirty(nilfs->ns_sbh[1]); 238 set_buffer_dirty(nilfs->ns_sbh[1]);
230 if (!sync_dirty_buffer(nilfs->ns_sbh[1])) 239 if (sync_dirty_buffer(nilfs->ns_sbh[1]) < 0)
231 sbp = nilfs->ns_sbp[1]; 240 goto out;
232 } 241 }
242 if (le64_to_cpu(nilfs->ns_sbp[1]->s_last_cno) <
243 le64_to_cpu(nilfs->ns_sbp[0]->s_last_cno))
244 sbp = nilfs->ns_sbp[1];
233 } 245 }
234 if (sbp) {
235 spin_lock(&nilfs->ns_last_segment_lock);
236 nilfs->ns_prot_seq = le64_to_cpu(sbp->s_last_seq);
237 spin_unlock(&nilfs->ns_last_segment_lock);
238 }
239 }
240 246
247 spin_lock(&nilfs->ns_last_segment_lock);
248 nilfs->ns_prot_seq = le64_to_cpu(sbp->s_last_seq);
249 spin_unlock(&nilfs->ns_last_segment_lock);
250 }
251 out:
241 return err; 252 return err;
242} 253}
243 254
@@ -257,7 +268,8 @@ void nilfs_set_log_cursor(struct nilfs_super_block *sbp,
257 spin_unlock(&nilfs->ns_last_segment_lock); 268 spin_unlock(&nilfs->ns_last_segment_lock);
258} 269}
259 270
260struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi) 271struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi,
272 int flip)
261{ 273{
262 struct the_nilfs *nilfs = sbi->s_nilfs; 274 struct the_nilfs *nilfs = sbi->s_nilfs;
263 struct nilfs_super_block **sbp = nilfs->ns_sbp; 275 struct nilfs_super_block **sbp = nilfs->ns_sbp;
@@ -266,38 +278,46 @@ struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi)
266 if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { 278 if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) {
267 if (sbp[1] && 279 if (sbp[1] &&
268 sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) { 280 sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) {
269 nilfs_swap_super_block(nilfs); 281 memcpy(sbp[0], sbp[1], nilfs->ns_sbsize);
270 } else { 282 } else {
271 printk(KERN_CRIT "NILFS: superblock broke on dev %s\n", 283 printk(KERN_CRIT "NILFS: superblock broke on dev %s\n",
272 sbi->s_super->s_id); 284 sbi->s_super->s_id);
273 return NULL; 285 return NULL;
274 } 286 }
287 } else if (sbp[1] &&
288 sbp[1]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) {
289 memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
275 } 290 }
291
292 if (flip && sbp[1])
293 nilfs_swap_super_block(nilfs);
294
276 return sbp; 295 return sbp;
277} 296}
278 297
279int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) 298int nilfs_commit_super(struct nilfs_sb_info *sbi, int flag)
280{ 299{
281 struct the_nilfs *nilfs = sbi->s_nilfs; 300 struct the_nilfs *nilfs = sbi->s_nilfs;
282 struct nilfs_super_block **sbp = nilfs->ns_sbp; 301 struct nilfs_super_block **sbp = nilfs->ns_sbp;
283 time_t t; 302 time_t t;
284 303
285 /* nilfs->ns_sem must be locked by the caller. */ 304 /* nilfs->ns_sem must be locked by the caller. */
286 nilfs_set_log_cursor(sbp[0], nilfs);
287
288 t = get_seconds(); 305 t = get_seconds();
289 nilfs->ns_sbwtime[0] = t; 306 nilfs->ns_sbwtime = t;
290 sbp[0]->s_wtime = cpu_to_le64(t); 307 sbp[0]->s_wtime = cpu_to_le64(t);
291 sbp[0]->s_sum = 0; 308 sbp[0]->s_sum = 0;
292 sbp[0]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed, 309 sbp[0]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed,
293 (unsigned char *)sbp[0], 310 (unsigned char *)sbp[0],
294 nilfs->ns_sbsize)); 311 nilfs->ns_sbsize));
295 if (dupsb && sbp[1]) { 312 if (flag == NILFS_SB_COMMIT_ALL && sbp[1]) {
296 memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); 313 sbp[1]->s_wtime = sbp[0]->s_wtime;
297 nilfs->ns_sbwtime[1] = t; 314 sbp[1]->s_sum = 0;
315 sbp[1]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed,
316 (unsigned char *)sbp[1],
317 nilfs->ns_sbsize));
298 } 318 }
299 clear_nilfs_sb_dirty(nilfs); 319 clear_nilfs_sb_dirty(nilfs);
300 return nilfs_sync_super(sbi, dupsb); 320 return nilfs_sync_super(sbi, flag);
301} 321}
302 322
303/** 323/**
@@ -311,12 +331,23 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb)
311int nilfs_cleanup_super(struct nilfs_sb_info *sbi) 331int nilfs_cleanup_super(struct nilfs_sb_info *sbi)
312{ 332{
313 struct nilfs_super_block **sbp; 333 struct nilfs_super_block **sbp;
334 int flag = NILFS_SB_COMMIT;
314 int ret = -EIO; 335 int ret = -EIO;
315 336
316 sbp = nilfs_prepare_super(sbi); 337 sbp = nilfs_prepare_super(sbi, 0);
317 if (sbp) { 338 if (sbp) {
318 sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state); 339 sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state);
319 ret = nilfs_commit_super(sbi, 1); 340 nilfs_set_log_cursor(sbp[0], sbi->s_nilfs);
341 if (sbp[1] && sbp[0]->s_last_cno == sbp[1]->s_last_cno) {
342 /*
343 * make the "clean" flag also to the opposite
344 * super block if both super blocks point to
345 * the same checkpoint.
346 */
347 sbp[1]->s_state = sbp[0]->s_state;
348 flag = NILFS_SB_COMMIT_ALL;
349 }
350 ret = nilfs_commit_super(sbi, flag);
320 } 351 }
321 return ret; 352 return ret;
322} 353}
@@ -362,9 +393,11 @@ static int nilfs_sync_fs(struct super_block *sb, int wait)
362 393
363 down_write(&nilfs->ns_sem); 394 down_write(&nilfs->ns_sem);
364 if (nilfs_sb_dirty(nilfs)) { 395 if (nilfs_sb_dirty(nilfs)) {
365 sbp = nilfs_prepare_super(sbi); 396 sbp = nilfs_prepare_super(sbi, nilfs_sb_will_flip(nilfs));
366 if (likely(sbp)) 397 if (likely(sbp)) {
367 nilfs_commit_super(sbi, 1); 398 nilfs_set_log_cursor(sbp[0], nilfs);
399 nilfs_commit_super(sbi, NILFS_SB_COMMIT);
400 }
368 } 401 }
369 up_write(&nilfs->ns_sem); 402 up_write(&nilfs->ns_sem);
370 403
@@ -664,7 +697,7 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
664 int mnt_count; 697 int mnt_count;
665 698
666 /* nilfs->ns_sem must be locked by the caller. */ 699 /* nilfs->ns_sem must be locked by the caller. */
667 sbp = nilfs_prepare_super(sbi); 700 sbp = nilfs_prepare_super(sbi, 0);
668 if (!sbp) 701 if (!sbp)
669 return -EIO; 702 return -EIO;
670 703
@@ -687,7 +720,9 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi)
687 sbp[0]->s_state = 720 sbp[0]->s_state =
688 cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS); 721 cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS);
689 sbp[0]->s_mtime = cpu_to_le64(get_seconds()); 722 sbp[0]->s_mtime = cpu_to_le64(get_seconds());
690 return nilfs_commit_super(sbi, 1); 723 /* synchronize sbp[1] with sbp[0] */
724 memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
725 return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL);
691} 726}
692 727
693struct nilfs_super_block *nilfs_read_super_block(struct super_block *sb, 728struct nilfs_super_block *nilfs_read_super_block(struct super_block *sb,
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index ed58053b6f68..530d2777b4c7 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -513,8 +513,8 @@ static int nilfs_load_super_block(struct the_nilfs *nilfs,
513 nilfs_swap_super_block(nilfs); 513 nilfs_swap_super_block(nilfs);
514 } 514 }
515 515
516 nilfs->ns_sbwtime[0] = le64_to_cpu(sbp[0]->s_wtime); 516 nilfs->ns_sbwcount = 0;
517 nilfs->ns_sbwtime[1] = valid[!swp] ? le64_to_cpu(sbp[1]->s_wtime) : 0; 517 nilfs->ns_sbwtime = le64_to_cpu(sbp[0]->s_wtime);
518 nilfs->ns_prot_seq = le64_to_cpu(sbp[valid[1] & !swp]->s_last_seq); 518 nilfs->ns_prot_seq = le64_to_cpu(sbp[valid[1] & !swp]->s_last_seq);
519 *sbpp = sbp[0]; 519 *sbpp = sbp[0];
520 return 0; 520 return 0;
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 191560ec2e7f..32b4983b7458 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -57,7 +57,8 @@ enum {
57 * @ns_current: back pointer to current mount 57 * @ns_current: back pointer to current mount
58 * @ns_sbh: buffer heads of on-disk super blocks 58 * @ns_sbh: buffer heads of on-disk super blocks
59 * @ns_sbp: pointers to super block data 59 * @ns_sbp: pointers to super block data
60 * @ns_sbwtime: previous write time of super blocks 60 * @ns_sbwtime: previous write time of super block
61 * @ns_sbwcount: write count of super block
61 * @ns_sbsize: size of valid data in super block 62 * @ns_sbsize: size of valid data in super block
62 * @ns_supers: list of nilfs super block structs 63 * @ns_supers: list of nilfs super block structs
63 * @ns_seg_seq: segment sequence counter 64 * @ns_seg_seq: segment sequence counter
@@ -119,7 +120,8 @@ struct the_nilfs {
119 */ 120 */
120 struct buffer_head *ns_sbh[2]; 121 struct buffer_head *ns_sbh[2];
121 struct nilfs_super_block *ns_sbp[2]; 122 struct nilfs_super_block *ns_sbp[2];
122 time_t ns_sbwtime[2]; 123 time_t ns_sbwtime;
124 unsigned ns_sbwcount;
123 unsigned ns_sbsize; 125 unsigned ns_sbsize;
124 unsigned ns_mount_state; 126 unsigned ns_mount_state;
125 127
@@ -203,20 +205,17 @@ THE_NILFS_FNS(SB_DIRTY, sb_dirty)
203 205
204/* Minimum interval of periodical update of superblocks (in seconds) */ 206/* Minimum interval of periodical update of superblocks (in seconds) */
205#define NILFS_SB_FREQ 10 207#define NILFS_SB_FREQ 10
206#define NILFS_ALTSB_FREQ 60 /* spare superblock */
207 208
208static inline int nilfs_sb_need_update(struct the_nilfs *nilfs) 209static inline int nilfs_sb_need_update(struct the_nilfs *nilfs)
209{ 210{
210 u64 t = get_seconds(); 211 u64 t = get_seconds();
211 return t < nilfs->ns_sbwtime[0] || 212 return t < nilfs->ns_sbwtime || t > nilfs->ns_sbwtime + NILFS_SB_FREQ;
212 t > nilfs->ns_sbwtime[0] + NILFS_SB_FREQ;
213} 213}
214 214
215static inline int nilfs_altsb_need_update(struct the_nilfs *nilfs) 215static inline int nilfs_sb_will_flip(struct the_nilfs *nilfs)
216{ 216{
217 u64 t = get_seconds(); 217 int flip_bits = nilfs->ns_sbwcount & 0x0FL;
218 struct nilfs_super_block **sbp = nilfs->ns_sbp; 218 return (flip_bits != 0x08 && flip_bits != 0x0F);
219 return sbp[1] && t > nilfs->ns_sbwtime[1] + NILFS_ALTSB_FREQ;
220} 219}
221 220
222void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64); 221void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64);