OSDN Git Service

Merge android-4.4-p.195 (4af3204) into msm-4.4
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / f2fs / segment.c
index 5f02c71..8f1af57 100644 (file)
@@ -873,11 +873,14 @@ void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi)
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
-int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
+block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi)
 {
+       int ovp_hole_segs =
+               (overprovision_segments(sbi) - reserved_segments(sbi));
+       block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg;
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
-       block_t ovp = overprovision_segments(sbi) << sbi->log_blocks_per_seg;
        block_t holes[2] = {0, 0};      /* DATA and NODE */
+       block_t unusable;
        struct seg_entry *se;
        unsigned int segno;
 
@@ -891,10 +894,20 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
        }
        mutex_unlock(&dirty_i->seglist_lock);
 
-       if (holes[DATA] > ovp || holes[NODE] > ovp)
+       unusable = holes[DATA] > holes[NODE] ? holes[DATA] : holes[NODE];
+       if (unusable > ovp_holes)
+               return unusable - ovp_holes;
+       return 0;
+}
+
+int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable)
+{
+       int ovp_hole_segs =
+               (overprovision_segments(sbi) - reserved_segments(sbi));
+       if (unusable > F2FS_OPTION(sbi).unusable_cap)
                return -EAGAIN;
        if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) &&
-               dirty_segments(sbi) > overprovision_segments(sbi))
+               dirty_segments(sbi) > ovp_hole_segs)
                return -EAGAIN;
        return 0;
 }
@@ -1560,6 +1573,10 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
                list_for_each_entry_safe(dc, tmp, pend_list, list) {
                        f2fs_bug_on(sbi, dc->state != D_PREP);
 
+                       if (dpolicy->timeout != 0 &&
+                               f2fs_time_over(sbi, dpolicy->timeout))
+                               break;
+
                        if (dpolicy->io_aware && i < dpolicy->io_aware_gran &&
                                                !is_idle(sbi, DISCARD_TIME)) {
                                io_interrupted = true;
@@ -1820,8 +1837,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
                devi = f2fs_target_device_index(sbi, blkstart);
                if (blkstart < FDEV(devi).start_blk ||
                    blkstart > FDEV(devi).end_blk) {
-                       f2fs_msg(sbi->sb, KERN_ERR, "Invalid block %x",
-                                blkstart);
+                       f2fs_err(sbi, "Invalid block %x", blkstart);
                        return -EIO;
                }
                blkstart -= FDEV(devi).start_blk;
@@ -1834,10 +1850,9 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
 
                if (sector & (bdev_zone_sectors(bdev) - 1) ||
                                nr_sects != bdev_zone_sectors(bdev)) {
-                       f2fs_msg(sbi->sb, KERN_ERR,
-                               "(%d) %s: Unaligned zone reset attempted (block %x + %x)",
-                               devi, sbi->s_ndevs ? FDEV(devi).path: "",
-                               blkstart, blklen);
+                       f2fs_err(sbi, "(%d) %s: Unaligned zone reset attempted (block %x + %x)",
+                                devi, sbi->s_ndevs ? FDEV(devi).path : "",
+                                blkstart, blklen);
                        return -EIO;
                }
                trace_f2fs_issue_reset_zone(bdev, blkstart);
@@ -2201,15 +2216,14 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
                mir_exist = f2fs_test_and_set_bit(offset,
                                                se->cur_valid_map_mir);
                if (unlikely(exist != mir_exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error "
-                               "when setting bitmap, blk:%u, old bit:%d",
-                               blkaddr, exist);
+                       f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d",
+                                blkaddr, exist);
                        f2fs_bug_on(sbi, 1);
                }
 #endif
                if (unlikely(exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR,
-                               "Bitmap was wrongly set, blk:%u", blkaddr);
+                       f2fs_err(sbi, "Bitmap was wrongly set, blk:%u",
+                                blkaddr);
                        f2fs_bug_on(sbi, 1);
                        se->valid_blocks--;
                        del = 0;
@@ -2230,15 +2244,14 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
                mir_exist = f2fs_test_and_clear_bit(offset,
                                                se->cur_valid_map_mir);
                if (unlikely(exist != mir_exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error "
-                               "when clearing bitmap, blk:%u, old bit:%d",
-                               blkaddr, exist);
+                       f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d",
+                                blkaddr, exist);
                        f2fs_bug_on(sbi, 1);
                }
 #endif
                if (unlikely(!exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR,
-                               "Bitmap was wrongly cleared, blk:%u", blkaddr);
+                       f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u",
+                                blkaddr);
                        f2fs_bug_on(sbi, 1);
                        se->valid_blocks++;
                        del = 0;
@@ -2720,6 +2733,39 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
        stat_inc_seg_type(sbi, curseg);
 }
 
+void allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
+                                       unsigned int start, unsigned int end)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+       unsigned int segno;
+
+       down_read(&SM_I(sbi)->curseg_lock);
+       mutex_lock(&curseg->curseg_mutex);
+       down_write(&SIT_I(sbi)->sentry_lock);
+
+       segno = CURSEG_I(sbi, type)->segno;
+       if (segno < start || segno > end)
+               goto unlock;
+
+       if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
+               change_curseg(sbi, type);
+       else
+               new_curseg(sbi, type, true);
+
+       stat_inc_seg_type(sbi, curseg);
+
+       locate_dirty_segment(sbi, segno);
+unlock:
+       up_write(&SIT_I(sbi)->sentry_lock);
+
+       if (segno != curseg->segno)
+               f2fs_notice(sbi, "For resize: curseg of type %d: %u ==> %u",
+                           type, segno, curseg->segno);
+
+       mutex_unlock(&curseg->curseg_mutex);
+       up_read(&SM_I(sbi)->curseg_lock);
+}
+
 void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi)
 {
        struct curseg_info *curseg;
@@ -2852,9 +2898,8 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
                goto out;
 
        if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) {
-               f2fs_msg(sbi->sb, KERN_WARNING,
-                       "Found FS corruption, run fsck to fix.");
-               return -EIO;
+               f2fs_warn(sbi, "Found FS corruption, run fsck to fix.");
+               return -EFSCORRUPTED;
        }
 
        /* start/end segment number in main_area */
@@ -3278,7 +3323,9 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
 
        if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) {
                set_sbi_flag(sbi, SBI_NEED_FSCK);
-               return -EFAULT;
+               f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.",
+                         __func__, segno);
+               return -EFSCORRUPTED;
        }
 
        stat_inc_inplace_blocks(fio->sbi);
@@ -3614,8 +3661,11 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
 
        /* sanity check for summary blocks */
        if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES ||
-                       sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES)
+                       sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) {
+               f2fs_err(sbi, "invalid journal entries nats %u sits %u\n",
+                        nats_in_cursum(nat_j), sits_in_cursum(sit_j));
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -3846,7 +3896,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        struct f2fs_journal *journal = curseg->journal;
        struct sit_entry_set *ses, *tmp;
        struct list_head *head = &SM_I(sbi)->sit_entry_set;
-       bool to_journal = true;
+       bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS);
        struct seg_entry *se;
 
        down_write(&sit_i->sentry_lock);
@@ -3865,7 +3915,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
         * entries, remove all entries from journal and add and account
         * them in sit entry set.
         */
-       if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL))
+       if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) ||
+                                                               !to_journal)
                remove_sits_in_journal(sbi);
 
        /*
@@ -4180,11 +4231,10 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
 
                start = le32_to_cpu(segno_in_journal(journal, i));
                if (start >= MAIN_SEGS(sbi)) {
-                       f2fs_msg(sbi->sb, KERN_ERR,
-                                       "Wrong journal entry on segno %u",
-                                       start);
+                       f2fs_err(sbi, "Wrong journal entry on segno %u",
+                                start);
                        set_sbi_flag(sbi, SBI_NEED_FSCK);
-                       err = -EINVAL;
+                       err = -EFSCORRUPTED;
                        break;
                }
 
@@ -4221,11 +4271,10 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
        up_read(&curseg->journal_rwsem);
 
        if (!err && total_node_blocks != valid_node_count(sbi)) {
-               f2fs_msg(sbi->sb, KERN_ERR,
-                       "SIT is corrupted node# %u vs %u",
-                       total_node_blocks, valid_node_count(sbi));
+               f2fs_err(sbi, "SIT is corrupted node# %u vs %u",
+                        total_node_blocks, valid_node_count(sbi));
                set_sbi_flag(sbi, SBI_NEED_FSCK);
-               err = -EINVAL;
+               err = -EFSCORRUPTED;
        }
 
        return err;
@@ -4316,6 +4365,41 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
        return init_victim_secmap(sbi);
 }
 
+static int sanity_check_curseg(struct f2fs_sb_info *sbi)
+{
+       int i;
+
+       /*
+        * In LFS/SSR curseg, .next_blkoff should point to an unused blkaddr;
+        * In LFS curseg, all blkaddr after .next_blkoff should be unused.
+        */
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
+               struct curseg_info *curseg = CURSEG_I(sbi, i);
+               struct seg_entry *se = get_seg_entry(sbi, curseg->segno);
+               unsigned int blkofs = curseg->next_blkoff;
+
+               if (f2fs_test_bit(blkofs, se->cur_valid_map))
+                       goto out;
+
+               if (curseg->alloc_type == SSR)
+                       continue;
+
+               for (blkofs += 1; blkofs < sbi->blocks_per_seg; blkofs++) {
+                       if (!f2fs_test_bit(blkofs, se->cur_valid_map))
+                               continue;
+out:
+                       f2fs_err(sbi,
+                                "Current segment's next free block offset is "
+                                "inconsistent with bitmap, logtype:%u, "
+                                "segno:%u, type:%u, next_blkoff:%u, blkofs:%u",
+                                i, curseg->segno, curseg->alloc_type,
+                                curseg->next_blkoff, blkofs);
+                       return -EFSCORRUPTED;
+               }
+       }
+       return 0;
+}
+
 /*
  * Update min, max modified time for cost-benefit GC algorithm
  */
@@ -4411,6 +4495,10 @@ int f2fs_build_segment_manager(struct f2fs_sb_info *sbi)
        if (err)
                return err;
 
+       err = sanity_check_curseg(sbi);
+       if (err)
+               return err;
+
        init_min_max_mtime(sbi);
        return 0;
 }