]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/f2fs/segment.c
Merge tag 'tag-chrome-platform-for-v5.6' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / fs / f2fs / segment.c
index 5c3018743a9fadaa228a37f832ec31c8eff26322..cf0eb002cfd40ced0bd135ab9593f9d30df68af0 100644 (file)
@@ -334,7 +334,6 @@ void f2fs_drop_inmem_pages(struct inode *inode)
        }
 
        fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
-       stat_dec_atomic_write(inode);
 
        spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
        if (!list_empty(&fi->inmem_ilist))
@@ -505,7 +504,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
         * dir/node pages without enough free segments.
         */
        if (has_not_enough_free_secs(sbi, 0, 0)) {
-               mutex_lock(&sbi->gc_mutex);
+               down_write(&sbi->gc_lock);
                f2fs_gc(sbi, false, false, NULL_SEGNO);
        }
 }
@@ -2225,7 +2224,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
        struct sit_info *sit_i = SIT_I(sbi);
 
        f2fs_bug_on(sbi, addr == NULL_ADDR);
-       if (addr == NEW_ADDR)
+       if (addr == NEW_ADDR || addr == COMPRESS_ADDR)
                return;
 
        invalidate_mapping_pages(META_MAPPING(sbi), addr, addr);
@@ -2861,9 +2860,9 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
        if (sbi->discard_blks == 0)
                goto out;
 
-       mutex_lock(&sbi->gc_mutex);
+       down_write(&sbi->gc_lock);
        err = f2fs_write_checkpoint(sbi, &cpc);
-       mutex_unlock(&sbi->gc_mutex);
+       up_write(&sbi->gc_lock);
        if (err)
                goto out;
 
@@ -3036,7 +3035,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
        if (fio->type == DATA) {
                struct inode *inode = fio->page->mapping->host;
 
-               if (is_cold_data(fio->page) || file_is_cold(inode))
+               if (is_cold_data(fio->page) || file_is_cold(inode) ||
+                               f2fs_compressed_file(inode))
                        return CURSEG_COLD_DATA;
                if (file_is_hot(inode) ||
                                is_inode_flag_set(inode, FI_HOT_DATA) ||
@@ -3289,7 +3289,7 @@ int f2fs_inplace_write_data(struct f2fs_io_info *fio)
 
        stat_inc_inplace_blocks(fio->sbi);
 
-       if (fio->bio)
+       if (fio->bio && !(SM_I(sbi)->ipu_policy & (1 << F2FS_IPU_NOCACHE)))
                err = f2fs_merge_page_bio(fio);
        else
                err = f2fs_submit_page_bio(fio);
@@ -4370,6 +4370,90 @@ static int sanity_check_curseg(struct f2fs_sb_info *sbi)
 
 #ifdef CONFIG_BLK_DEV_ZONED
 
+static int check_zone_write_pointer(struct f2fs_sb_info *sbi,
+                                   struct f2fs_dev_info *fdev,
+                                   struct blk_zone *zone)
+{
+       unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno;
+       block_t zone_block, wp_block, last_valid_block;
+       unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT;
+       int i, s, b, ret;
+       struct seg_entry *se;
+
+       if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
+               return 0;
+
+       wp_block = fdev->start_blk + (zone->wp >> log_sectors_per_block);
+       wp_segno = GET_SEGNO(sbi, wp_block);
+       wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno);
+       zone_block = fdev->start_blk + (zone->start >> log_sectors_per_block);
+       zone_segno = GET_SEGNO(sbi, zone_block);
+       zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno);
+
+       if (zone_segno >= MAIN_SEGS(sbi))
+               return 0;
+
+       /*
+        * Skip check of zones cursegs point to, since
+        * fix_curseg_write_pointer() checks them.
+        */
+       for (i = 0; i < NO_CHECK_TYPE; i++)
+               if (zone_secno == GET_SEC_FROM_SEG(sbi,
+                                                  CURSEG_I(sbi, i)->segno))
+                       return 0;
+
+       /*
+        * Get last valid block of the zone.
+        */
+       last_valid_block = zone_block - 1;
+       for (s = sbi->segs_per_sec - 1; s >= 0; s--) {
+               segno = zone_segno + s;
+               se = get_seg_entry(sbi, segno);
+               for (b = sbi->blocks_per_seg - 1; b >= 0; b--)
+                       if (f2fs_test_bit(b, se->cur_valid_map)) {
+                               last_valid_block = START_BLOCK(sbi, segno) + b;
+                               break;
+                       }
+               if (last_valid_block >= zone_block)
+                       break;
+       }
+
+       /*
+        * If last valid block is beyond the write pointer, report the
+        * inconsistency. This inconsistency does not cause write error
+        * because the zone will not be selected for write operation until
+        * it get discarded. Just report it.
+        */
+       if (last_valid_block >= wp_block) {
+               f2fs_notice(sbi, "Valid block beyond write pointer: "
+                           "valid block[0x%x,0x%x] wp[0x%x,0x%x]",
+                           GET_SEGNO(sbi, last_valid_block),
+                           GET_BLKOFF_FROM_SEG0(sbi, last_valid_block),
+                           wp_segno, wp_blkoff);
+               return 0;
+       }
+
+       /*
+        * If there is no valid block in the zone and if write pointer is
+        * not at zone start, reset the write pointer.
+        */
+       if (last_valid_block + 1 == zone_block && zone->wp != zone->start) {
+               f2fs_notice(sbi,
+                           "Zone without valid block has non-zero write "
+                           "pointer. Reset the write pointer: wp[0x%x,0x%x]",
+                           wp_segno, wp_blkoff);
+               ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block,
+                                       zone->len >> log_sectors_per_block);
+               if (ret) {
+                       f2fs_err(sbi, "Discard zone failed: %s (errno=%d)",
+                                fdev->path, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi,
                                                  block_t zone_blkaddr)
 {
@@ -4441,6 +4525,10 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
                    "curseg[0x%x,0x%x]", type, cs->segno, cs->next_blkoff);
        allocate_segment_by_default(sbi, type, true);
 
+       /* check consistency of the zone curseg pointed to */
+       if (check_zone_write_pointer(sbi, zbd, &zone))
+               return -EIO;
+
        /* check newly assigned zone */
        cs_section = GET_SEC_FROM_SEG(sbi, cs->segno);
        cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section));
@@ -4492,11 +4580,49 @@ int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi)
 
        return 0;
 }
+
+struct check_zone_write_pointer_args {
+       struct f2fs_sb_info *sbi;
+       struct f2fs_dev_info *fdev;
+};
+
+static int check_zone_write_pointer_cb(struct blk_zone *zone, unsigned int idx,
+                                     void *data) {
+       struct check_zone_write_pointer_args *args;
+       args = (struct check_zone_write_pointer_args *)data;
+
+       return check_zone_write_pointer(args->sbi, args->fdev, zone);
+}
+
+int f2fs_check_write_pointer(struct f2fs_sb_info *sbi)
+{
+       int i, ret;
+       struct check_zone_write_pointer_args args;
+
+       for (i = 0; i < sbi->s_ndevs; i++) {
+               if (!bdev_is_zoned(FDEV(i).bdev))
+                       continue;
+
+               args.sbi = sbi;
+               args.fdev = &FDEV(i);
+               ret = blkdev_report_zones(FDEV(i).bdev, 0, BLK_ALL_ZONES,
+                                         check_zone_write_pointer_cb, &args);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
 #else
 int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi)
 {
        return 0;
 }
+
+int f2fs_check_write_pointer(struct f2fs_sb_info *sbi)
+{
+       return 0;
+}
 #endif
 
 /*