static void account_shadow(void *_shadow, unsigned long _sb, u64 ignore, size_t ignore2) { struct logfs_shadow *shadow = _shadow; struct super_block *sb = (void *)_sb; struct logfs_super *super = logfs_super(sb); super->s_free_bytes -= shadow->new_len; super->s_used_bytes += shadow->new_len; super->s_dirty_used_bytes -= shadow->new_len; super->s_free_bytes += shadow->old_len; super->s_used_bytes -= shadow->old_len; super->s_dirty_free_bytes -= shadow->old_len; logfs_set_segment_used(sb, shadow->old_ofs, -shadow->old_len); logfs_set_segment_used(sb, shadow->new_ofs, shadow->new_len); log_journal("account_shadow(%llx, %llx, %x) %llx->%llx %x->%x\n", shadow->ino, shadow->bix, shadow->gc_level, shadow->old_ofs, shadow->new_ofs, shadow->old_len, shadow->new_len); mempool_free(shadow, super->s_shadow_pool); }
void do_logfs_journal_wl_pass(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); struct logfs_area *area = super->s_journal_area; u32 segno, ec; int i, err; log_journal("Journal requires wear-leveling.\n"); /* Drop old segments */ journal_for_each(i) if (super->s_journal_seg[i]) { logfs_set_segment_unreserved(sb, super->s_journal_seg[i], super->s_journal_ec[i]); super->s_journal_seg[i] = 0; super->s_journal_ec[i] = 0; } /* Get new segments */ for (i = 0; i < super->s_no_journal_segs; i++) { segno = get_best_cand(sb, &super->s_reserve_list, &ec); super->s_journal_seg[i] = segno; super->s_journal_ec[i] = ec; logfs_set_segment_reserved(sb, segno); } /* Manually move journal_area */ area->a_segno = super->s_journal_seg[0]; area->a_is_open = 0; area->a_used_bytes = 0; /* Write journal */ logfs_write_anchor(sb); /* Write superblocks */ err = logfs_write_sb(sb); BUG_ON(err); }
void do_logfs_journal_wl_pass(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); struct logfs_area *area = super->s_journal_area; struct btree_head32 *head = &super->s_reserved_segments; u32 segno, ec; int i, err; log_journal("Journal requires wear-leveling.\n"); /* Drop old segments */ journal_for_each(i) if (super->s_journal_seg[i]) { btree_remove32(head, super->s_journal_seg[i]); logfs_set_segment_unreserved(sb, super->s_journal_seg[i], super->s_journal_ec[i]); super->s_journal_seg[i] = 0; super->s_journal_ec[i] = 0; } /* Get new segments */ for (i = 0; i < super->s_no_journal_segs; i++) { segno = get_best_cand(sb, &super->s_reserve_list, &ec); super->s_journal_seg[i] = segno; super->s_journal_ec[i] = ec; logfs_set_segment_reserved(sb, segno); <<<<<<< HEAD err = btree_insert32(head, segno, (void *)1, GFP_NOFS); =======
static int logfs_read_segment(struct super_block *sb, u32 segno) { struct logfs_super *super = logfs_super(sb); struct logfs_journal_header *jh = super->s_compressed_je; u64 ofs, seg_ofs = dev_ofs(sb, segno, 0); u32 h_ofs, last_ofs = 0; u16 len, datalen, last_len = 0; int i, err; for (h_ofs = 0; h_ofs < super->s_segsize; h_ofs += sizeof(*jh)) { ofs = seg_ofs + h_ofs; err = __read_je_header(sb, ofs, jh); if (err) continue; if (jh->h_type != cpu_to_be16(JE_COMMIT)) continue; err = __read_je_payload(sb, ofs, jh); if (err) continue; len = be16_to_cpu(jh->h_len); datalen = be16_to_cpu(jh->h_datalen); if ((datalen > sizeof(super->s_je_array)) || (datalen % sizeof(__be64))) continue; last_ofs = h_ofs; last_len = datalen; h_ofs += ALIGN(len, sizeof(*jh)) - sizeof(*jh); } if (last_ofs == 0) return -ENOENT; ofs = seg_ofs + last_ofs; log_journal("Read commit from %llx\n", ofs); err = __read_je(sb, ofs, jh); BUG_ON(err); if (err) return err; unpack(jh, super->s_je_array); super->s_no_je = last_len / sizeof(__be64); for (i = 0; i < super->s_no_je; i++) { err = read_je(sb, be64_to_cpu(super->s_je_array[i])); if (err) return err; } super->s_journal_area->a_segno = segno; return 0; }
static int logfs_write_obj_aliases(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); int err; log_journal("logfs_write_obj_aliases: %d aliases to write\n", super->s_no_object_aliases); super->s_je_fill = 0; err = logfs_write_obj_aliases_pagecache(sb); if (err) return err; if (super->s_je_fill) err = logfs_write_je_buf(sb, super->s_je, JE_OBJ_ALIAS, super->s_je_fill * sizeof(struct logfs_obj_alias)); return err; }
void do_logfs_journal_wl_pass(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); struct logfs_area *area = super->s_journal_area; struct btree_head32 *head = &super->s_reserved_segments; u32 segno, ec; int i, err; log_journal("Journal requires wear-leveling.\n"); journal_for_each(i) if (super->s_journal_seg[i]) { btree_remove32(head, super->s_journal_seg[i]); logfs_set_segment_unreserved(sb, super->s_journal_seg[i], super->s_journal_ec[i]); super->s_journal_seg[i] = 0; super->s_journal_ec[i] = 0; } for (i = 0; i < super->s_no_journal_segs; i++) { segno = get_best_cand(sb, &super->s_reserve_list, &ec); super->s_journal_seg[i] = segno; super->s_journal_ec[i] = ec; logfs_set_segment_reserved(sb, segno); err = btree_insert32(head, segno, (void *)1, GFP_NOFS); BUG_ON(err); err = logfs_erase_segment(sb, segno, 1); BUG_ON(err); } freeseg(sb, area->a_segno); area->a_segno = super->s_journal_seg[0]; area->a_is_open = 0; area->a_used_bytes = 0; logfs_write_anchor(sb); err = logfs_write_sb(sb); BUG_ON(err); }
void do_logfs_journal_wl_pass(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); struct logfs_area *area = super->s_journal_area; struct btree_head32 *head = &super->s_reserved_segments; u32 segno, ec; int i, err; log_journal("Journal requires wear-leveling.\n"); /* Drop old segments */ journal_for_each(i) if (super->s_journal_seg[i]) { btree_remove32(head, super->s_journal_seg[i]); logfs_set_segment_unreserved(sb, super->s_journal_seg[i], super->s_journal_ec[i]); super->s_journal_seg[i] = 0; super->s_journal_ec[i] = 0; } /* Get new segments */ for (i = 0; i < super->s_no_journal_segs; i++) { segno = get_best_cand(sb, &super->s_reserve_list, &ec); super->s_journal_seg[i] = segno; super->s_journal_ec[i] = ec; logfs_set_segment_reserved(sb, segno); err = btree_insert32(head, segno, (void *)1, GFP_KERNEL); BUG_ON(err); /* mempool should prevent this */ err = logfs_erase_segment(sb, segno, 1); BUG_ON(err); /* FIXME: remount-ro would be nicer */ } /* Manually move journal_area */ freeseg(sb, area->a_segno); area->a_segno = super->s_journal_seg[0]; area->a_is_open = 0; area->a_used_bytes = 0; /* Write journal */ logfs_write_anchor(sb); /* Write superblocks */ err = logfs_write_sb(sb); BUG_ON(err); }
static void journal_get_free_segment(struct logfs_area *area) { struct logfs_super *super = logfs_super(area->a_sb); int i; journal_for_each(i) { if (area->a_segno != super->s_journal_seg[i]) continue; do { i++; if (i == LOGFS_JOURNAL_SEGS) i = 0; } while (!super->s_journal_seg[i]); area->a_segno = super->s_journal_seg[i]; area->a_erase_count = ++(super->s_journal_ec[i]); log_journal("Journal now at %x (ec %x)\n", area->a_segno, area->a_erase_count); return; } BUG(); }
/* * Write all journal entries. The goto logic ensures that all journal entries * are written whenever a new segment is used. It is ugly and potentially a * bit wasteful, but robustness is more important. With this we can *always* * erase all journal segments except the one containing the most recent commit. */ void logfs_write_anchor(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); struct logfs_area *area = super->s_journal_area; int i, err; if (!(super->s_flags & LOGFS_SB_FLAG_DIRTY)) return; super->s_flags &= ~LOGFS_SB_FLAG_DIRTY; BUG_ON(super->s_flags & LOGFS_SB_FLAG_SHUTDOWN); mutex_lock(&super->s_journal_mutex); logfs_sync_segments(sb); account_shadows(sb); again: super->s_no_je = 0; for_each_area(i) { if (!super->s_area[i]->a_is_open) continue; super->s_sum_index = i; err = logfs_write_je(sb, logfs_write_area); if (err) goto again; } err = logfs_write_obj_aliases(sb); if (err) goto again; err = logfs_write_je(sb, logfs_write_erasecount); if (err) goto again; err = logfs_write_je(sb, __logfs_write_anchor); if (err) goto again; err = logfs_write_je(sb, logfs_write_dynsb); if (err) goto again; /* * Order is imperative. First we sync all writes, including the * non-committed journal writes. Then we write the final commit and * sync the current journal segment. * There is a theoretical bug here. Syncing the journal segment will * write a number of journal entries and the final commit. All these * are written in a single operation. If the device layer writes the * data back-to-front, the commit will precede the other journal * entries, leaving a race window. * Two fixes are possible. Preferred is to fix the device layer to * ensure writes happen front-to-back. Alternatively we can insert * another logfs_sync_area() super->s_devops->sync() combo before * writing the commit. */ super->s_devops->sync(sb); err = logfs_write_je(sb, logfs_write_commit); if (err) goto again; log_journal("Write commit to %llx\n", be64_to_cpu(super->s_je_array[super->s_no_je - 1])); logfs_sync_area(area); BUG_ON(area->a_used_bytes != area->a_written_bytes); super->s_devops->sync(sb); mutex_unlock(&super->s_journal_mutex); return; }