f2fs: fix to handle segment allocation failure correctly
[linux-2.6-microblaze.git] / fs / f2fs / segment.c
index dc51163..a5339dd 100644 (file)
@@ -400,6 +400,9 @@ int f2fs_commit_atomic_write(struct inode *inode)
  */
 void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
 {
+       if (f2fs_cp_error(sbi))
+               return;
+
        if (time_to_inject(sbi, FAULT_CHECKPOINT))
                f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_FAULT_INJECT);
 
@@ -2639,7 +2642,7 @@ static int is_next_segment_free(struct f2fs_sb_info *sbi,
  * Find a new segment from the free segments bitmap to right order
  * This function should be returned with success, otherwise BUG
  */
-static void get_new_segment(struct f2fs_sb_info *sbi,
+static int get_new_segment(struct f2fs_sb_info *sbi,
                        unsigned int *newseg, bool new_sec, bool pinning)
 {
        struct free_segmap_info *free_i = FREE_I(sbi);
@@ -2714,6 +2717,7 @@ out_unlock:
                f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_NO_SEGMENT);
                f2fs_bug_on(sbi, 1);
        }
+       return ret;
 }
 
 static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
@@ -2722,6 +2726,10 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
        struct summary_footer *sum_footer;
        unsigned short seg_type = curseg->seg_type;
 
+       /* only happen when get_new_segment() fails */
+       if (curseg->next_segno == NULL_SEGNO)
+               return;
+
        curseg->inited = true;
        curseg->segno = curseg->next_segno;
        curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno);
@@ -2786,7 +2794,10 @@ static int new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
                write_sum_page(sbi, curseg->sum_blk, GET_SUM_BLOCK(sbi, segno));
 
        segno = __get_next_segno(sbi, type);
-       get_new_segment(sbi, &segno, new_sec, pinning);
+       if (get_new_segment(sbi, &segno, new_sec, pinning)) {
+               curseg->segno = NULL_SEGNO;
+               return -ENOSPC;
+       }
        if (new_sec && pinning &&
            !f2fs_valid_pinned_area(sbi, START_BLOCK(sbi, segno))) {
                __set_free(sbi, segno);
@@ -3428,7 +3439,7 @@ static void f2fs_randomize_chunk(struct f2fs_sb_info *sbi,
                get_random_u32_inclusive(1, sbi->max_fragment_hole);
 }
 
-void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
+int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
                block_t old_blkaddr, block_t *new_blkaddr,
                struct f2fs_summary *sum, int type,
                struct f2fs_io_info *fio)
@@ -3445,6 +3456,9 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
        mutex_lock(&curseg->curseg_mutex);
        down_write(&sit_i->sentry_lock);
 
+       if (curseg->segno == NULL_SEGNO)
+               goto out_err;
+
        if (from_gc) {
                f2fs_bug_on(sbi, GET_SEGNO(sbi, old_blkaddr) == NULL_SEGNO);
                se = get_seg_entry(sbi, GET_SEGNO(sbi, old_blkaddr));
@@ -3504,6 +3518,9 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
                                change_curseg(sbi, type);
                        stat_inc_seg_type(sbi, curseg);
                }
+
+               if (curseg->segno == NULL_SEGNO)
+                       goto out_err;
        }
 
 skip_new_segment:
@@ -3538,8 +3555,15 @@ skip_new_segment:
        }
 
        mutex_unlock(&curseg->curseg_mutex);
-
        f2fs_up_read(&SM_I(sbi)->curseg_lock);
+       return 0;
+out_err:
+       *new_blkaddr = NULL_ADDR;
+       up_write(&sit_i->sentry_lock);
+       mutex_unlock(&curseg->curseg_mutex);
+       f2fs_up_read(&SM_I(sbi)->curseg_lock);
+       return -ENOSPC;
+
 }
 
 void f2fs_update_device_state(struct f2fs_sb_info *sbi, nid_t ino,
@@ -3577,8 +3601,16 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
        if (keep_order)
                f2fs_down_read(&fio->sbi->io_order_lock);
 
-       f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
-                       &fio->new_blkaddr, sum, type, fio);
+       if (f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
+                       &fio->new_blkaddr, sum, type, fio)) {
+               if (fscrypt_inode_uses_fs_layer_crypto(fio->page->mapping->host))
+                       fscrypt_finalize_bounce_page(&fio->encrypted_page);
+               if (PageWriteback(fio->page))
+                       end_page_writeback(fio->page);
+               if (f2fs_in_warm_node_list(fio->sbi, fio->page))
+                       f2fs_del_fsync_node_entry(fio->sbi, fio->page);
+               goto out;
+       }
        if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO)
                f2fs_invalidate_internal_cache(fio->sbi, fio->old_blkaddr);
 
@@ -3586,7 +3618,7 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
        f2fs_submit_page_write(fio);
 
        f2fs_update_device_state(fio->sbi, fio->ino, fio->new_blkaddr, 1);
-
+out:
        if (keep_order)
                f2fs_up_read(&fio->sbi->io_order_lock);
 }