17c1a000dSChao Yu // SPDX-License-Identifier: GPL-2.0 20a8165d7SJaegeuk Kim /* 3351df4b2SJaegeuk Kim * fs/f2fs/segment.c 4351df4b2SJaegeuk Kim * 5351df4b2SJaegeuk Kim * Copyright (c) 2012 Samsung Electronics Co., Ltd. 6351df4b2SJaegeuk Kim * http://www.samsung.com/ 7351df4b2SJaegeuk Kim */ 8351df4b2SJaegeuk Kim #include <linux/fs.h> 9351df4b2SJaegeuk Kim #include <linux/f2fs_fs.h> 10351df4b2SJaegeuk Kim #include <linux/bio.h> 11351df4b2SJaegeuk Kim #include <linux/blkdev.h> 12690e4a3eSGeert Uytterhoeven #include <linux/prefetch.h> 136b4afdd7SJaegeuk Kim #include <linux/kthread.h> 1474de593aSChao Yu #include <linux/swap.h> 1560b99b48SJaegeuk Kim #include <linux/timer.h> 161d7be270SJaegeuk Kim #include <linux/freezer.h> 171eb1ef4aSJaegeuk Kim #include <linux/sched/signal.h> 18351df4b2SJaegeuk Kim 19351df4b2SJaegeuk Kim #include "f2fs.h" 20351df4b2SJaegeuk Kim #include "segment.h" 21351df4b2SJaegeuk Kim #include "node.h" 225f656541SJaegeuk Kim #include "gc.h" 236ec178daSNamjae Jeon #include <trace/events/f2fs.h> 24351df4b2SJaegeuk Kim 259a7f143aSChangman Lee #define __reverse_ffz(x) __reverse_ffs(~(x)) 269a7f143aSChangman Lee 277fd9e544SJaegeuk Kim static struct kmem_cache *discard_entry_slab; 28b01a9201SJaegeuk Kim static struct kmem_cache *discard_cmd_slab; 29184a5cd2SChao Yu static struct kmem_cache *sit_entry_set_slab; 3088b88a66SJaegeuk Kim static struct kmem_cache *inmem_entry_slab; 317fd9e544SJaegeuk Kim 32f96999c3SJaegeuk Kim static unsigned long __reverse_ulong(unsigned char *str) 33f96999c3SJaegeuk Kim { 34f96999c3SJaegeuk Kim unsigned long tmp = 0; 35f96999c3SJaegeuk Kim int shift = 24, idx = 0; 36f96999c3SJaegeuk Kim 37f96999c3SJaegeuk Kim #if BITS_PER_LONG == 64 38f96999c3SJaegeuk Kim shift = 56; 39f96999c3SJaegeuk Kim #endif 40f96999c3SJaegeuk Kim while (shift >= 0) { 41f96999c3SJaegeuk Kim tmp |= (unsigned long)str[idx++] << shift; 42f96999c3SJaegeuk Kim shift -= BITS_PER_BYTE; 43f96999c3SJaegeuk Kim } 44f96999c3SJaegeuk Kim return tmp; 45f96999c3SJaegeuk Kim } 46f96999c3SJaegeuk Kim 479a7f143aSChangman Lee /* 489a7f143aSChangman Lee * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since 499a7f143aSChangman Lee * MSB and LSB are reversed in a byte by f2fs_set_bit. 509a7f143aSChangman Lee */ 519a7f143aSChangman Lee static inline unsigned long __reverse_ffs(unsigned long word) 529a7f143aSChangman Lee { 539a7f143aSChangman Lee int num = 0; 549a7f143aSChangman Lee 559a7f143aSChangman Lee #if BITS_PER_LONG == 64 56f96999c3SJaegeuk Kim if ((word & 0xffffffff00000000UL) == 0) 579a7f143aSChangman Lee num += 32; 58f96999c3SJaegeuk Kim else 599a7f143aSChangman Lee word >>= 32; 609a7f143aSChangman Lee #endif 61f96999c3SJaegeuk Kim if ((word & 0xffff0000) == 0) 629a7f143aSChangman Lee num += 16; 63f96999c3SJaegeuk Kim else 649a7f143aSChangman Lee word >>= 16; 65f96999c3SJaegeuk Kim 66f96999c3SJaegeuk Kim if ((word & 0xff00) == 0) 679a7f143aSChangman Lee num += 8; 68f96999c3SJaegeuk Kim else 699a7f143aSChangman Lee word >>= 8; 70f96999c3SJaegeuk Kim 719a7f143aSChangman Lee if ((word & 0xf0) == 0) 729a7f143aSChangman Lee num += 4; 739a7f143aSChangman Lee else 749a7f143aSChangman Lee word >>= 4; 75f96999c3SJaegeuk Kim 769a7f143aSChangman Lee if ((word & 0xc) == 0) 779a7f143aSChangman Lee num += 2; 789a7f143aSChangman Lee else 799a7f143aSChangman Lee word >>= 2; 80f96999c3SJaegeuk Kim 819a7f143aSChangman Lee if ((word & 0x2) == 0) 829a7f143aSChangman Lee num += 1; 839a7f143aSChangman Lee return num; 849a7f143aSChangman Lee } 859a7f143aSChangman Lee 869a7f143aSChangman Lee /* 87e1c42045Sarter97 * __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c because 889a7f143aSChangman Lee * f2fs_set_bit makes MSB and LSB reversed in a byte. 89692223d1SFan Li * @size must be integral times of unsigned long. 909a7f143aSChangman Lee * Example: 91f96999c3SJaegeuk Kim * MSB <--> LSB 92f96999c3SJaegeuk Kim * f2fs_set_bit(0, bitmap) => 1000 0000 93f96999c3SJaegeuk Kim * f2fs_set_bit(7, bitmap) => 0000 0001 949a7f143aSChangman Lee */ 959a7f143aSChangman Lee static unsigned long __find_rev_next_bit(const unsigned long *addr, 969a7f143aSChangman Lee unsigned long size, unsigned long offset) 979a7f143aSChangman Lee { 989a7f143aSChangman Lee const unsigned long *p = addr + BIT_WORD(offset); 99692223d1SFan Li unsigned long result = size; 1009a7f143aSChangman Lee unsigned long tmp; 1019a7f143aSChangman Lee 1029a7f143aSChangman Lee if (offset >= size) 1039a7f143aSChangman Lee return size; 1049a7f143aSChangman Lee 105692223d1SFan Li size -= (offset & ~(BITS_PER_LONG - 1)); 1069a7f143aSChangman Lee offset %= BITS_PER_LONG; 107692223d1SFan Li 108692223d1SFan Li while (1) { 109692223d1SFan Li if (*p == 0) 110692223d1SFan Li goto pass; 1119a7f143aSChangman Lee 112f96999c3SJaegeuk Kim tmp = __reverse_ulong((unsigned char *)p); 113692223d1SFan Li 114f96999c3SJaegeuk Kim tmp &= ~0UL >> offset; 1159a7f143aSChangman Lee if (size < BITS_PER_LONG) 116692223d1SFan Li tmp &= (~0UL << (BITS_PER_LONG - size)); 1179a7f143aSChangman Lee if (tmp) 118692223d1SFan Li goto found; 119692223d1SFan Li pass: 120692223d1SFan Li if (size <= BITS_PER_LONG) 121692223d1SFan Li break; 1229a7f143aSChangman Lee size -= BITS_PER_LONG; 123692223d1SFan Li offset = 0; 124f96999c3SJaegeuk Kim p++; 1259a7f143aSChangman Lee } 1269a7f143aSChangman Lee return result; 127692223d1SFan Li found: 128692223d1SFan Li return result - size + __reverse_ffs(tmp); 1299a7f143aSChangman Lee } 1309a7f143aSChangman Lee 1319a7f143aSChangman Lee static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, 1329a7f143aSChangman Lee unsigned long size, unsigned long offset) 1339a7f143aSChangman Lee { 1349a7f143aSChangman Lee const unsigned long *p = addr + BIT_WORD(offset); 13580609448SJaegeuk Kim unsigned long result = size; 1369a7f143aSChangman Lee unsigned long tmp; 1379a7f143aSChangman Lee 1389a7f143aSChangman Lee if (offset >= size) 1399a7f143aSChangman Lee return size; 1409a7f143aSChangman Lee 14180609448SJaegeuk Kim size -= (offset & ~(BITS_PER_LONG - 1)); 1429a7f143aSChangman Lee offset %= BITS_PER_LONG; 14380609448SJaegeuk Kim 14480609448SJaegeuk Kim while (1) { 14580609448SJaegeuk Kim if (*p == ~0UL) 14680609448SJaegeuk Kim goto pass; 1479a7f143aSChangman Lee 148f96999c3SJaegeuk Kim tmp = __reverse_ulong((unsigned char *)p); 149f96999c3SJaegeuk Kim 15080609448SJaegeuk Kim if (offset) 15180609448SJaegeuk Kim tmp |= ~0UL << (BITS_PER_LONG - offset); 1529a7f143aSChangman Lee if (size < BITS_PER_LONG) 15380609448SJaegeuk Kim tmp |= ~0UL >> size; 154f96999c3SJaegeuk Kim if (tmp != ~0UL) 15580609448SJaegeuk Kim goto found; 15680609448SJaegeuk Kim pass: 15780609448SJaegeuk Kim if (size <= BITS_PER_LONG) 15880609448SJaegeuk Kim break; 1599a7f143aSChangman Lee size -= BITS_PER_LONG; 16080609448SJaegeuk Kim offset = 0; 161f96999c3SJaegeuk Kim p++; 1629a7f143aSChangman Lee } 1639a7f143aSChangman Lee return result; 16480609448SJaegeuk Kim found: 16580609448SJaegeuk Kim return result - size + __reverse_ffz(tmp); 1669a7f143aSChangman Lee } 1679a7f143aSChangman Lee 1684d57b86dSChao Yu bool f2fs_need_SSR(struct f2fs_sb_info *sbi) 169b3a97a2aSJaegeuk Kim { 170b3a97a2aSJaegeuk Kim int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); 171b3a97a2aSJaegeuk Kim int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); 172b3a97a2aSJaegeuk Kim int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); 173b3a97a2aSJaegeuk Kim 174b0332a0fSChao Yu if (f2fs_lfs_mode(sbi)) 175b3a97a2aSJaegeuk Kim return false; 1760e5e8111SDaeho Jeong if (sbi->gc_mode == GC_URGENT_HIGH) 177b3a97a2aSJaegeuk Kim return true; 1784354994fSDaniel Rosenberg if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 1794354994fSDaniel Rosenberg return true; 180b3a97a2aSJaegeuk Kim 181b3a97a2aSJaegeuk Kim return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + 182a2a12b67SChao Yu SM_I(sbi)->min_ssr_sections + reserved_sections(sbi)); 183b3a97a2aSJaegeuk Kim } 184b3a97a2aSJaegeuk Kim 1854d57b86dSChao Yu void f2fs_register_inmem_page(struct inode *inode, struct page *page) 18688b88a66SJaegeuk Kim { 18788b88a66SJaegeuk Kim struct inmem_pages *new; 1889be32d72SJaegeuk Kim 189e90027d2SXiaojun Wang f2fs_set_page_private(page, ATOMIC_WRITTEN_PAGE); 190decd36b6SChao Yu 19188b88a66SJaegeuk Kim new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS); 19288b88a66SJaegeuk Kim 19388b88a66SJaegeuk Kim /* add atomic page indices to the list */ 19488b88a66SJaegeuk Kim new->page = page; 19588b88a66SJaegeuk Kim INIT_LIST_HEAD(&new->list); 196decd36b6SChao Yu 19788b88a66SJaegeuk Kim /* increase reference count with clean state */ 19888b88a66SJaegeuk Kim get_page(page); 199743b620cSJaegeuk Kim mutex_lock(&F2FS_I(inode)->inmem_lock); 200743b620cSJaegeuk Kim list_add_tail(&new->list, &F2FS_I(inode)->inmem_pages); 2018dcf2ff7SJaegeuk Kim inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); 202743b620cSJaegeuk Kim mutex_unlock(&F2FS_I(inode)->inmem_lock); 2038ce67cb0SJaegeuk Kim 2048ce67cb0SJaegeuk Kim trace_f2fs_register_inmem_page(page, INMEM); 20588b88a66SJaegeuk Kim } 20688b88a66SJaegeuk Kim 20728bc106bSChao Yu static int __revoke_inmem_pages(struct inode *inode, 20848432984SChao Yu struct list_head *head, bool drop, bool recover, 20948432984SChao Yu bool trylock) 21029b96b54SChao Yu { 21128bc106bSChao Yu struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 21229b96b54SChao Yu struct inmem_pages *cur, *tmp; 21328bc106bSChao Yu int err = 0; 21429b96b54SChao Yu 21529b96b54SChao Yu list_for_each_entry_safe(cur, tmp, head, list) { 21628bc106bSChao Yu struct page *page = cur->page; 21729b96b54SChao Yu 21828bc106bSChao Yu if (drop) 21928bc106bSChao Yu trace_f2fs_commit_inmem_page(page, INMEM_DROP); 22028bc106bSChao Yu 22148432984SChao Yu if (trylock) { 22248432984SChao Yu /* 22348432984SChao Yu * to avoid deadlock in between page lock and 22448432984SChao Yu * inmem_lock. 22548432984SChao Yu */ 22648432984SChao Yu if (!trylock_page(page)) 22748432984SChao Yu continue; 22848432984SChao Yu } else { 22928bc106bSChao Yu lock_page(page); 23048432984SChao Yu } 23128bc106bSChao Yu 232bae0ee7aSChao Yu f2fs_wait_on_page_writeback(page, DATA, true, true); 233e5e5732dSChao Yu 23428bc106bSChao Yu if (recover) { 23528bc106bSChao Yu struct dnode_of_data dn; 23628bc106bSChao Yu struct node_info ni; 23728bc106bSChao Yu 23828bc106bSChao Yu trace_f2fs_commit_inmem_page(page, INMEM_REVOKE); 2397f2b4e8eSChao Yu retry: 24028bc106bSChao Yu set_new_dnode(&dn, inode, NULL, NULL, 0); 2414d57b86dSChao Yu err = f2fs_get_dnode_of_data(&dn, page->index, 2424d57b86dSChao Yu LOOKUP_NODE); 2437f2b4e8eSChao Yu if (err) { 2447f2b4e8eSChao Yu if (err == -ENOMEM) { 2455df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, 2465df7731fSChao Yu DEFAULT_IO_TIMEOUT); 2477f2b4e8eSChao Yu cond_resched(); 2487f2b4e8eSChao Yu goto retry; 2497f2b4e8eSChao Yu } 25028bc106bSChao Yu err = -EAGAIN; 25128bc106bSChao Yu goto next; 25228bc106bSChao Yu } 2537735730dSChao Yu 2547735730dSChao Yu err = f2fs_get_node_info(sbi, dn.nid, &ni); 2557735730dSChao Yu if (err) { 2567735730dSChao Yu f2fs_put_dnode(&dn); 2577735730dSChao Yu return err; 2587735730dSChao Yu } 2597735730dSChao Yu 260f1d2564aSDaeho Jeong if (cur->old_addr == NEW_ADDR) { 2614d57b86dSChao Yu f2fs_invalidate_blocks(sbi, dn.data_blkaddr); 262f1d2564aSDaeho Jeong f2fs_update_data_blkaddr(&dn, NEW_ADDR); 263f1d2564aSDaeho Jeong } else 26428bc106bSChao Yu f2fs_replace_block(sbi, &dn, dn.data_blkaddr, 26528bc106bSChao Yu cur->old_addr, ni.version, true, true); 26628bc106bSChao Yu f2fs_put_dnode(&dn); 26728bc106bSChao Yu } 26828bc106bSChao Yu next: 26963c52d78SJaegeuk Kim /* we don't need to invalidate this in the sccessful status */ 2702baf0781SChao Yu if (drop || recover) { 27128bc106bSChao Yu ClearPageUptodate(page); 2722baf0781SChao Yu clear_cold_data(page); 2732baf0781SChao Yu } 274240a5915SChao Yu f2fs_clear_page_private(page); 27528bc106bSChao Yu f2fs_put_page(page, 1); 27629b96b54SChao Yu 27729b96b54SChao Yu list_del(&cur->list); 27829b96b54SChao Yu kmem_cache_free(inmem_entry_slab, cur); 27929b96b54SChao Yu dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); 28029b96b54SChao Yu } 28128bc106bSChao Yu return err; 28229b96b54SChao Yu } 28329b96b54SChao Yu 2844d57b86dSChao Yu void f2fs_drop_inmem_pages_all(struct f2fs_sb_info *sbi, bool gc_failure) 28557864ae5SJaegeuk Kim { 28657864ae5SJaegeuk Kim struct list_head *head = &sbi->inode_list[ATOMIC_FILE]; 28757864ae5SJaegeuk Kim struct inode *inode; 28857864ae5SJaegeuk Kim struct f2fs_inode_info *fi; 289677017d1SSahitya Tummala unsigned int count = sbi->atomic_files; 290677017d1SSahitya Tummala unsigned int looped = 0; 29157864ae5SJaegeuk Kim next: 29257864ae5SJaegeuk Kim spin_lock(&sbi->inode_lock[ATOMIC_FILE]); 29357864ae5SJaegeuk Kim if (list_empty(head)) { 29457864ae5SJaegeuk Kim spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 29557864ae5SJaegeuk Kim return; 29657864ae5SJaegeuk Kim } 29757864ae5SJaegeuk Kim fi = list_first_entry(head, struct f2fs_inode_info, inmem_ilist); 29857864ae5SJaegeuk Kim inode = igrab(&fi->vfs_inode); 299677017d1SSahitya Tummala if (inode) 300677017d1SSahitya Tummala list_move_tail(&fi->inmem_ilist, head); 30157864ae5SJaegeuk Kim spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 30257864ae5SJaegeuk Kim 30357864ae5SJaegeuk Kim if (inode) { 3042ef79ecbSChao Yu if (gc_failure) { 305677017d1SSahitya Tummala if (!fi->i_gc_failures[GC_FAILURE_ATOMIC]) 3062ef79ecbSChao Yu goto skip; 3072ef79ecbSChao Yu } 3082ef79ecbSChao Yu set_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST); 3094d57b86dSChao Yu f2fs_drop_inmem_pages(inode); 310677017d1SSahitya Tummala skip: 31157864ae5SJaegeuk Kim iput(inode); 31257864ae5SJaegeuk Kim } 3135df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, DEFAULT_IO_TIMEOUT); 31457864ae5SJaegeuk Kim cond_resched(); 315677017d1SSahitya Tummala if (gc_failure) { 316677017d1SSahitya Tummala if (++looped >= count) 317677017d1SSahitya Tummala return; 318677017d1SSahitya Tummala } 31957864ae5SJaegeuk Kim goto next; 32057864ae5SJaegeuk Kim } 32157864ae5SJaegeuk Kim 3224d57b86dSChao Yu void f2fs_drop_inmem_pages(struct inode *inode) 32329b96b54SChao Yu { 32457864ae5SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 32529b96b54SChao Yu struct f2fs_inode_info *fi = F2FS_I(inode); 32629b96b54SChao Yu 327be1ee45dSYi Zhuang do { 32829b96b54SChao Yu mutex_lock(&fi->inmem_lock); 329be1ee45dSYi Zhuang if (list_empty(&fi->inmem_pages)) { 3302ef79ecbSChao Yu fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0; 331743b620cSJaegeuk Kim 332743b620cSJaegeuk Kim spin_lock(&sbi->inode_lock[ATOMIC_FILE]); 333743b620cSJaegeuk Kim if (!list_empty(&fi->inmem_ilist)) 334743b620cSJaegeuk Kim list_del_init(&fi->inmem_ilist); 335677017d1SSahitya Tummala if (f2fs_is_atomic_file(inode)) { 336677017d1SSahitya Tummala clear_inode_flag(inode, FI_ATOMIC_FILE); 337677017d1SSahitya Tummala sbi->atomic_files--; 338677017d1SSahitya Tummala } 339743b620cSJaegeuk Kim spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 340be1ee45dSYi Zhuang 341be1ee45dSYi Zhuang mutex_unlock(&fi->inmem_lock); 342be1ee45dSYi Zhuang break; 343be1ee45dSYi Zhuang } 344be1ee45dSYi Zhuang __revoke_inmem_pages(inode, &fi->inmem_pages, 345be1ee45dSYi Zhuang true, false, true); 346be1ee45dSYi Zhuang mutex_unlock(&fi->inmem_lock); 347be1ee45dSYi Zhuang } while (1); 34829b96b54SChao Yu } 34929b96b54SChao Yu 3504d57b86dSChao Yu void f2fs_drop_inmem_page(struct inode *inode, struct page *page) 3518c242db9SJaegeuk Kim { 3528c242db9SJaegeuk Kim struct f2fs_inode_info *fi = F2FS_I(inode); 3538c242db9SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 3548c242db9SJaegeuk Kim struct list_head *head = &fi->inmem_pages; 3558c242db9SJaegeuk Kim struct inmem_pages *cur = NULL; 3568c242db9SJaegeuk Kim 3578c242db9SJaegeuk Kim f2fs_bug_on(sbi, !IS_ATOMIC_WRITTEN_PAGE(page)); 3588c242db9SJaegeuk Kim 3598c242db9SJaegeuk Kim mutex_lock(&fi->inmem_lock); 3608c242db9SJaegeuk Kim list_for_each_entry(cur, head, list) { 3618c242db9SJaegeuk Kim if (cur->page == page) 3628c242db9SJaegeuk Kim break; 3638c242db9SJaegeuk Kim } 3648c242db9SJaegeuk Kim 365d0891e84SSheng Yong f2fs_bug_on(sbi, list_empty(head) || cur->page != page); 3668c242db9SJaegeuk Kim list_del(&cur->list); 3678c242db9SJaegeuk Kim mutex_unlock(&fi->inmem_lock); 3688c242db9SJaegeuk Kim 3698c242db9SJaegeuk Kim dec_page_count(sbi, F2FS_INMEM_PAGES); 3708c242db9SJaegeuk Kim kmem_cache_free(inmem_entry_slab, cur); 3718c242db9SJaegeuk Kim 3728c242db9SJaegeuk Kim ClearPageUptodate(page); 373240a5915SChao Yu f2fs_clear_page_private(page); 3748c242db9SJaegeuk Kim f2fs_put_page(page, 0); 3758c242db9SJaegeuk Kim 3768c242db9SJaegeuk Kim trace_f2fs_commit_inmem_page(page, INMEM_INVALIDATE); 3778c242db9SJaegeuk Kim } 3788c242db9SJaegeuk Kim 3794d57b86dSChao Yu static int __f2fs_commit_inmem_pages(struct inode *inode) 38088b88a66SJaegeuk Kim { 38188b88a66SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 38288b88a66SJaegeuk Kim struct f2fs_inode_info *fi = F2FS_I(inode); 38388b88a66SJaegeuk Kim struct inmem_pages *cur, *tmp; 38488b88a66SJaegeuk Kim struct f2fs_io_info fio = { 38505ca3632SJaegeuk Kim .sbi = sbi, 38639d787beSChao Yu .ino = inode->i_ino, 38788b88a66SJaegeuk Kim .type = DATA, 38804d328deSMike Christie .op = REQ_OP_WRITE, 38970fd7614SChristoph Hellwig .op_flags = REQ_SYNC | REQ_PRIO, 390b0af6d49SChao Yu .io_type = FS_DATA_IO, 39188b88a66SJaegeuk Kim }; 392cf52b27aSChao Yu struct list_head revoke_list; 393bab475c5SChao Yu bool submit_bio = false; 394edb27deeSJaegeuk Kim int err = 0; 39588b88a66SJaegeuk Kim 396cf52b27aSChao Yu INIT_LIST_HEAD(&revoke_list); 397cf52b27aSChao Yu 39888b88a66SJaegeuk Kim list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { 39928bc106bSChao Yu struct page *page = cur->page; 40028bc106bSChao Yu 40128bc106bSChao Yu lock_page(page); 40228bc106bSChao Yu if (page->mapping == inode->i_mapping) { 40328bc106bSChao Yu trace_f2fs_commit_inmem_page(page, INMEM); 40428bc106bSChao Yu 405bae0ee7aSChao Yu f2fs_wait_on_page_writeback(page, DATA, true, true); 4068d64d365SChao Yu 4078d64d365SChao Yu set_page_dirty(page); 408933439c8SChao Yu if (clear_page_dirty_for_io(page)) { 40988b88a66SJaegeuk Kim inode_dec_dirty_pages(inode); 4104d57b86dSChao Yu f2fs_remove_dirty_inode(inode); 411933439c8SChao Yu } 412640cc189SJaegeuk Kim retry: 41328bc106bSChao Yu fio.page = page; 414e959c8f5SHou Pengyang fio.old_blkaddr = NULL_ADDR; 4154d978078SJaegeuk Kim fio.encrypted_page = NULL; 416cc15620bSJaegeuk Kim fio.need_lock = LOCK_DONE; 4174d57b86dSChao Yu err = f2fs_do_write_data_page(&fio); 418edb27deeSJaegeuk Kim if (err) { 419640cc189SJaegeuk Kim if (err == -ENOMEM) { 4205df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, 4215df7731fSChao Yu DEFAULT_IO_TIMEOUT); 422640cc189SJaegeuk Kim cond_resched(); 423640cc189SJaegeuk Kim goto retry; 424640cc189SJaegeuk Kim } 42528bc106bSChao Yu unlock_page(page); 426edb27deeSJaegeuk Kim break; 427edb27deeSJaegeuk Kim } 42828bc106bSChao Yu /* record old blkaddr for revoking */ 42928bc106bSChao Yu cur->old_addr = fio.old_blkaddr; 430bab475c5SChao Yu submit_bio = true; 43188b88a66SJaegeuk Kim } 43228bc106bSChao Yu unlock_page(page); 433cf52b27aSChao Yu list_move_tail(&cur->list, &revoke_list); 43488b88a66SJaegeuk Kim } 43529b96b54SChao Yu 436bab475c5SChao Yu if (submit_bio) 437bab475c5SChao Yu f2fs_submit_merged_write_cond(sbi, inode, NULL, 0, DATA); 43828bc106bSChao Yu 43928bc106bSChao Yu if (err) { 44028bc106bSChao Yu /* 44128bc106bSChao Yu * try to revoke all committed pages, but still we could fail 44228bc106bSChao Yu * due to no memory or other reason, if that happened, EAGAIN 44328bc106bSChao Yu * will be returned, which means in such case, transaction is 44428bc106bSChao Yu * already not integrity, caller should use journal to do the 44528bc106bSChao Yu * recovery or rewrite & commit last transaction. For other 44628bc106bSChao Yu * error number, revoking was done by filesystem itself. 44728bc106bSChao Yu */ 44848432984SChao Yu err = __revoke_inmem_pages(inode, &revoke_list, 44948432984SChao Yu false, true, false); 45028bc106bSChao Yu 45128bc106bSChao Yu /* drop all uncommitted pages */ 45248432984SChao Yu __revoke_inmem_pages(inode, &fi->inmem_pages, 45348432984SChao Yu true, false, false); 454cf52b27aSChao Yu } else { 45548432984SChao Yu __revoke_inmem_pages(inode, &revoke_list, 45648432984SChao Yu false, false, false); 45728bc106bSChao Yu } 458cf52b27aSChao Yu 459cf52b27aSChao Yu return err; 460cf52b27aSChao Yu } 461cf52b27aSChao Yu 4624d57b86dSChao Yu int f2fs_commit_inmem_pages(struct inode *inode) 463cf52b27aSChao Yu { 464cf52b27aSChao Yu struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 465cf52b27aSChao Yu struct f2fs_inode_info *fi = F2FS_I(inode); 466cf52b27aSChao Yu int err; 467cf52b27aSChao Yu 468cf52b27aSChao Yu f2fs_balance_fs(sbi, true); 469cf52b27aSChao Yu 4706f8d4455SJaegeuk Kim down_write(&fi->i_gc_rwsem[WRITE]); 4716f8d4455SJaegeuk Kim 4726f8d4455SJaegeuk Kim f2fs_lock_op(sbi); 473cf52b27aSChao Yu set_inode_flag(inode, FI_ATOMIC_COMMIT); 474cf52b27aSChao Yu 475cf52b27aSChao Yu mutex_lock(&fi->inmem_lock); 4764d57b86dSChao Yu err = __f2fs_commit_inmem_pages(inode); 47788b88a66SJaegeuk Kim mutex_unlock(&fi->inmem_lock); 47888b88a66SJaegeuk Kim 4795fe45743SChao Yu clear_inode_flag(inode, FI_ATOMIC_COMMIT); 4805fe45743SChao Yu 48188b88a66SJaegeuk Kim f2fs_unlock_op(sbi); 4826f8d4455SJaegeuk Kim up_write(&fi->i_gc_rwsem[WRITE]); 4836f8d4455SJaegeuk Kim 484edb27deeSJaegeuk Kim return err; 48588b88a66SJaegeuk Kim } 48688b88a66SJaegeuk Kim 4870a8165d7SJaegeuk Kim /* 488351df4b2SJaegeuk Kim * This function balances dirty node and dentry pages. 489351df4b2SJaegeuk Kim * In addition, it controls garbage collection. 490351df4b2SJaegeuk Kim */ 4912c4db1a6SJaegeuk Kim void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) 492351df4b2SJaegeuk Kim { 49355523519SChao Yu if (time_to_inject(sbi, FAULT_CHECKPOINT)) { 494c45d6002SChao Yu f2fs_show_injection_info(sbi, FAULT_CHECKPOINT); 4950f348028SChao Yu f2fs_stop_checkpoint(sbi, false); 49655523519SChao Yu } 4970f348028SChao Yu 498e589c2c4SJaegeuk Kim /* balance_fs_bg is able to be pending */ 499a7881893SJaegeuk Kim if (need && excess_cached_nats(sbi)) 5007bcd0cfaSChao Yu f2fs_balance_fs_bg(sbi, false); 501e589c2c4SJaegeuk Kim 50200e09c0bSChao Yu if (!f2fs_is_checkpoint_ready(sbi)) 5034354994fSDaniel Rosenberg return; 5044354994fSDaniel Rosenberg 505351df4b2SJaegeuk Kim /* 506029cd28cSJaegeuk Kim * We should do GC or end up with checkpoint, if there are so many dirty 507029cd28cSJaegeuk Kim * dir/node pages without enough free segments. 508351df4b2SJaegeuk Kim */ 5097f3037a5SJaegeuk Kim if (has_not_enough_free_secs(sbi, 0, 0)) { 5105911d2d1SChao Yu if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && 5115911d2d1SChao Yu sbi->gc_thread->f2fs_gc_task) { 5125911d2d1SChao Yu DEFINE_WAIT(wait); 5135911d2d1SChao Yu 5145911d2d1SChao Yu prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait, 5155911d2d1SChao Yu TASK_UNINTERRUPTIBLE); 5165911d2d1SChao Yu wake_up(&sbi->gc_thread->gc_wait_queue_head); 5175911d2d1SChao Yu io_schedule(); 5185911d2d1SChao Yu finish_wait(&sbi->gc_thread->fggc_wq, &wait); 5195911d2d1SChao Yu } else { 520fb24fea7SChao Yu down_write(&sbi->gc_lock); 5217dede886SChao Yu f2fs_gc(sbi, false, false, false, NULL_SEGNO); 522351df4b2SJaegeuk Kim } 523351df4b2SJaegeuk Kim } 5245911d2d1SChao Yu } 525351df4b2SJaegeuk Kim 5267bcd0cfaSChao Yu void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi, bool from_bg) 5274660f9c0SJaegeuk Kim { 52864c74a7aSChao Yu if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) 52964c74a7aSChao Yu return; 53064c74a7aSChao Yu 5311dcc336bSChao Yu /* try to shrink extent cache when there is no enough memory */ 5324d57b86dSChao Yu if (!f2fs_available_free_memory(sbi, EXTENT_CACHE)) 5331dcc336bSChao Yu f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER); 5341dcc336bSChao Yu 5351b38dc8eSJaegeuk Kim /* check the # of cached NAT entries */ 5364d57b86dSChao Yu if (!f2fs_available_free_memory(sbi, NAT_ENTRIES)) 5374d57b86dSChao Yu f2fs_try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK); 5381b38dc8eSJaegeuk Kim 5394d57b86dSChao Yu if (!f2fs_available_free_memory(sbi, FREE_NIDS)) 5404d57b86dSChao Yu f2fs_try_to_free_nids(sbi, MAX_FREE_NIDS); 541ad4edb83SJaegeuk Kim else 5424d57b86dSChao Yu f2fs_build_free_nids(sbi, false, false); 54331696580SChao Yu 544493720a4SChao Yu if (excess_dirty_nats(sbi) || excess_dirty_nodes(sbi) || 545493720a4SChao Yu excess_prefree_segs(sbi)) 546493720a4SChao Yu goto do_sync; 547493720a4SChao Yu 548493720a4SChao Yu /* there is background inflight IO or foreground operation recently */ 549493720a4SChao Yu if (is_inflight_io(sbi, REQ_TIME) || 550493720a4SChao Yu (!f2fs_time_over(sbi, REQ_TIME) && rwsem_is_locked(&sbi->cp_rwsem))) 551f455c8a5SJaegeuk Kim return; 552e5e7ea3cSJaegeuk Kim 553493720a4SChao Yu /* exceed periodical checkpoint timeout threshold */ 554493720a4SChao Yu if (f2fs_time_over(sbi, CP_TIME)) 555493720a4SChao Yu goto do_sync; 556493720a4SChao Yu 5574660f9c0SJaegeuk Kim /* checkpoint is the only way to shrink partial cached entries */ 558493720a4SChao Yu if (f2fs_available_free_memory(sbi, NAT_ENTRIES) || 559493720a4SChao Yu f2fs_available_free_memory(sbi, INO_ENTRIES)) 560493720a4SChao Yu return; 561493720a4SChao Yu 562493720a4SChao Yu do_sync: 5637bcd0cfaSChao Yu if (test_opt(sbi, DATA_FLUSH) && from_bg) { 564e9f5b8b8SChao Yu struct blk_plug plug; 565e9f5b8b8SChao Yu 566040d2bb3SChao Yu mutex_lock(&sbi->flush_lock); 567040d2bb3SChao Yu 568e9f5b8b8SChao Yu blk_start_plug(&plug); 5694d57b86dSChao Yu f2fs_sync_dirty_inodes(sbi, FILE_INODE); 570e9f5b8b8SChao Yu blk_finish_plug(&plug); 571040d2bb3SChao Yu 572040d2bb3SChao Yu mutex_unlock(&sbi->flush_lock); 573e9f5b8b8SChao Yu } 5744660f9c0SJaegeuk Kim f2fs_sync_fs(sbi->sb, true); 57542190d2aSJaegeuk Kim stat_inc_bg_cp_count(sbi->stat_info); 5764660f9c0SJaegeuk Kim } 5774660f9c0SJaegeuk Kim 57820fda56bSKinglong Mee static int __submit_flush_wait(struct f2fs_sb_info *sbi, 57920fda56bSKinglong Mee struct block_device *bdev) 5803c62be17SJaegeuk Kim { 58125ac8426SChristoph Hellwig int ret = blkdev_issue_flush(bdev); 58220fda56bSKinglong Mee 58320fda56bSKinglong Mee trace_f2fs_issue_flush(bdev, test_opt(sbi, NOBARRIER), 58420fda56bSKinglong Mee test_opt(sbi, FLUSH_MERGE), ret); 5853c62be17SJaegeuk Kim return ret; 5863c62be17SJaegeuk Kim } 5873c62be17SJaegeuk Kim 58839d787beSChao Yu static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino) 5893c62be17SJaegeuk Kim { 59039d787beSChao Yu int ret = 0; 5913c62be17SJaegeuk Kim int i; 5923c62be17SJaegeuk Kim 5930916878dSDamien Le Moal if (!f2fs_is_multi_device(sbi)) 59439d787beSChao Yu return __submit_flush_wait(sbi, sbi->sb->s_bdev); 59520fda56bSKinglong Mee 59639d787beSChao Yu for (i = 0; i < sbi->s_ndevs; i++) { 5974d57b86dSChao Yu if (!f2fs_is_dirty_device(sbi, ino, i, FLUSH_INO)) 59839d787beSChao Yu continue; 59920fda56bSKinglong Mee ret = __submit_flush_wait(sbi, FDEV(i).bdev); 6003c62be17SJaegeuk Kim if (ret) 6013c62be17SJaegeuk Kim break; 6023c62be17SJaegeuk Kim } 6033c62be17SJaegeuk Kim return ret; 6043c62be17SJaegeuk Kim } 6053c62be17SJaegeuk Kim 6062163d198SGu Zheng static int issue_flush_thread(void *data) 6076b4afdd7SJaegeuk Kim { 6086b4afdd7SJaegeuk Kim struct f2fs_sb_info *sbi = data; 609b01a9201SJaegeuk Kim struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 610a688b9d9SGu Zheng wait_queue_head_t *q = &fcc->flush_wait_queue; 6116b4afdd7SJaegeuk Kim repeat: 6126b4afdd7SJaegeuk Kim if (kthread_should_stop()) 6136b4afdd7SJaegeuk Kim return 0; 6146b4afdd7SJaegeuk Kim 615721bd4d5SGu Zheng if (!llist_empty(&fcc->issue_list)) { 6166b4afdd7SJaegeuk Kim struct flush_cmd *cmd, *next; 6176b4afdd7SJaegeuk Kim int ret; 6186b4afdd7SJaegeuk Kim 619721bd4d5SGu Zheng fcc->dispatch_list = llist_del_all(&fcc->issue_list); 620721bd4d5SGu Zheng fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); 621721bd4d5SGu Zheng 62239d787beSChao Yu cmd = llist_entry(fcc->dispatch_list, struct flush_cmd, llnode); 62339d787beSChao Yu 62439d787beSChao Yu ret = submit_flush_wait(sbi, cmd->ino); 6258b8dd65fSChao Yu atomic_inc(&fcc->issued_flush); 6268b8dd65fSChao Yu 627721bd4d5SGu Zheng llist_for_each_entry_safe(cmd, next, 628721bd4d5SGu Zheng fcc->dispatch_list, llnode) { 6296b4afdd7SJaegeuk Kim cmd->ret = ret; 6306b4afdd7SJaegeuk Kim complete(&cmd->wait); 6316b4afdd7SJaegeuk Kim } 632a688b9d9SGu Zheng fcc->dispatch_list = NULL; 6336b4afdd7SJaegeuk Kim } 6346b4afdd7SJaegeuk Kim 635a688b9d9SGu Zheng wait_event_interruptible(*q, 636721bd4d5SGu Zheng kthread_should_stop() || !llist_empty(&fcc->issue_list)); 6376b4afdd7SJaegeuk Kim goto repeat; 6386b4afdd7SJaegeuk Kim } 6396b4afdd7SJaegeuk Kim 64039d787beSChao Yu int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino) 6416b4afdd7SJaegeuk Kim { 642b01a9201SJaegeuk Kim struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 643adf8d90bSChao Yu struct flush_cmd cmd; 6448b8dd65fSChao Yu int ret; 6456b4afdd7SJaegeuk Kim 6460f7b2abdSJaegeuk Kim if (test_opt(sbi, NOBARRIER)) 6470f7b2abdSJaegeuk Kim return 0; 6480f7b2abdSJaegeuk Kim 6498b8dd65fSChao Yu if (!test_opt(sbi, FLUSH_MERGE)) { 65072691af6SJaegeuk Kim atomic_inc(&fcc->queued_flush); 65139d787beSChao Yu ret = submit_flush_wait(sbi, ino); 65272691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 6538b8dd65fSChao Yu atomic_inc(&fcc->issued_flush); 6548b8dd65fSChao Yu return ret; 6558b8dd65fSChao Yu } 6568b8dd65fSChao Yu 6570916878dSDamien Le Moal if (atomic_inc_return(&fcc->queued_flush) == 1 || 6580916878dSDamien Le Moal f2fs_is_multi_device(sbi)) { 65939d787beSChao Yu ret = submit_flush_wait(sbi, ino); 66072691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 6618b8dd65fSChao Yu 6628b8dd65fSChao Yu atomic_inc(&fcc->issued_flush); 663740432f8SJaegeuk Kim return ret; 664740432f8SJaegeuk Kim } 6656b4afdd7SJaegeuk Kim 66639d787beSChao Yu cmd.ino = ino; 667adf8d90bSChao Yu init_completion(&cmd.wait); 6686b4afdd7SJaegeuk Kim 669721bd4d5SGu Zheng llist_add(&cmd.llnode, &fcc->issue_list); 6706b4afdd7SJaegeuk Kim 6713b42c741SChao Yu /* 6723b42c741SChao Yu * update issue_list before we wake up issue_flush thread, this 6733b42c741SChao Yu * smp_mb() pairs with another barrier in ___wait_event(), see 6743b42c741SChao Yu * more details in comments of waitqueue_active(). 6753b42c741SChao Yu */ 6766f890df0SChao Yu smp_mb(); 6776f890df0SChao Yu 6786f890df0SChao Yu if (waitqueue_active(&fcc->flush_wait_queue)) 679a688b9d9SGu Zheng wake_up(&fcc->flush_wait_queue); 6806b4afdd7SJaegeuk Kim 6815eba8c5dSJaegeuk Kim if (fcc->f2fs_issue_flush) { 682adf8d90bSChao Yu wait_for_completion(&cmd.wait); 68372691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 6845eba8c5dSJaegeuk Kim } else { 685d3238691SChao Yu struct llist_node *list; 686d3238691SChao Yu 687d3238691SChao Yu list = llist_del_all(&fcc->issue_list); 688d3238691SChao Yu if (!list) { 689d3238691SChao Yu wait_for_completion(&cmd.wait); 69072691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 691d3238691SChao Yu } else { 692d3238691SChao Yu struct flush_cmd *tmp, *next; 693d3238691SChao Yu 69439d787beSChao Yu ret = submit_flush_wait(sbi, ino); 695d3238691SChao Yu 696d3238691SChao Yu llist_for_each_entry_safe(tmp, next, list, llnode) { 697d3238691SChao Yu if (tmp == &cmd) { 698d3238691SChao Yu cmd.ret = ret; 69972691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 700d3238691SChao Yu continue; 701d3238691SChao Yu } 702d3238691SChao Yu tmp->ret = ret; 703d3238691SChao Yu complete(&tmp->wait); 704d3238691SChao Yu } 705d3238691SChao Yu } 7065eba8c5dSJaegeuk Kim } 707adf8d90bSChao Yu 708adf8d90bSChao Yu return cmd.ret; 7096b4afdd7SJaegeuk Kim } 7106b4afdd7SJaegeuk Kim 7114d57b86dSChao Yu int f2fs_create_flush_cmd_control(struct f2fs_sb_info *sbi) 7122163d198SGu Zheng { 7132163d198SGu Zheng dev_t dev = sbi->sb->s_bdev->bd_dev; 7142163d198SGu Zheng struct flush_cmd_control *fcc; 7152163d198SGu Zheng int err = 0; 7162163d198SGu Zheng 717b01a9201SJaegeuk Kim if (SM_I(sbi)->fcc_info) { 718b01a9201SJaegeuk Kim fcc = SM_I(sbi)->fcc_info; 719d871cd04SYunlong Song if (fcc->f2fs_issue_flush) 720d871cd04SYunlong Song return err; 7215eba8c5dSJaegeuk Kim goto init_thread; 7225eba8c5dSJaegeuk Kim } 7235eba8c5dSJaegeuk Kim 724acbf054dSChao Yu fcc = f2fs_kzalloc(sbi, sizeof(struct flush_cmd_control), GFP_KERNEL); 7252163d198SGu Zheng if (!fcc) 7262163d198SGu Zheng return -ENOMEM; 7278b8dd65fSChao Yu atomic_set(&fcc->issued_flush, 0); 72872691af6SJaegeuk Kim atomic_set(&fcc->queued_flush, 0); 7292163d198SGu Zheng init_waitqueue_head(&fcc->flush_wait_queue); 730721bd4d5SGu Zheng init_llist_head(&fcc->issue_list); 731b01a9201SJaegeuk Kim SM_I(sbi)->fcc_info = fcc; 732d4fdf8baSYunlei He if (!test_opt(sbi, FLUSH_MERGE)) 733d4fdf8baSYunlei He return err; 734d4fdf8baSYunlei He 7355eba8c5dSJaegeuk Kim init_thread: 7362163d198SGu Zheng fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, 7372163d198SGu Zheng "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); 7382163d198SGu Zheng if (IS_ERR(fcc->f2fs_issue_flush)) { 7392163d198SGu Zheng err = PTR_ERR(fcc->f2fs_issue_flush); 740c8eb7024SChao Yu kfree(fcc); 741b01a9201SJaegeuk Kim SM_I(sbi)->fcc_info = NULL; 7422163d198SGu Zheng return err; 7432163d198SGu Zheng } 7442163d198SGu Zheng 7452163d198SGu Zheng return err; 7462163d198SGu Zheng } 7472163d198SGu Zheng 7484d57b86dSChao Yu void f2fs_destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) 7492163d198SGu Zheng { 750b01a9201SJaegeuk Kim struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 7512163d198SGu Zheng 7525eba8c5dSJaegeuk Kim if (fcc && fcc->f2fs_issue_flush) { 7535eba8c5dSJaegeuk Kim struct task_struct *flush_thread = fcc->f2fs_issue_flush; 7545eba8c5dSJaegeuk Kim 7555eba8c5dSJaegeuk Kim fcc->f2fs_issue_flush = NULL; 7565eba8c5dSJaegeuk Kim kthread_stop(flush_thread); 7575eba8c5dSJaegeuk Kim } 7585eba8c5dSJaegeuk Kim if (free) { 759c8eb7024SChao Yu kfree(fcc); 760b01a9201SJaegeuk Kim SM_I(sbi)->fcc_info = NULL; 7612163d198SGu Zheng } 7625eba8c5dSJaegeuk Kim } 7632163d198SGu Zheng 7641228b482SChao Yu int f2fs_flush_device_cache(struct f2fs_sb_info *sbi) 7651228b482SChao Yu { 7661228b482SChao Yu int ret = 0, i; 7671228b482SChao Yu 7680916878dSDamien Le Moal if (!f2fs_is_multi_device(sbi)) 7691228b482SChao Yu return 0; 7701228b482SChao Yu 7716ed29fe1SChao Yu if (test_opt(sbi, NOBARRIER)) 7726ed29fe1SChao Yu return 0; 7736ed29fe1SChao Yu 7741228b482SChao Yu for (i = 1; i < sbi->s_ndevs; i++) { 7751228b482SChao Yu if (!f2fs_test_bit(i, (char *)&sbi->dirty_device)) 7761228b482SChao Yu continue; 7771228b482SChao Yu ret = __submit_flush_wait(sbi, FDEV(i).bdev); 7781228b482SChao Yu if (ret) 7791228b482SChao Yu break; 7801228b482SChao Yu 7811228b482SChao Yu spin_lock(&sbi->dev_lock); 7821228b482SChao Yu f2fs_clear_bit(i, (char *)&sbi->dirty_device); 7831228b482SChao Yu spin_unlock(&sbi->dev_lock); 7841228b482SChao Yu } 7851228b482SChao Yu 7861228b482SChao Yu return ret; 7871228b482SChao Yu } 7881228b482SChao Yu 789351df4b2SJaegeuk Kim static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 790351df4b2SJaegeuk Kim enum dirty_type dirty_type) 791351df4b2SJaegeuk Kim { 792351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 793351df4b2SJaegeuk Kim 794351df4b2SJaegeuk Kim /* need not be added */ 795351df4b2SJaegeuk Kim if (IS_CURSEG(sbi, segno)) 796351df4b2SJaegeuk Kim return; 797351df4b2SJaegeuk Kim 798351df4b2SJaegeuk Kim if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) 799351df4b2SJaegeuk Kim dirty_i->nr_dirty[dirty_type]++; 800351df4b2SJaegeuk Kim 801351df4b2SJaegeuk Kim if (dirty_type == DIRTY) { 802351df4b2SJaegeuk Kim struct seg_entry *sentry = get_seg_entry(sbi, segno); 8034625d6aaSChangman Lee enum dirty_type t = sentry->type; 804b2f2c390SJaegeuk Kim 805ec325b52SJaegeuk Kim if (unlikely(t >= DIRTY)) { 806ec325b52SJaegeuk Kim f2fs_bug_on(sbi, 1); 807ec325b52SJaegeuk Kim return; 808ec325b52SJaegeuk Kim } 8094625d6aaSChangman Lee if (!test_and_set_bit(segno, dirty_i->dirty_segmap[t])) 8104625d6aaSChangman Lee dirty_i->nr_dirty[t]++; 811da52f8adSJack Qiu 812da52f8adSJack Qiu if (__is_large_section(sbi)) { 813da52f8adSJack Qiu unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 814123aaf77SShin'ichiro Kawasaki block_t valid_blocks = 815da52f8adSJack Qiu get_valid_blocks(sbi, segno, true); 816da52f8adSJack Qiu 817da52f8adSJack Qiu f2fs_bug_on(sbi, unlikely(!valid_blocks || 818da52f8adSJack Qiu valid_blocks == BLKS_PER_SEC(sbi))); 819da52f8adSJack Qiu 820da52f8adSJack Qiu if (!IS_CURSEC(sbi, secno)) 821da52f8adSJack Qiu set_bit(secno, dirty_i->dirty_secmap); 822da52f8adSJack Qiu } 823351df4b2SJaegeuk Kim } 824351df4b2SJaegeuk Kim } 825351df4b2SJaegeuk Kim 826351df4b2SJaegeuk Kim static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 827351df4b2SJaegeuk Kim enum dirty_type dirty_type) 828351df4b2SJaegeuk Kim { 829351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 830123aaf77SShin'ichiro Kawasaki block_t valid_blocks; 831351df4b2SJaegeuk Kim 832351df4b2SJaegeuk Kim if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type])) 833351df4b2SJaegeuk Kim dirty_i->nr_dirty[dirty_type]--; 834351df4b2SJaegeuk Kim 835351df4b2SJaegeuk Kim if (dirty_type == DIRTY) { 8364625d6aaSChangman Lee struct seg_entry *sentry = get_seg_entry(sbi, segno); 8374625d6aaSChangman Lee enum dirty_type t = sentry->type; 838b2f2c390SJaegeuk Kim 8394625d6aaSChangman Lee if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) 840b2f2c390SJaegeuk Kim dirty_i->nr_dirty[t]--; 841b2f2c390SJaegeuk Kim 842da52f8adSJack Qiu valid_blocks = get_valid_blocks(sbi, segno, true); 843da52f8adSJack Qiu if (valid_blocks == 0) { 8444ddb1a4dSJaegeuk Kim clear_bit(GET_SEC_FROM_SEG(sbi, segno), 8455ec4e49fSJaegeuk Kim dirty_i->victim_secmap); 846bbf9f7d9SSahitya Tummala #ifdef CONFIG_F2FS_CHECK_FS 847bbf9f7d9SSahitya Tummala clear_bit(segno, SIT_I(sbi)->invalid_segmap); 848bbf9f7d9SSahitya Tummala #endif 849bbf9f7d9SSahitya Tummala } 850da52f8adSJack Qiu if (__is_large_section(sbi)) { 851da52f8adSJack Qiu unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 852da52f8adSJack Qiu 853da52f8adSJack Qiu if (!valid_blocks || 854da52f8adSJack Qiu valid_blocks == BLKS_PER_SEC(sbi)) { 855da52f8adSJack Qiu clear_bit(secno, dirty_i->dirty_secmap); 856da52f8adSJack Qiu return; 857da52f8adSJack Qiu } 858da52f8adSJack Qiu 859da52f8adSJack Qiu if (!IS_CURSEC(sbi, secno)) 860da52f8adSJack Qiu set_bit(secno, dirty_i->dirty_secmap); 861da52f8adSJack Qiu } 862351df4b2SJaegeuk Kim } 863351df4b2SJaegeuk Kim } 864351df4b2SJaegeuk Kim 8650a8165d7SJaegeuk Kim /* 866351df4b2SJaegeuk Kim * Should not occur error such as -ENOMEM. 867351df4b2SJaegeuk Kim * Adding dirty entry into seglist is not critical operation. 868351df4b2SJaegeuk Kim * If a given segment is one of current working segments, it won't be added. 869351df4b2SJaegeuk Kim */ 8708d8451afSHaicheng Li static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) 871351df4b2SJaegeuk Kim { 872351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 8734354994fSDaniel Rosenberg unsigned short valid_blocks, ckpt_valid_blocks; 874de881df9SAravind Ramesh unsigned int usable_blocks; 875351df4b2SJaegeuk Kim 876351df4b2SJaegeuk Kim if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno)) 877351df4b2SJaegeuk Kim return; 878351df4b2SJaegeuk Kim 879de881df9SAravind Ramesh usable_blocks = f2fs_usable_blks_in_seg(sbi, segno); 880351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 881351df4b2SJaegeuk Kim 882302bd348SJaegeuk Kim valid_blocks = get_valid_blocks(sbi, segno, false); 88361461fc9SChao Yu ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno, false); 884351df4b2SJaegeuk Kim 8854354994fSDaniel Rosenberg if (valid_blocks == 0 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) || 886de881df9SAravind Ramesh ckpt_valid_blocks == usable_blocks)) { 887351df4b2SJaegeuk Kim __locate_dirty_segment(sbi, segno, PRE); 888351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, segno, DIRTY); 889de881df9SAravind Ramesh } else if (valid_blocks < usable_blocks) { 890351df4b2SJaegeuk Kim __locate_dirty_segment(sbi, segno, DIRTY); 891351df4b2SJaegeuk Kim } else { 892351df4b2SJaegeuk Kim /* Recovery routine with SSR needs this */ 893351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, segno, DIRTY); 894351df4b2SJaegeuk Kim } 895351df4b2SJaegeuk Kim 896351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 897351df4b2SJaegeuk Kim } 898351df4b2SJaegeuk Kim 8994354994fSDaniel Rosenberg /* This moves currently empty dirty blocks to prefree. Must hold seglist_lock */ 9004354994fSDaniel Rosenberg void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi) 9014354994fSDaniel Rosenberg { 9024354994fSDaniel Rosenberg struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9034354994fSDaniel Rosenberg unsigned int segno; 9044354994fSDaniel Rosenberg 9054354994fSDaniel Rosenberg mutex_lock(&dirty_i->seglist_lock); 9064354994fSDaniel Rosenberg for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 9074354994fSDaniel Rosenberg if (get_valid_blocks(sbi, segno, false)) 9084354994fSDaniel Rosenberg continue; 9094354994fSDaniel Rosenberg if (IS_CURSEG(sbi, segno)) 9104354994fSDaniel Rosenberg continue; 9114354994fSDaniel Rosenberg __locate_dirty_segment(sbi, segno, PRE); 9124354994fSDaniel Rosenberg __remove_dirty_segment(sbi, segno, DIRTY); 9134354994fSDaniel Rosenberg } 9144354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 9154354994fSDaniel Rosenberg } 9164354994fSDaniel Rosenberg 9174d3aed70SDaniel Rosenberg block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi) 9184354994fSDaniel Rosenberg { 919ae4ad7eaSDaniel Rosenberg int ovp_hole_segs = 920ae4ad7eaSDaniel Rosenberg (overprovision_segments(sbi) - reserved_segments(sbi)); 921ae4ad7eaSDaniel Rosenberg block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg; 9224d3aed70SDaniel Rosenberg struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9234354994fSDaniel Rosenberg block_t holes[2] = {0, 0}; /* DATA and NODE */ 9244d3aed70SDaniel Rosenberg block_t unusable; 9254354994fSDaniel Rosenberg struct seg_entry *se; 9264354994fSDaniel Rosenberg unsigned int segno; 9274354994fSDaniel Rosenberg 9284354994fSDaniel Rosenberg mutex_lock(&dirty_i->seglist_lock); 9294354994fSDaniel Rosenberg for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 9304354994fSDaniel Rosenberg se = get_seg_entry(sbi, segno); 9314354994fSDaniel Rosenberg if (IS_NODESEG(se->type)) 932de881df9SAravind Ramesh holes[NODE] += f2fs_usable_blks_in_seg(sbi, segno) - 933de881df9SAravind Ramesh se->valid_blocks; 9344354994fSDaniel Rosenberg else 935de881df9SAravind Ramesh holes[DATA] += f2fs_usable_blks_in_seg(sbi, segno) - 936de881df9SAravind Ramesh se->valid_blocks; 9374354994fSDaniel Rosenberg } 9384354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 9394354994fSDaniel Rosenberg 9404d3aed70SDaniel Rosenberg unusable = holes[DATA] > holes[NODE] ? holes[DATA] : holes[NODE]; 9414d3aed70SDaniel Rosenberg if (unusable > ovp_holes) 9424d3aed70SDaniel Rosenberg return unusable - ovp_holes; 9434d3aed70SDaniel Rosenberg return 0; 9444d3aed70SDaniel Rosenberg } 9454d3aed70SDaniel Rosenberg 9464d3aed70SDaniel Rosenberg int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable) 9474d3aed70SDaniel Rosenberg { 9484d3aed70SDaniel Rosenberg int ovp_hole_segs = 9494d3aed70SDaniel Rosenberg (overprovision_segments(sbi) - reserved_segments(sbi)); 9504d3aed70SDaniel Rosenberg if (unusable > F2FS_OPTION(sbi).unusable_cap) 9514354994fSDaniel Rosenberg return -EAGAIN; 952db610a64SJaegeuk Kim if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) && 953ae4ad7eaSDaniel Rosenberg dirty_segments(sbi) > ovp_hole_segs) 954db610a64SJaegeuk Kim return -EAGAIN; 9554354994fSDaniel Rosenberg return 0; 9564354994fSDaniel Rosenberg } 9574354994fSDaniel Rosenberg 9584354994fSDaniel Rosenberg /* This is only used by SBI_CP_DISABLED */ 9594354994fSDaniel Rosenberg static unsigned int get_free_segment(struct f2fs_sb_info *sbi) 9604354994fSDaniel Rosenberg { 9614354994fSDaniel Rosenberg struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9624354994fSDaniel Rosenberg unsigned int segno = 0; 9634354994fSDaniel Rosenberg 9644354994fSDaniel Rosenberg mutex_lock(&dirty_i->seglist_lock); 9654354994fSDaniel Rosenberg for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 9664354994fSDaniel Rosenberg if (get_valid_blocks(sbi, segno, false)) 9674354994fSDaniel Rosenberg continue; 96861461fc9SChao Yu if (get_ckpt_valid_blocks(sbi, segno, false)) 9694354994fSDaniel Rosenberg continue; 9704354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 9714354994fSDaniel Rosenberg return segno; 9724354994fSDaniel Rosenberg } 9734354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 9744354994fSDaniel Rosenberg return NULL_SEGNO; 9754354994fSDaniel Rosenberg } 9764354994fSDaniel Rosenberg 977004b6862SChao Yu static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, 978c81abe34SJaegeuk Kim struct block_device *bdev, block_t lstart, 979c81abe34SJaegeuk Kim block_t start, block_t len) 980275b66b0SChao Yu { 9810b54fb84SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 982ba48a33eSChao Yu struct list_head *pend_list; 983b01a9201SJaegeuk Kim struct discard_cmd *dc; 984275b66b0SChao Yu 985ba48a33eSChao Yu f2fs_bug_on(sbi, !len); 986ba48a33eSChao Yu 987ba48a33eSChao Yu pend_list = &dcc->pend_list[plist_idx(len)]; 988ba48a33eSChao Yu 989b01a9201SJaegeuk Kim dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS); 990b01a9201SJaegeuk Kim INIT_LIST_HEAD(&dc->list); 991c81abe34SJaegeuk Kim dc->bdev = bdev; 992b01a9201SJaegeuk Kim dc->lstart = lstart; 993c81abe34SJaegeuk Kim dc->start = start; 994b01a9201SJaegeuk Kim dc->len = len; 995ec9895adSChao Yu dc->ref = 0; 99615469963SJaegeuk Kim dc->state = D_PREP; 99772691af6SJaegeuk Kim dc->queued = 0; 998c81abe34SJaegeuk Kim dc->error = 0; 999b01a9201SJaegeuk Kim init_completion(&dc->wait); 100022d375ddSChao Yu list_add_tail(&dc->list, pend_list); 100135ec7d57SChao Yu spin_lock_init(&dc->lock); 100235ec7d57SChao Yu dc->bio_ref = 0; 10035f32366aSChao Yu atomic_inc(&dcc->discard_cmd_cnt); 1004d84d1cbdSChao Yu dcc->undiscard_blks += len; 1005004b6862SChao Yu 1006004b6862SChao Yu return dc; 100715469963SJaegeuk Kim } 100815469963SJaegeuk Kim 1009004b6862SChao Yu static struct discard_cmd *__attach_discard_cmd(struct f2fs_sb_info *sbi, 1010004b6862SChao Yu struct block_device *bdev, block_t lstart, 1011004b6862SChao Yu block_t start, block_t len, 10124dada3fdSChao Yu struct rb_node *parent, struct rb_node **p, 10134dada3fdSChao Yu bool leftmost) 1014004b6862SChao Yu { 1015004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1016004b6862SChao Yu struct discard_cmd *dc; 1017004b6862SChao Yu 1018004b6862SChao Yu dc = __create_discard_cmd(sbi, bdev, lstart, start, len); 1019004b6862SChao Yu 1020004b6862SChao Yu rb_link_node(&dc->rb_node, parent, p); 10214dada3fdSChao Yu rb_insert_color_cached(&dc->rb_node, &dcc->root, leftmost); 1022004b6862SChao Yu 1023004b6862SChao Yu return dc; 1024004b6862SChao Yu } 1025004b6862SChao Yu 1026004b6862SChao Yu static void __detach_discard_cmd(struct discard_cmd_control *dcc, 1027004b6862SChao Yu struct discard_cmd *dc) 102815469963SJaegeuk Kim { 1029dcc9165dSJaegeuk Kim if (dc->state == D_DONE) 103072691af6SJaegeuk Kim atomic_sub(dc->queued, &dcc->queued_discard); 1031004b6862SChao Yu 1032004b6862SChao Yu list_del(&dc->list); 10334dada3fdSChao Yu rb_erase_cached(&dc->rb_node, &dcc->root); 1034d84d1cbdSChao Yu dcc->undiscard_blks -= dc->len; 1035004b6862SChao Yu 1036004b6862SChao Yu kmem_cache_free(discard_cmd_slab, dc); 1037004b6862SChao Yu 1038004b6862SChao Yu atomic_dec(&dcc->discard_cmd_cnt); 1039004b6862SChao Yu } 1040004b6862SChao Yu 1041004b6862SChao Yu static void __remove_discard_cmd(struct f2fs_sb_info *sbi, 1042004b6862SChao Yu struct discard_cmd *dc) 1043004b6862SChao Yu { 1044004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 104535ec7d57SChao Yu unsigned long flags; 1046dcc9165dSJaegeuk Kim 10472ec6f2efSChao Yu trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len); 10482ec6f2efSChao Yu 104935ec7d57SChao Yu spin_lock_irqsave(&dc->lock, flags); 105035ec7d57SChao Yu if (dc->bio_ref) { 105135ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 105235ec7d57SChao Yu return; 105335ec7d57SChao Yu } 105435ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 105535ec7d57SChao Yu 1056d9703d90SChao Yu f2fs_bug_on(sbi, dc->ref); 1057d9703d90SChao Yu 1058c81abe34SJaegeuk Kim if (dc->error == -EOPNOTSUPP) 1059c81abe34SJaegeuk Kim dc->error = 0; 106015469963SJaegeuk Kim 1061c81abe34SJaegeuk Kim if (dc->error) 106222d7ea13SChao Yu printk_ratelimited( 1063c45d6002SChao Yu "%sF2FS-fs (%s): Issue discard(%u, %u, %u) failed, ret: %d", 1064c45d6002SChao Yu KERN_INFO, sbi->sb->s_id, 1065c45d6002SChao Yu dc->lstart, dc->start, dc->len, dc->error); 1066004b6862SChao Yu __detach_discard_cmd(dcc, dc); 1067275b66b0SChao Yu } 1068275b66b0SChao Yu 1069c81abe34SJaegeuk Kim static void f2fs_submit_discard_endio(struct bio *bio) 1070c81abe34SJaegeuk Kim { 1071c81abe34SJaegeuk Kim struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private; 107235ec7d57SChao Yu unsigned long flags; 1073c81abe34SJaegeuk Kim 107435ec7d57SChao Yu spin_lock_irqsave(&dc->lock, flags); 10753fa6a8c5SSahitya Tummala if (!dc->error) 10763fa6a8c5SSahitya Tummala dc->error = blk_status_to_errno(bio->bi_status); 107735ec7d57SChao Yu dc->bio_ref--; 107835ec7d57SChao Yu if (!dc->bio_ref && dc->state == D_SUBMIT) { 1079c81abe34SJaegeuk Kim dc->state = D_DONE; 1080e31b9821SChao Yu complete_all(&dc->wait); 108135ec7d57SChao Yu } 108235ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 1083c81abe34SJaegeuk Kim bio_put(bio); 1084c81abe34SJaegeuk Kim } 1085c81abe34SJaegeuk Kim 108694b1e10eSWei Yongjun static void __check_sit_bitmap(struct f2fs_sb_info *sbi, 10876915ea9dSChao Yu block_t start, block_t end) 10886915ea9dSChao Yu { 10896915ea9dSChao Yu #ifdef CONFIG_F2FS_CHECK_FS 10906915ea9dSChao Yu struct seg_entry *sentry; 10916915ea9dSChao Yu unsigned int segno; 10926915ea9dSChao Yu block_t blk = start; 10936915ea9dSChao Yu unsigned long offset, size, max_blocks = sbi->blocks_per_seg; 10946915ea9dSChao Yu unsigned long *map; 10956915ea9dSChao Yu 10966915ea9dSChao Yu while (blk < end) { 10976915ea9dSChao Yu segno = GET_SEGNO(sbi, blk); 10986915ea9dSChao Yu sentry = get_seg_entry(sbi, segno); 10996915ea9dSChao Yu offset = GET_BLKOFF_FROM_SEG0(sbi, blk); 11006915ea9dSChao Yu 1101008396e1SYunlong Song if (end < START_BLOCK(sbi, segno + 1)) 1102008396e1SYunlong Song size = GET_BLKOFF_FROM_SEG0(sbi, end); 1103008396e1SYunlong Song else 1104008396e1SYunlong Song size = max_blocks; 11056915ea9dSChao Yu map = (unsigned long *)(sentry->cur_valid_map); 11066915ea9dSChao Yu offset = __find_rev_next_bit(map, size, offset); 11076915ea9dSChao Yu f2fs_bug_on(sbi, offset != size); 1108008396e1SYunlong Song blk = START_BLOCK(sbi, segno + 1); 11096915ea9dSChao Yu } 11106915ea9dSChao Yu #endif 11116915ea9dSChao Yu } 11126915ea9dSChao Yu 11138bb4f253SJaegeuk Kim static void __init_discard_policy(struct f2fs_sb_info *sbi, 11148bb4f253SJaegeuk Kim struct discard_policy *dpolicy, 11158bb4f253SJaegeuk Kim int discard_type, unsigned int granularity) 11168bb4f253SJaegeuk Kim { 1117c35b8d5eSSahitya Tummala struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1118c35b8d5eSSahitya Tummala 11198bb4f253SJaegeuk Kim /* common policy */ 11208bb4f253SJaegeuk Kim dpolicy->type = discard_type; 11218bb4f253SJaegeuk Kim dpolicy->sync = true; 112220ee4382SChao Yu dpolicy->ordered = false; 11238bb4f253SJaegeuk Kim dpolicy->granularity = granularity; 11248bb4f253SJaegeuk Kim 11258bb4f253SJaegeuk Kim dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST; 11268bb4f253SJaegeuk Kim dpolicy->io_aware_gran = MAX_PLIST_NUM; 11276ce48b0cSChao Yu dpolicy->timeout = false; 11288bb4f253SJaegeuk Kim 11298bb4f253SJaegeuk Kim if (discard_type == DPOLICY_BG) { 11308bb4f253SJaegeuk Kim dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; 1131f9d1dcedSYunlei He dpolicy->mid_interval = DEF_MID_DISCARD_ISSUE_TIME; 11328bb4f253SJaegeuk Kim dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; 11338bb4f253SJaegeuk Kim dpolicy->io_aware = true; 1134cba60849SChao Yu dpolicy->sync = false; 113520ee4382SChao Yu dpolicy->ordered = true; 11368bb4f253SJaegeuk Kim if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) { 11378bb4f253SJaegeuk Kim dpolicy->granularity = 1; 1138c35b8d5eSSahitya Tummala if (atomic_read(&dcc->discard_cmd_cnt)) 1139c35b8d5eSSahitya Tummala dpolicy->max_interval = 1140c35b8d5eSSahitya Tummala DEF_MIN_DISCARD_ISSUE_TIME; 11418bb4f253SJaegeuk Kim } 11428bb4f253SJaegeuk Kim } else if (discard_type == DPOLICY_FORCE) { 11438bb4f253SJaegeuk Kim dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME; 1144f9d1dcedSYunlei He dpolicy->mid_interval = DEF_MID_DISCARD_ISSUE_TIME; 11458bb4f253SJaegeuk Kim dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME; 11468bb4f253SJaegeuk Kim dpolicy->io_aware = false; 11478bb4f253SJaegeuk Kim } else if (discard_type == DPOLICY_FSTRIM) { 11488bb4f253SJaegeuk Kim dpolicy->io_aware = false; 11498bb4f253SJaegeuk Kim } else if (discard_type == DPOLICY_UMOUNT) { 11508bb4f253SJaegeuk Kim dpolicy->io_aware = false; 1151b8623253SJaegeuk Kim /* we need to issue all to keep CP_TRIMMED_FLAG */ 1152b8623253SJaegeuk Kim dpolicy->granularity = 1; 11536ce48b0cSChao Yu dpolicy->timeout = true; 11548bb4f253SJaegeuk Kim } 11558bb4f253SJaegeuk Kim } 11568bb4f253SJaegeuk Kim 115735ec7d57SChao Yu static void __update_discard_tree_range(struct f2fs_sb_info *sbi, 115835ec7d57SChao Yu struct block_device *bdev, block_t lstart, 115935ec7d57SChao Yu block_t start, block_t len); 1160c81abe34SJaegeuk Kim /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ 11616b9cb124SChao Yu static int __submit_discard_cmd(struct f2fs_sb_info *sbi, 116278997b56SChao Yu struct discard_policy *dpolicy, 116335ec7d57SChao Yu struct discard_cmd *dc, 116435ec7d57SChao Yu unsigned int *issued) 1165c81abe34SJaegeuk Kim { 116635ec7d57SChao Yu struct block_device *bdev = dc->bdev; 116735ec7d57SChao Yu struct request_queue *q = bdev_get_queue(bdev); 116835ec7d57SChao Yu unsigned int max_discard_blocks = 116935ec7d57SChao Yu SECTOR_TO_BLOCK(q->limits.max_discard_sectors); 1170c81abe34SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 117178997b56SChao Yu struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? 117278997b56SChao Yu &(dcc->fstrim_list) : &(dcc->wait_list); 117378997b56SChao Yu int flag = dpolicy->sync ? REQ_SYNC : 0; 117435ec7d57SChao Yu block_t lstart, start, len, total_len; 117535ec7d57SChao Yu int err = 0; 1176c81abe34SJaegeuk Kim 1177c81abe34SJaegeuk Kim if (dc->state != D_PREP) 11786b9cb124SChao Yu return 0; 1179c81abe34SJaegeuk Kim 1180d6184774SYunlei He if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) 11816b9cb124SChao Yu return 0; 1182d6184774SYunlei He 118335ec7d57SChao Yu trace_f2fs_issue_discard(bdev, dc->start, dc->len); 11840243a5f9SChao Yu 118535ec7d57SChao Yu lstart = dc->lstart; 118635ec7d57SChao Yu start = dc->start; 118735ec7d57SChao Yu len = dc->len; 118835ec7d57SChao Yu total_len = len; 118935ec7d57SChao Yu 119035ec7d57SChao Yu dc->len = 0; 119135ec7d57SChao Yu 119235ec7d57SChao Yu while (total_len && *issued < dpolicy->max_requests && !err) { 119335ec7d57SChao Yu struct bio *bio = NULL; 119435ec7d57SChao Yu unsigned long flags; 119535ec7d57SChao Yu bool last = true; 119635ec7d57SChao Yu 119735ec7d57SChao Yu if (len > max_discard_blocks) { 119835ec7d57SChao Yu len = max_discard_blocks; 119935ec7d57SChao Yu last = false; 120035ec7d57SChao Yu } 120135ec7d57SChao Yu 120235ec7d57SChao Yu (*issued)++; 120335ec7d57SChao Yu if (*issued == dpolicy->max_requests) 120435ec7d57SChao Yu last = true; 120535ec7d57SChao Yu 120635ec7d57SChao Yu dc->len += len; 120735ec7d57SChao Yu 1208b83dcfe6SChao Yu if (time_to_inject(sbi, FAULT_DISCARD)) { 1209c45d6002SChao Yu f2fs_show_injection_info(sbi, FAULT_DISCARD); 1210b83dcfe6SChao Yu err = -EIO; 1211b83dcfe6SChao Yu goto submit; 1212b83dcfe6SChao Yu } 121335ec7d57SChao Yu err = __blkdev_issue_discard(bdev, 121435ec7d57SChao Yu SECTOR_FROM_BLOCK(start), 121535ec7d57SChao Yu SECTOR_FROM_BLOCK(len), 1216c81abe34SJaegeuk Kim GFP_NOFS, 0, &bio); 1217b83dcfe6SChao Yu submit: 12186b9cb124SChao Yu if (err) { 12196b9cb124SChao Yu spin_lock_irqsave(&dc->lock, flags); 12206b9cb124SChao Yu if (dc->state == D_PARTIAL) 12216b9cb124SChao Yu dc->state = D_SUBMIT; 12226b9cb124SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 12236b9cb124SChao Yu 12246b9cb124SChao Yu break; 12256b9cb124SChao Yu } 12266b9cb124SChao Yu 12276b9cb124SChao Yu f2fs_bug_on(sbi, !bio); 12286b9cb124SChao Yu 122935ec7d57SChao Yu /* 123035ec7d57SChao Yu * should keep before submission to avoid D_DONE 123135ec7d57SChao Yu * right away 123235ec7d57SChao Yu */ 123335ec7d57SChao Yu spin_lock_irqsave(&dc->lock, flags); 123435ec7d57SChao Yu if (last) 1235c81abe34SJaegeuk Kim dc->state = D_SUBMIT; 123635ec7d57SChao Yu else 123735ec7d57SChao Yu dc->state = D_PARTIAL; 123835ec7d57SChao Yu dc->bio_ref++; 123935ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 124035ec7d57SChao Yu 124172691af6SJaegeuk Kim atomic_inc(&dcc->queued_discard); 124272691af6SJaegeuk Kim dc->queued++; 124335ec7d57SChao Yu list_move_tail(&dc->list, wait_list); 124435ec7d57SChao Yu 124535ec7d57SChao Yu /* sanity check on discard range */ 12469249ddedSQiuyang Sun __check_sit_bitmap(sbi, lstart, lstart + len); 124735ec7d57SChao Yu 1248c81abe34SJaegeuk Kim bio->bi_private = dc; 1249c81abe34SJaegeuk Kim bio->bi_end_io = f2fs_submit_discard_endio; 1250ecc9aa00SChao Yu bio->bi_opf |= flag; 1251c81abe34SJaegeuk Kim submit_bio(bio); 125235ec7d57SChao Yu 125335ec7d57SChao Yu atomic_inc(&dcc->issued_discard); 1254b0af6d49SChao Yu 1255b0af6d49SChao Yu f2fs_update_iostat(sbi, FS_DISCARD, 1); 125635ec7d57SChao Yu 125735ec7d57SChao Yu lstart += len; 125835ec7d57SChao Yu start += len; 125935ec7d57SChao Yu total_len -= len; 126035ec7d57SChao Yu len = total_len; 126135ec7d57SChao Yu } 126235ec7d57SChao Yu 1263df423399SSahitya Tummala if (!err && len) { 1264df423399SSahitya Tummala dcc->undiscard_blks -= len; 126535ec7d57SChao Yu __update_discard_tree_range(sbi, bdev, lstart, start, len); 1266df423399SSahitya Tummala } 12676b9cb124SChao Yu return err; 1268c81abe34SJaegeuk Kim } 1269c81abe34SJaegeuk Kim 127047d0d7d7SChao Yu static void __insert_discard_tree(struct f2fs_sb_info *sbi, 1271004b6862SChao Yu struct block_device *bdev, block_t lstart, 1272004b6862SChao Yu block_t start, block_t len, 1273004b6862SChao Yu struct rb_node **insert_p, 1274004b6862SChao Yu struct rb_node *insert_parent) 1275004b6862SChao Yu { 1276004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1277dca6951fSColin Ian King struct rb_node **p; 1278004b6862SChao Yu struct rb_node *parent = NULL; 12794dada3fdSChao Yu bool leftmost = true; 1280004b6862SChao Yu 1281004b6862SChao Yu if (insert_p && insert_parent) { 1282004b6862SChao Yu parent = insert_parent; 1283004b6862SChao Yu p = insert_p; 1284004b6862SChao Yu goto do_insert; 1285004b6862SChao Yu } 1286004b6862SChao Yu 12874dada3fdSChao Yu p = f2fs_lookup_rb_tree_for_insert(sbi, &dcc->root, &parent, 12884dada3fdSChao Yu lstart, &leftmost); 1289004b6862SChao Yu do_insert: 129047d0d7d7SChao Yu __attach_discard_cmd(sbi, bdev, lstart, start, len, parent, 12914dada3fdSChao Yu p, leftmost); 1292004b6862SChao Yu } 1293004b6862SChao Yu 1294ba48a33eSChao Yu static void __relocate_discard_cmd(struct discard_cmd_control *dcc, 1295ba48a33eSChao Yu struct discard_cmd *dc) 1296ba48a33eSChao Yu { 1297ba48a33eSChao Yu list_move_tail(&dc->list, &dcc->pend_list[plist_idx(dc->len)]); 1298ba48a33eSChao Yu } 1299ba48a33eSChao Yu 1300004b6862SChao Yu static void __punch_discard_cmd(struct f2fs_sb_info *sbi, 1301004b6862SChao Yu struct discard_cmd *dc, block_t blkaddr) 1302004b6862SChao Yu { 1303ba48a33eSChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1304004b6862SChao Yu struct discard_info di = dc->di; 1305004b6862SChao Yu bool modified = false; 1306004b6862SChao Yu 1307004b6862SChao Yu if (dc->state == D_DONE || dc->len == 1) { 1308004b6862SChao Yu __remove_discard_cmd(sbi, dc); 1309004b6862SChao Yu return; 1310004b6862SChao Yu } 1311004b6862SChao Yu 1312d84d1cbdSChao Yu dcc->undiscard_blks -= di.len; 1313d84d1cbdSChao Yu 1314004b6862SChao Yu if (blkaddr > di.lstart) { 1315004b6862SChao Yu dc->len = blkaddr - dc->lstart; 1316d84d1cbdSChao Yu dcc->undiscard_blks += dc->len; 1317ba48a33eSChao Yu __relocate_discard_cmd(dcc, dc); 1318004b6862SChao Yu modified = true; 1319004b6862SChao Yu } 1320004b6862SChao Yu 1321004b6862SChao Yu if (blkaddr < di.lstart + di.len - 1) { 1322004b6862SChao Yu if (modified) { 1323004b6862SChao Yu __insert_discard_tree(sbi, dc->bdev, blkaddr + 1, 1324004b6862SChao Yu di.start + blkaddr + 1 - di.lstart, 1325004b6862SChao Yu di.lstart + di.len - 1 - blkaddr, 1326004b6862SChao Yu NULL, NULL); 1327004b6862SChao Yu } else { 1328004b6862SChao Yu dc->lstart++; 1329004b6862SChao Yu dc->len--; 1330004b6862SChao Yu dc->start++; 1331d84d1cbdSChao Yu dcc->undiscard_blks += dc->len; 1332ba48a33eSChao Yu __relocate_discard_cmd(dcc, dc); 1333004b6862SChao Yu } 1334004b6862SChao Yu } 1335004b6862SChao Yu } 1336004b6862SChao Yu 1337004b6862SChao Yu static void __update_discard_tree_range(struct f2fs_sb_info *sbi, 1338004b6862SChao Yu struct block_device *bdev, block_t lstart, 1339004b6862SChao Yu block_t start, block_t len) 1340004b6862SChao Yu { 1341004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1342004b6862SChao Yu struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 1343004b6862SChao Yu struct discard_cmd *dc; 1344004b6862SChao Yu struct discard_info di = {0}; 1345004b6862SChao Yu struct rb_node **insert_p = NULL, *insert_parent = NULL; 134635ec7d57SChao Yu struct request_queue *q = bdev_get_queue(bdev); 134735ec7d57SChao Yu unsigned int max_discard_blocks = 134835ec7d57SChao Yu SECTOR_TO_BLOCK(q->limits.max_discard_sectors); 1349004b6862SChao Yu block_t end = lstart + len; 1350004b6862SChao Yu 13514d57b86dSChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, 1352004b6862SChao Yu NULL, lstart, 1353004b6862SChao Yu (struct rb_entry **)&prev_dc, 1354004b6862SChao Yu (struct rb_entry **)&next_dc, 13554dada3fdSChao Yu &insert_p, &insert_parent, true, NULL); 1356004b6862SChao Yu if (dc) 1357004b6862SChao Yu prev_dc = dc; 1358004b6862SChao Yu 1359004b6862SChao Yu if (!prev_dc) { 1360004b6862SChao Yu di.lstart = lstart; 1361004b6862SChao Yu di.len = next_dc ? next_dc->lstart - lstart : len; 1362004b6862SChao Yu di.len = min(di.len, len); 1363004b6862SChao Yu di.start = start; 1364004b6862SChao Yu } 1365004b6862SChao Yu 1366004b6862SChao Yu while (1) { 1367004b6862SChao Yu struct rb_node *node; 1368004b6862SChao Yu bool merged = false; 1369004b6862SChao Yu struct discard_cmd *tdc = NULL; 1370004b6862SChao Yu 1371004b6862SChao Yu if (prev_dc) { 1372004b6862SChao Yu di.lstart = prev_dc->lstart + prev_dc->len; 1373004b6862SChao Yu if (di.lstart < lstart) 1374004b6862SChao Yu di.lstart = lstart; 1375004b6862SChao Yu if (di.lstart >= end) 1376004b6862SChao Yu break; 1377004b6862SChao Yu 1378004b6862SChao Yu if (!next_dc || next_dc->lstart > end) 1379004b6862SChao Yu di.len = end - di.lstart; 1380004b6862SChao Yu else 1381004b6862SChao Yu di.len = next_dc->lstart - di.lstart; 1382004b6862SChao Yu di.start = start + di.lstart - lstart; 1383004b6862SChao Yu } 1384004b6862SChao Yu 1385004b6862SChao Yu if (!di.len) 1386004b6862SChao Yu goto next; 1387004b6862SChao Yu 1388004b6862SChao Yu if (prev_dc && prev_dc->state == D_PREP && 1389004b6862SChao Yu prev_dc->bdev == bdev && 139035ec7d57SChao Yu __is_discard_back_mergeable(&di, &prev_dc->di, 139135ec7d57SChao Yu max_discard_blocks)) { 1392004b6862SChao Yu prev_dc->di.len += di.len; 1393d84d1cbdSChao Yu dcc->undiscard_blks += di.len; 1394ba48a33eSChao Yu __relocate_discard_cmd(dcc, prev_dc); 1395004b6862SChao Yu di = prev_dc->di; 1396004b6862SChao Yu tdc = prev_dc; 1397004b6862SChao Yu merged = true; 1398004b6862SChao Yu } 1399004b6862SChao Yu 1400004b6862SChao Yu if (next_dc && next_dc->state == D_PREP && 1401004b6862SChao Yu next_dc->bdev == bdev && 140235ec7d57SChao Yu __is_discard_front_mergeable(&di, &next_dc->di, 140335ec7d57SChao Yu max_discard_blocks)) { 1404004b6862SChao Yu next_dc->di.lstart = di.lstart; 1405004b6862SChao Yu next_dc->di.len += di.len; 1406004b6862SChao Yu next_dc->di.start = di.start; 1407d84d1cbdSChao Yu dcc->undiscard_blks += di.len; 1408ba48a33eSChao Yu __relocate_discard_cmd(dcc, next_dc); 1409004b6862SChao Yu if (tdc) 1410004b6862SChao Yu __remove_discard_cmd(sbi, tdc); 1411004b6862SChao Yu merged = true; 1412004b6862SChao Yu } 1413004b6862SChao Yu 1414df0f6b44SChao Yu if (!merged) { 1415004b6862SChao Yu __insert_discard_tree(sbi, bdev, di.lstart, di.start, 1416004b6862SChao Yu di.len, NULL, NULL); 1417df0f6b44SChao Yu } 1418004b6862SChao Yu next: 1419004b6862SChao Yu prev_dc = next_dc; 1420004b6862SChao Yu if (!prev_dc) 1421004b6862SChao Yu break; 1422004b6862SChao Yu 1423004b6862SChao Yu node = rb_next(&prev_dc->rb_node); 1424004b6862SChao Yu next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); 1425004b6862SChao Yu } 1426004b6862SChao Yu } 1427004b6862SChao Yu 1428c81abe34SJaegeuk Kim static int __queue_discard_cmd(struct f2fs_sb_info *sbi, 1429c81abe34SJaegeuk Kim struct block_device *bdev, block_t blkstart, block_t blklen) 1430c81abe34SJaegeuk Kim { 1431c81abe34SJaegeuk Kim block_t lblkstart = blkstart; 1432c81abe34SJaegeuk Kim 14337f3d7719SDamien Le Moal if (!f2fs_bdev_support_discard(bdev)) 14347f3d7719SDamien Le Moal return 0; 14357f3d7719SDamien Le Moal 14360243a5f9SChao Yu trace_f2fs_queue_discard(bdev, blkstart, blklen); 1437c81abe34SJaegeuk Kim 14380916878dSDamien Le Moal if (f2fs_is_multi_device(sbi)) { 1439c81abe34SJaegeuk Kim int devi = f2fs_target_device_index(sbi, blkstart); 1440c81abe34SJaegeuk Kim 1441c81abe34SJaegeuk Kim blkstart -= FDEV(devi).start_blk; 1442c81abe34SJaegeuk Kim } 144335ec7d57SChao Yu mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock); 1444004b6862SChao Yu __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen); 144535ec7d57SChao Yu mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock); 1446c81abe34SJaegeuk Kim return 0; 1447c81abe34SJaegeuk Kim } 1448c81abe34SJaegeuk Kim 144920ee4382SChao Yu static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, 145020ee4382SChao Yu struct discard_policy *dpolicy) 145120ee4382SChao Yu { 145220ee4382SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 145320ee4382SChao Yu struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 145420ee4382SChao Yu struct rb_node **insert_p = NULL, *insert_parent = NULL; 145520ee4382SChao Yu struct discard_cmd *dc; 145620ee4382SChao Yu struct blk_plug plug; 145720ee4382SChao Yu unsigned int pos = dcc->next_pos; 145820ee4382SChao Yu unsigned int issued = 0; 145920ee4382SChao Yu bool io_interrupted = false; 146020ee4382SChao Yu 146120ee4382SChao Yu mutex_lock(&dcc->cmd_lock); 146220ee4382SChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, 146320ee4382SChao Yu NULL, pos, 146420ee4382SChao Yu (struct rb_entry **)&prev_dc, 146520ee4382SChao Yu (struct rb_entry **)&next_dc, 14664dada3fdSChao Yu &insert_p, &insert_parent, true, NULL); 146720ee4382SChao Yu if (!dc) 146820ee4382SChao Yu dc = next_dc; 146920ee4382SChao Yu 147020ee4382SChao Yu blk_start_plug(&plug); 147120ee4382SChao Yu 147220ee4382SChao Yu while (dc) { 147320ee4382SChao Yu struct rb_node *node; 14746b9cb124SChao Yu int err = 0; 147520ee4382SChao Yu 147620ee4382SChao Yu if (dc->state != D_PREP) 147720ee4382SChao Yu goto next; 147820ee4382SChao Yu 1479a7d10cf3SSahitya Tummala if (dpolicy->io_aware && !is_idle(sbi, DISCARD_TIME)) { 148020ee4382SChao Yu io_interrupted = true; 148120ee4382SChao Yu break; 148220ee4382SChao Yu } 148320ee4382SChao Yu 148420ee4382SChao Yu dcc->next_pos = dc->lstart + dc->len; 14856b9cb124SChao Yu err = __submit_discard_cmd(sbi, dpolicy, dc, &issued); 148620ee4382SChao Yu 148735ec7d57SChao Yu if (issued >= dpolicy->max_requests) 148820ee4382SChao Yu break; 148920ee4382SChao Yu next: 149020ee4382SChao Yu node = rb_next(&dc->rb_node); 14916b9cb124SChao Yu if (err) 14926b9cb124SChao Yu __remove_discard_cmd(sbi, dc); 149320ee4382SChao Yu dc = rb_entry_safe(node, struct discard_cmd, rb_node); 149420ee4382SChao Yu } 149520ee4382SChao Yu 149620ee4382SChao Yu blk_finish_plug(&plug); 149720ee4382SChao Yu 149820ee4382SChao Yu if (!dc) 149920ee4382SChao Yu dcc->next_pos = 0; 150020ee4382SChao Yu 150120ee4382SChao Yu mutex_unlock(&dcc->cmd_lock); 150220ee4382SChao Yu 150320ee4382SChao Yu if (!issued && io_interrupted) 150420ee4382SChao Yu issued = -1; 150520ee4382SChao Yu 150620ee4382SChao Yu return issued; 150720ee4382SChao Yu } 1508141af6baSSahitya Tummala static unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi, 1509141af6baSSahitya Tummala struct discard_policy *dpolicy); 151020ee4382SChao Yu 151178997b56SChao Yu static int __issue_discard_cmd(struct f2fs_sb_info *sbi, 151278997b56SChao Yu struct discard_policy *dpolicy) 1513bd5b0738SChao Yu { 1514bd5b0738SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1515bd5b0738SChao Yu struct list_head *pend_list; 1516bd5b0738SChao Yu struct discard_cmd *dc, *tmp; 1517bd5b0738SChao Yu struct blk_plug plug; 1518141af6baSSahitya Tummala int i, issued; 1519e6c6de18SChao Yu bool io_interrupted = false; 1520bd5b0738SChao Yu 15216ce48b0cSChao Yu if (dpolicy->timeout) 15226ce48b0cSChao Yu f2fs_update_time(sbi, UMOUNT_DISCARD_TIMEOUT); 152303f2c02dSJaegeuk Kim 1524141af6baSSahitya Tummala retry: 1525141af6baSSahitya Tummala issued = 0; 152678997b56SChao Yu for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { 15276ce48b0cSChao Yu if (dpolicy->timeout && 15286ce48b0cSChao Yu f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) 152903f2c02dSJaegeuk Kim break; 153003f2c02dSJaegeuk Kim 153178997b56SChao Yu if (i + 1 < dpolicy->granularity) 153278997b56SChao Yu break; 153320ee4382SChao Yu 153420ee4382SChao Yu if (i < DEFAULT_DISCARD_GRANULARITY && dpolicy->ordered) 153520ee4382SChao Yu return __issue_discard_cmd_orderly(sbi, dpolicy); 153620ee4382SChao Yu 1537bd5b0738SChao Yu pend_list = &dcc->pend_list[i]; 153833da62cfSChao Yu 153933da62cfSChao Yu mutex_lock(&dcc->cmd_lock); 154049c60c67SChao Yu if (list_empty(pend_list)) 154149c60c67SChao Yu goto next; 154267fce70bSChao Yu if (unlikely(dcc->rbtree_check)) 154367fce70bSChao Yu f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi, 15442e9b2bb2SChao Yu &dcc->root, false)); 154533da62cfSChao Yu blk_start_plug(&plug); 1546bd5b0738SChao Yu list_for_each_entry_safe(dc, tmp, pend_list, list) { 1547bd5b0738SChao Yu f2fs_bug_on(sbi, dc->state != D_PREP); 1548bd5b0738SChao Yu 15496ce48b0cSChao Yu if (dpolicy->timeout && 15506ce48b0cSChao Yu f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) 15516e0cd4a9SHeng Xiao break; 15526e0cd4a9SHeng Xiao 1553ecc9aa00SChao Yu if (dpolicy->io_aware && i < dpolicy->io_aware_gran && 1554a7d10cf3SSahitya Tummala !is_idle(sbi, DISCARD_TIME)) { 1555e6c6de18SChao Yu io_interrupted = true; 1556522d1711SChao Yu break; 1557e6c6de18SChao Yu } 1558e6c6de18SChao Yu 155935ec7d57SChao Yu __submit_discard_cmd(sbi, dpolicy, dc, &issued); 1560522d1711SChao Yu 156135ec7d57SChao Yu if (issued >= dpolicy->max_requests) 156233da62cfSChao Yu break; 1563bd5b0738SChao Yu } 1564bd5b0738SChao Yu blk_finish_plug(&plug); 156549c60c67SChao Yu next: 1566bd5b0738SChao Yu mutex_unlock(&dcc->cmd_lock); 1567969d1b18SChao Yu 1568522d1711SChao Yu if (issued >= dpolicy->max_requests || io_interrupted) 156933da62cfSChao Yu break; 157033da62cfSChao Yu } 157133da62cfSChao Yu 1572141af6baSSahitya Tummala if (dpolicy->type == DPOLICY_UMOUNT && issued) { 1573141af6baSSahitya Tummala __wait_all_discard_cmd(sbi, dpolicy); 1574141af6baSSahitya Tummala goto retry; 1575141af6baSSahitya Tummala } 1576141af6baSSahitya Tummala 1577e6c6de18SChao Yu if (!issued && io_interrupted) 1578e6c6de18SChao Yu issued = -1; 1579e6c6de18SChao Yu 1580969d1b18SChao Yu return issued; 1581969d1b18SChao Yu } 1582969d1b18SChao Yu 1583cf5c759fSChao Yu static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) 1584969d1b18SChao Yu { 1585969d1b18SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1586969d1b18SChao Yu struct list_head *pend_list; 1587969d1b18SChao Yu struct discard_cmd *dc, *tmp; 1588969d1b18SChao Yu int i; 1589cf5c759fSChao Yu bool dropped = false; 1590969d1b18SChao Yu 1591969d1b18SChao Yu mutex_lock(&dcc->cmd_lock); 1592969d1b18SChao Yu for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { 1593969d1b18SChao Yu pend_list = &dcc->pend_list[i]; 1594969d1b18SChao Yu list_for_each_entry_safe(dc, tmp, pend_list, list) { 1595969d1b18SChao Yu f2fs_bug_on(sbi, dc->state != D_PREP); 1596969d1b18SChao Yu __remove_discard_cmd(sbi, dc); 1597cf5c759fSChao Yu dropped = true; 1598969d1b18SChao Yu } 1599969d1b18SChao Yu } 1600969d1b18SChao Yu mutex_unlock(&dcc->cmd_lock); 1601cf5c759fSChao Yu 1602cf5c759fSChao Yu return dropped; 1603bd5b0738SChao Yu } 1604bd5b0738SChao Yu 16054d57b86dSChao Yu void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi) 16067950e9acSChao Yu { 16077950e9acSChao Yu __drop_discard_cmd(sbi); 16087950e9acSChao Yu } 16097950e9acSChao Yu 16100ea80512SChao Yu static unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, 16112a510c00SChao Yu struct discard_cmd *dc) 16122a510c00SChao Yu { 16132a510c00SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 16140ea80512SChao Yu unsigned int len = 0; 16152a510c00SChao Yu 16162a510c00SChao Yu wait_for_completion_io(&dc->wait); 16172a510c00SChao Yu mutex_lock(&dcc->cmd_lock); 16182a510c00SChao Yu f2fs_bug_on(sbi, dc->state != D_DONE); 16192a510c00SChao Yu dc->ref--; 16200ea80512SChao Yu if (!dc->ref) { 16210ea80512SChao Yu if (!dc->error) 16220ea80512SChao Yu len = dc->len; 16232a510c00SChao Yu __remove_discard_cmd(sbi, dc); 16240ea80512SChao Yu } 16252a510c00SChao Yu mutex_unlock(&dcc->cmd_lock); 16260ea80512SChao Yu 16270ea80512SChao Yu return len; 16282a510c00SChao Yu } 16292a510c00SChao Yu 16300ea80512SChao Yu static unsigned int __wait_discard_cmd_range(struct f2fs_sb_info *sbi, 163178997b56SChao Yu struct discard_policy *dpolicy, 163278997b56SChao Yu block_t start, block_t end) 163363a94fa1SChao Yu { 163463a94fa1SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 163578997b56SChao Yu struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? 163678997b56SChao Yu &(dcc->fstrim_list) : &(dcc->wait_list); 163763a94fa1SChao Yu struct discard_cmd *dc, *tmp; 16386afae633SChao Yu bool need_wait; 16390ea80512SChao Yu unsigned int trimmed = 0; 16406afae633SChao Yu 16416afae633SChao Yu next: 16426afae633SChao Yu need_wait = false; 164363a94fa1SChao Yu 164463a94fa1SChao Yu mutex_lock(&dcc->cmd_lock); 164563a94fa1SChao Yu list_for_each_entry_safe(dc, tmp, wait_list, list) { 16468412663dSChao Yu if (dc->lstart + dc->len <= start || end <= dc->lstart) 16478412663dSChao Yu continue; 164878997b56SChao Yu if (dc->len < dpolicy->granularity) 16498412663dSChao Yu continue; 165078997b56SChao Yu if (dc->state == D_DONE && !dc->ref) { 165163a94fa1SChao Yu wait_for_completion_io(&dc->wait); 16520ea80512SChao Yu if (!dc->error) 16530ea80512SChao Yu trimmed += dc->len; 165463a94fa1SChao Yu __remove_discard_cmd(sbi, dc); 16556afae633SChao Yu } else { 16566afae633SChao Yu dc->ref++; 16576afae633SChao Yu need_wait = true; 16586afae633SChao Yu break; 165963a94fa1SChao Yu } 166063a94fa1SChao Yu } 166163a94fa1SChao Yu mutex_unlock(&dcc->cmd_lock); 16626afae633SChao Yu 16636afae633SChao Yu if (need_wait) { 16640ea80512SChao Yu trimmed += __wait_one_discard_bio(sbi, dc); 16656afae633SChao Yu goto next; 16666afae633SChao Yu } 16670ea80512SChao Yu 16680ea80512SChao Yu return trimmed; 166963a94fa1SChao Yu } 167063a94fa1SChao Yu 167101f9cf6dSChao Yu static unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi, 167278997b56SChao Yu struct discard_policy *dpolicy) 16738412663dSChao Yu { 16749a997188SJaegeuk Kim struct discard_policy dp; 167501f9cf6dSChao Yu unsigned int discard_blks; 16769a997188SJaegeuk Kim 167701f9cf6dSChao Yu if (dpolicy) 167801f9cf6dSChao Yu return __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); 16799a997188SJaegeuk Kim 16809a997188SJaegeuk Kim /* wait all */ 16818bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1); 168201f9cf6dSChao Yu discard_blks = __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); 16838bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1); 168401f9cf6dSChao Yu discard_blks += __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); 168501f9cf6dSChao Yu 168601f9cf6dSChao Yu return discard_blks; 16878412663dSChao Yu } 16888412663dSChao Yu 16894e6a8d9bSJaegeuk Kim /* This should be covered by global mutex, &sit_i->sentry_lock */ 169094b1e10eSWei Yongjun static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) 1691275b66b0SChao Yu { 16920b54fb84SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1693004b6862SChao Yu struct discard_cmd *dc; 1694ec9895adSChao Yu bool need_wait = false; 1695275b66b0SChao Yu 169615469963SJaegeuk Kim mutex_lock(&dcc->cmd_lock); 16974d57b86dSChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree(&dcc->root, 16984d57b86dSChao Yu NULL, blkaddr); 1699004b6862SChao Yu if (dc) { 1700ec9895adSChao Yu if (dc->state == D_PREP) { 17013d6a650fSYunlei He __punch_discard_cmd(sbi, dc, blkaddr); 1702ec9895adSChao Yu } else { 1703ec9895adSChao Yu dc->ref++; 1704ec9895adSChao Yu need_wait = true; 1705275b66b0SChao Yu } 1706ec9895adSChao Yu } 1707d431413fSChao Yu mutex_unlock(&dcc->cmd_lock); 1708ec9895adSChao Yu 17092a510c00SChao Yu if (need_wait) 17102a510c00SChao Yu __wait_one_discard_bio(sbi, dc); 1711d431413fSChao Yu } 1712d431413fSChao Yu 17134d57b86dSChao Yu void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi) 1714cce13252SChao Yu { 1715cce13252SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1716cce13252SChao Yu 1717cce13252SChao Yu if (dcc && dcc->f2fs_issue_discard) { 1718cce13252SChao Yu struct task_struct *discard_thread = dcc->f2fs_issue_discard; 1719cce13252SChao Yu 1720cce13252SChao Yu dcc->f2fs_issue_discard = NULL; 1721cce13252SChao Yu kthread_stop(discard_thread); 172215469963SJaegeuk Kim } 172315469963SJaegeuk Kim } 172415469963SJaegeuk Kim 17258412663dSChao Yu /* This comes from f2fs_put_super */ 172603f2c02dSJaegeuk Kim bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) 1727275b66b0SChao Yu { 1728969d1b18SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 172978997b56SChao Yu struct discard_policy dpolicy; 1730cf5c759fSChao Yu bool dropped; 1731969d1b18SChao Yu 17328bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT, 17338bb4f253SJaegeuk Kim dcc->discard_granularity); 173478997b56SChao Yu __issue_discard_cmd(sbi, &dpolicy); 1735cf5c759fSChao Yu dropped = __drop_discard_cmd(sbi); 1736cf5c759fSChao Yu 17379a997188SJaegeuk Kim /* just to make sure there is no pending discard commands */ 17389a997188SJaegeuk Kim __wait_all_discard_cmd(sbi, NULL); 17392482c432SChao Yu 17402482c432SChao Yu f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt)); 1741cf5c759fSChao Yu return dropped; 1742969d1b18SChao Yu } 1743969d1b18SChao Yu 174415469963SJaegeuk Kim static int issue_discard_thread(void *data) 174515469963SJaegeuk Kim { 174615469963SJaegeuk Kim struct f2fs_sb_info *sbi = data; 174715469963SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 174815469963SJaegeuk Kim wait_queue_head_t *q = &dcc->discard_wait_queue; 174978997b56SChao Yu struct discard_policy dpolicy; 1750969d1b18SChao Yu unsigned int wait_ms = DEF_MIN_DISCARD_ISSUE_TIME; 1751969d1b18SChao Yu int issued; 17521d7be270SJaegeuk Kim 17531d7be270SJaegeuk Kim set_freezable(); 17541d7be270SJaegeuk Kim 17551d7be270SJaegeuk Kim do { 1756c35b8d5eSSahitya Tummala if (sbi->gc_mode == GC_URGENT_HIGH || 1757c35b8d5eSSahitya Tummala !f2fs_available_free_memory(sbi, DISCARD_CACHE)) 1758c35b8d5eSSahitya Tummala __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1); 1759c35b8d5eSSahitya Tummala else 17608bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dpolicy, DPOLICY_BG, 176178997b56SChao Yu dcc->discard_granularity); 176278997b56SChao Yu 1763c35b8d5eSSahitya Tummala if (!atomic_read(&dcc->discard_cmd_cnt)) 1764c35b8d5eSSahitya Tummala wait_ms = dpolicy.max_interval; 1765c35b8d5eSSahitya Tummala 1766969d1b18SChao Yu wait_event_interruptible_timeout(*q, 1767969d1b18SChao Yu kthread_should_stop() || freezing(current) || 1768969d1b18SChao Yu dcc->discard_wake, 1769969d1b18SChao Yu msecs_to_jiffies(wait_ms)); 177035a9a766SSheng Yong 177135a9a766SSheng Yong if (dcc->discard_wake) 177235a9a766SSheng Yong dcc->discard_wake = 0; 177335a9a766SSheng Yong 177476c7bfb3SJaegeuk Kim /* clean up pending candidates before going to sleep */ 177576c7bfb3SJaegeuk Kim if (atomic_read(&dcc->queued_discard)) 177676c7bfb3SJaegeuk Kim __wait_all_discard_cmd(sbi, NULL); 177776c7bfb3SJaegeuk Kim 17781d7be270SJaegeuk Kim if (try_to_freeze()) 17791d7be270SJaegeuk Kim continue; 17803b60d802SChao Yu if (f2fs_readonly(sbi->sb)) 17813b60d802SChao Yu continue; 178215469963SJaegeuk Kim if (kthread_should_stop()) 178315469963SJaegeuk Kim return 0; 1784d6184774SYunlei He if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { 1785d6184774SYunlei He wait_ms = dpolicy.max_interval; 1786d6184774SYunlei He continue; 1787d6184774SYunlei He } 178843f8c47eSChao Yu if (!atomic_read(&dcc->discard_cmd_cnt)) 178943f8c47eSChao Yu continue; 179015469963SJaegeuk Kim 1791dc6febb6SChao Yu sb_start_intwrite(sbi->sb); 1792dc6febb6SChao Yu 179378997b56SChao Yu issued = __issue_discard_cmd(sbi, &dpolicy); 1794f9d1dcedSYunlei He if (issued > 0) { 179578997b56SChao Yu __wait_all_discard_cmd(sbi, &dpolicy); 179678997b56SChao Yu wait_ms = dpolicy.min_interval; 1797f9d1dcedSYunlei He } else if (issued == -1) { 1798a7d10cf3SSahitya Tummala wait_ms = f2fs_time_to_wait(sbi, DISCARD_TIME); 1799a7d10cf3SSahitya Tummala if (!wait_ms) 1800f9d1dcedSYunlei He wait_ms = dpolicy.mid_interval; 1801969d1b18SChao Yu } else { 180278997b56SChao Yu wait_ms = dpolicy.max_interval; 1803969d1b18SChao Yu } 180415469963SJaegeuk Kim 1805dc6febb6SChao Yu sb_end_intwrite(sbi->sb); 1806dc6febb6SChao Yu 18071d7be270SJaegeuk Kim } while (!kthread_should_stop()); 18081d7be270SJaegeuk Kim return 0; 180915469963SJaegeuk Kim } 181015469963SJaegeuk Kim 1811f46e8809SDamien Le Moal #ifdef CONFIG_BLK_DEV_ZONED 18123c62be17SJaegeuk Kim static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, 18133c62be17SJaegeuk Kim struct block_device *bdev, block_t blkstart, block_t blklen) 1814f46e8809SDamien Le Moal { 181592592285SJaegeuk Kim sector_t sector, nr_sects; 181610a875f8SKinglong Mee block_t lblkstart = blkstart; 18173c62be17SJaegeuk Kim int devi = 0; 1818f46e8809SDamien Le Moal 18190916878dSDamien Le Moal if (f2fs_is_multi_device(sbi)) { 18203c62be17SJaegeuk Kim devi = f2fs_target_device_index(sbi, blkstart); 182195175dafSDamien Le Moal if (blkstart < FDEV(devi).start_blk || 182295175dafSDamien Le Moal blkstart > FDEV(devi).end_blk) { 1823dcbb4c10SJoe Perches f2fs_err(sbi, "Invalid block %x", blkstart); 182495175dafSDamien Le Moal return -EIO; 182595175dafSDamien Le Moal } 18263c62be17SJaegeuk Kim blkstart -= FDEV(devi).start_blk; 18273c62be17SJaegeuk Kim } 1828f46e8809SDamien Le Moal 182995175dafSDamien Le Moal /* For sequential zones, reset the zone write pointer */ 183095175dafSDamien Le Moal if (f2fs_blkz_is_seq(sbi, devi, blkstart)) { 183192592285SJaegeuk Kim sector = SECTOR_FROM_BLOCK(blkstart); 183292592285SJaegeuk Kim nr_sects = SECTOR_FROM_BLOCK(blklen); 183392592285SJaegeuk Kim 183492592285SJaegeuk Kim if (sector & (bdev_zone_sectors(bdev) - 1) || 183592592285SJaegeuk Kim nr_sects != bdev_zone_sectors(bdev)) { 1836dcbb4c10SJoe Perches f2fs_err(sbi, "(%d) %s: Unaligned zone reset attempted (block %x + %x)", 183792592285SJaegeuk Kim devi, sbi->s_ndevs ? FDEV(devi).path : "", 183892592285SJaegeuk Kim blkstart, blklen); 183992592285SJaegeuk Kim return -EIO; 184092592285SJaegeuk Kim } 1841d50aaeecSJaegeuk Kim trace_f2fs_issue_reset_zone(bdev, blkstart); 18426c1b1da5SAjay Joshi return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET, 18436c1b1da5SAjay Joshi sector, nr_sects, GFP_NOFS); 1844f46e8809SDamien Le Moal } 184595175dafSDamien Le Moal 184695175dafSDamien Le Moal /* For conventional zones, use regular discard if supported */ 184795175dafSDamien Le Moal return __queue_discard_cmd(sbi, bdev, lblkstart, blklen); 1848f46e8809SDamien Le Moal } 1849f46e8809SDamien Le Moal #endif 1850f46e8809SDamien Le Moal 18513c62be17SJaegeuk Kim static int __issue_discard_async(struct f2fs_sb_info *sbi, 18523c62be17SJaegeuk Kim struct block_device *bdev, block_t blkstart, block_t blklen) 18533c62be17SJaegeuk Kim { 18543c62be17SJaegeuk Kim #ifdef CONFIG_BLK_DEV_ZONED 18557f3d7719SDamien Le Moal if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) 18563c62be17SJaegeuk Kim return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); 18573c62be17SJaegeuk Kim #endif 1858c81abe34SJaegeuk Kim return __queue_discard_cmd(sbi, bdev, blkstart, blklen); 18593c62be17SJaegeuk Kim } 18603c62be17SJaegeuk Kim 18611e87a78dSJaegeuk Kim static int f2fs_issue_discard(struct f2fs_sb_info *sbi, 186237208879SJaegeuk Kim block_t blkstart, block_t blklen) 186337208879SJaegeuk Kim { 18643c62be17SJaegeuk Kim sector_t start = blkstart, len = 0; 18653c62be17SJaegeuk Kim struct block_device *bdev; 1866a66cdd98SJaegeuk Kim struct seg_entry *se; 1867a66cdd98SJaegeuk Kim unsigned int offset; 1868a66cdd98SJaegeuk Kim block_t i; 18693c62be17SJaegeuk Kim int err = 0; 1870a66cdd98SJaegeuk Kim 18713c62be17SJaegeuk Kim bdev = f2fs_target_device(sbi, blkstart, NULL); 18723c62be17SJaegeuk Kim 18733c62be17SJaegeuk Kim for (i = blkstart; i < blkstart + blklen; i++, len++) { 18743c62be17SJaegeuk Kim if (i != start) { 18753c62be17SJaegeuk Kim struct block_device *bdev2 = 18763c62be17SJaegeuk Kim f2fs_target_device(sbi, i, NULL); 18773c62be17SJaegeuk Kim 18783c62be17SJaegeuk Kim if (bdev2 != bdev) { 18793c62be17SJaegeuk Kim err = __issue_discard_async(sbi, bdev, 18803c62be17SJaegeuk Kim start, len); 18813c62be17SJaegeuk Kim if (err) 18823c62be17SJaegeuk Kim return err; 18833c62be17SJaegeuk Kim bdev = bdev2; 18843c62be17SJaegeuk Kim start = i; 18853c62be17SJaegeuk Kim len = 0; 18863c62be17SJaegeuk Kim } 18873c62be17SJaegeuk Kim } 18883c62be17SJaegeuk Kim 1889a66cdd98SJaegeuk Kim se = get_seg_entry(sbi, GET_SEGNO(sbi, i)); 1890a66cdd98SJaegeuk Kim offset = GET_BLKOFF_FROM_SEG0(sbi, i); 1891a66cdd98SJaegeuk Kim 1892a66cdd98SJaegeuk Kim if (!f2fs_test_and_set_bit(offset, se->discard_map)) 1893a66cdd98SJaegeuk Kim sbi->discard_blks--; 1894a66cdd98SJaegeuk Kim } 1895f46e8809SDamien Le Moal 18963c62be17SJaegeuk Kim if (len) 18973c62be17SJaegeuk Kim err = __issue_discard_async(sbi, bdev, start, len); 18983c62be17SJaegeuk Kim return err; 18991e87a78dSJaegeuk Kim } 19001e87a78dSJaegeuk Kim 190125290fa5SJaegeuk Kim static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, 190225290fa5SJaegeuk Kim bool check_only) 1903adf4983bSJaegeuk Kim { 1904b2955550SJaegeuk Kim int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 1905b2955550SJaegeuk Kim int max_blocks = sbi->blocks_per_seg; 19064b2fecc8SJaegeuk Kim struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); 1907b2955550SJaegeuk Kim unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 1908b2955550SJaegeuk Kim unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 1909a66cdd98SJaegeuk Kim unsigned long *discard_map = (unsigned long *)se->discard_map; 191060a3b782SJaegeuk Kim unsigned long *dmap = SIT_I(sbi)->tmp_map; 1911b2955550SJaegeuk Kim unsigned int start = 0, end = -1; 1912c473f1a9SChao Yu bool force = (cpc->reason & CP_DISCARD); 1913a7eeb823SChao Yu struct discard_entry *de = NULL; 191446f84c2cSChao Yu struct list_head *head = &SM_I(sbi)->dcc_info->entry_list; 1915b2955550SJaegeuk Kim int i; 1916b2955550SJaegeuk Kim 19177d20c8abSChao Yu if (se->valid_blocks == max_blocks || !f2fs_hw_support_discard(sbi)) 191825290fa5SJaegeuk Kim return false; 1919b2955550SJaegeuk Kim 1920a66cdd98SJaegeuk Kim if (!force) { 19217d20c8abSChao Yu if (!f2fs_realtime_discard_enable(sbi) || !se->valid_blocks || 19220b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info->nr_discards >= 19230b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info->max_discards) 192425290fa5SJaegeuk Kim return false; 19254b2fecc8SJaegeuk Kim } 1926b2955550SJaegeuk Kim 1927b2955550SJaegeuk Kim /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ 1928b2955550SJaegeuk Kim for (i = 0; i < entries; i++) 1929a66cdd98SJaegeuk Kim dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : 1930d7bc2484SJaegeuk Kim (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; 1931b2955550SJaegeuk Kim 19320b54fb84SJaegeuk Kim while (force || SM_I(sbi)->dcc_info->nr_discards <= 19330b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info->max_discards) { 1934b2955550SJaegeuk Kim start = __find_rev_next_bit(dmap, max_blocks, end + 1); 1935b2955550SJaegeuk Kim if (start >= max_blocks) 1936b2955550SJaegeuk Kim break; 1937b2955550SJaegeuk Kim 1938b2955550SJaegeuk Kim end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); 1939c7b41e16SYunlei He if (force && start && end != max_blocks 1940c7b41e16SYunlei He && (end - start) < cpc->trim_minlen) 1941c7b41e16SYunlei He continue; 1942c7b41e16SYunlei He 194325290fa5SJaegeuk Kim if (check_only) 194425290fa5SJaegeuk Kim return true; 194525290fa5SJaegeuk Kim 1946a7eeb823SChao Yu if (!de) { 1947a7eeb823SChao Yu de = f2fs_kmem_cache_alloc(discard_entry_slab, 1948a7eeb823SChao Yu GFP_F2FS_ZERO); 1949a7eeb823SChao Yu de->start_blkaddr = START_BLOCK(sbi, cpc->trim_start); 1950a7eeb823SChao Yu list_add_tail(&de->list, head); 1951a7eeb823SChao Yu } 1952a7eeb823SChao Yu 1953a7eeb823SChao Yu for (i = start; i < end; i++) 1954a7eeb823SChao Yu __set_bit_le(i, (void *)de->discard_map); 1955a7eeb823SChao Yu 1956a7eeb823SChao Yu SM_I(sbi)->dcc_info->nr_discards += end - start; 1957b2955550SJaegeuk Kim } 195825290fa5SJaegeuk Kim return false; 1959b2955550SJaegeuk Kim } 1960b2955550SJaegeuk Kim 1961af8ff65bSChao Yu static void release_discard_addr(struct discard_entry *entry) 1962af8ff65bSChao Yu { 1963af8ff65bSChao Yu list_del(&entry->list); 1964af8ff65bSChao Yu kmem_cache_free(discard_entry_slab, entry); 1965af8ff65bSChao Yu } 1966af8ff65bSChao Yu 19674d57b86dSChao Yu void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi) 19684b2fecc8SJaegeuk Kim { 196946f84c2cSChao Yu struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list); 19704b2fecc8SJaegeuk Kim struct discard_entry *entry, *this; 19714b2fecc8SJaegeuk Kim 19724b2fecc8SJaegeuk Kim /* drop caches */ 1973af8ff65bSChao Yu list_for_each_entry_safe(entry, this, head, list) 1974af8ff65bSChao Yu release_discard_addr(entry); 19754b2fecc8SJaegeuk Kim } 19764b2fecc8SJaegeuk Kim 19770a8165d7SJaegeuk Kim /* 19784d57b86dSChao Yu * Should call f2fs_clear_prefree_segments after checkpoint is done. 1979351df4b2SJaegeuk Kim */ 1980351df4b2SJaegeuk Kim static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) 1981351df4b2SJaegeuk Kim { 1982351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 1983b65ee148SChao Yu unsigned int segno; 1984351df4b2SJaegeuk Kim 1985351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 19867cd8558bSJaegeuk Kim for_each_set_bit(segno, dirty_i->dirty_segmap[PRE], MAIN_SEGS(sbi)) 1987d0b9e42aSChao Yu __set_test_and_free(sbi, segno, false); 1988351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 1989351df4b2SJaegeuk Kim } 1990351df4b2SJaegeuk Kim 19914d57b86dSChao Yu void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, 19924d57b86dSChao Yu struct cp_control *cpc) 1993351df4b2SJaegeuk Kim { 1994969d1b18SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1995969d1b18SChao Yu struct list_head *head = &dcc->entry_list; 19962d7b822aSChao Yu struct discard_entry *entry, *this; 1997351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 199829e59c14SChangman Lee unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; 199929e59c14SChangman Lee unsigned int start = 0, end = -1; 200036abef4eSJaegeuk Kim unsigned int secno, start_segno; 2001c473f1a9SChao Yu bool force = (cpc->reason & CP_DISCARD); 2002b0332a0fSChao Yu bool need_align = f2fs_lfs_mode(sbi) && __is_large_section(sbi); 2003351df4b2SJaegeuk Kim 2004351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 200529e59c14SChangman Lee 2006351df4b2SJaegeuk Kim while (1) { 200729e59c14SChangman Lee int i; 2008ad6672bbSYunlong Song 2009ad6672bbSYunlong Song if (need_align && end != -1) 2010ad6672bbSYunlong Song end--; 20117cd8558bSJaegeuk Kim start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1); 20127cd8558bSJaegeuk Kim if (start >= MAIN_SEGS(sbi)) 2013351df4b2SJaegeuk Kim break; 20147cd8558bSJaegeuk Kim end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi), 20157cd8558bSJaegeuk Kim start + 1); 2016351df4b2SJaegeuk Kim 2017ad6672bbSYunlong Song if (need_align) { 2018ad6672bbSYunlong Song start = rounddown(start, sbi->segs_per_sec); 2019ad6672bbSYunlong Song end = roundup(end, sbi->segs_per_sec); 2020ad6672bbSYunlong Song } 2021351df4b2SJaegeuk Kim 2022ad6672bbSYunlong Song for (i = start; i < end; i++) { 2023ad6672bbSYunlong Song if (test_and_clear_bit(i, prefree_map)) 2024ad6672bbSYunlong Song dirty_i->nr_dirty[PRE]--; 2025ad6672bbSYunlong Song } 202629e59c14SChangman Lee 20277d20c8abSChao Yu if (!f2fs_realtime_discard_enable(sbi)) 2028650d3c4eSYunlei He continue; 2029650d3c4eSYunlei He 2030650d3c4eSYunlei He if (force && start >= cpc->trim_start && 2031650d3c4eSYunlei He (end - 1) <= cpc->trim_end) 203229e59c14SChangman Lee continue; 203329e59c14SChangman Lee 2034b0332a0fSChao Yu if (!f2fs_lfs_mode(sbi) || !__is_large_section(sbi)) { 203537208879SJaegeuk Kim f2fs_issue_discard(sbi, START_BLOCK(sbi, start), 203637208879SJaegeuk Kim (end - start) << sbi->log_blocks_per_seg); 203736abef4eSJaegeuk Kim continue; 203836abef4eSJaegeuk Kim } 203936abef4eSJaegeuk Kim next: 20404ddb1a4dSJaegeuk Kim secno = GET_SEC_FROM_SEG(sbi, start); 20414ddb1a4dSJaegeuk Kim start_segno = GET_SEG_FROM_SEC(sbi, secno); 204236abef4eSJaegeuk Kim if (!IS_CURSEC(sbi, secno) && 2043302bd348SJaegeuk Kim !get_valid_blocks(sbi, start, true)) 204436abef4eSJaegeuk Kim f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), 204536abef4eSJaegeuk Kim sbi->segs_per_sec << sbi->log_blocks_per_seg); 204636abef4eSJaegeuk Kim 204736abef4eSJaegeuk Kim start = start_segno + sbi->segs_per_sec; 204836abef4eSJaegeuk Kim if (start < end) 204936abef4eSJaegeuk Kim goto next; 20508b107f5bSJaegeuk Kim else 20518b107f5bSJaegeuk Kim end = start - 1; 2052351df4b2SJaegeuk Kim } 2053351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 2054b2955550SJaegeuk Kim 2055b2955550SJaegeuk Kim /* send small discards */ 20562d7b822aSChao Yu list_for_each_entry_safe(entry, this, head, list) { 2057a7eeb823SChao Yu unsigned int cur_pos = 0, next_pos, len, total_len = 0; 2058a7eeb823SChao Yu bool is_valid = test_bit_le(0, entry->discard_map); 2059a7eeb823SChao Yu 2060a7eeb823SChao Yu find_next: 2061a7eeb823SChao Yu if (is_valid) { 2062a7eeb823SChao Yu next_pos = find_next_zero_bit_le(entry->discard_map, 2063a7eeb823SChao Yu sbi->blocks_per_seg, cur_pos); 2064a7eeb823SChao Yu len = next_pos - cur_pos; 2065a7eeb823SChao Yu 20667beb01f7SChao Yu if (f2fs_sb_has_blkzoned(sbi) || 2067acfd2810SDamien Le Moal (force && len < cpc->trim_minlen)) 2068836b5a63SJaegeuk Kim goto skip; 2069a7eeb823SChao Yu 2070a7eeb823SChao Yu f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, 2071a7eeb823SChao Yu len); 2072a7eeb823SChao Yu total_len += len; 2073a7eeb823SChao Yu } else { 2074a7eeb823SChao Yu next_pos = find_next_bit_le(entry->discard_map, 2075a7eeb823SChao Yu sbi->blocks_per_seg, cur_pos); 2076a7eeb823SChao Yu } 2077836b5a63SJaegeuk Kim skip: 2078a7eeb823SChao Yu cur_pos = next_pos; 2079a7eeb823SChao Yu is_valid = !is_valid; 2080a7eeb823SChao Yu 2081a7eeb823SChao Yu if (cur_pos < sbi->blocks_per_seg) 2082a7eeb823SChao Yu goto find_next; 2083a7eeb823SChao Yu 2084af8ff65bSChao Yu release_discard_addr(entry); 2085969d1b18SChao Yu dcc->nr_discards -= total_len; 2086b2955550SJaegeuk Kim } 208734e159daSChao Yu 208801983c71SJaegeuk Kim wake_up_discard_thread(sbi, false); 2089351df4b2SJaegeuk Kim } 2090351df4b2SJaegeuk Kim 20918ed59745SJaegeuk Kim static int create_discard_cmd_control(struct f2fs_sb_info *sbi) 20920b54fb84SJaegeuk Kim { 209315469963SJaegeuk Kim dev_t dev = sbi->sb->s_bdev->bd_dev; 20940b54fb84SJaegeuk Kim struct discard_cmd_control *dcc; 2095ba48a33eSChao Yu int err = 0, i; 20960b54fb84SJaegeuk Kim 20970b54fb84SJaegeuk Kim if (SM_I(sbi)->dcc_info) { 20980b54fb84SJaegeuk Kim dcc = SM_I(sbi)->dcc_info; 20990b54fb84SJaegeuk Kim goto init_thread; 21000b54fb84SJaegeuk Kim } 21010b54fb84SJaegeuk Kim 2102acbf054dSChao Yu dcc = f2fs_kzalloc(sbi, sizeof(struct discard_cmd_control), GFP_KERNEL); 21030b54fb84SJaegeuk Kim if (!dcc) 21040b54fb84SJaegeuk Kim return -ENOMEM; 21050b54fb84SJaegeuk Kim 2106969d1b18SChao Yu dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY; 210746f84c2cSChao Yu INIT_LIST_HEAD(&dcc->entry_list); 210878997b56SChao Yu for (i = 0; i < MAX_PLIST_NUM; i++) 2109ba48a33eSChao Yu INIT_LIST_HEAD(&dcc->pend_list[i]); 211046f84c2cSChao Yu INIT_LIST_HEAD(&dcc->wait_list); 21118412663dSChao Yu INIT_LIST_HEAD(&dcc->fstrim_list); 211215469963SJaegeuk Kim mutex_init(&dcc->cmd_lock); 21138b8dd65fSChao Yu atomic_set(&dcc->issued_discard, 0); 211472691af6SJaegeuk Kim atomic_set(&dcc->queued_discard, 0); 21155f32366aSChao Yu atomic_set(&dcc->discard_cmd_cnt, 0); 21160b54fb84SJaegeuk Kim dcc->nr_discards = 0; 2117d618ebafSChao Yu dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg; 2118d84d1cbdSChao Yu dcc->undiscard_blks = 0; 211920ee4382SChao Yu dcc->next_pos = 0; 21204dada3fdSChao Yu dcc->root = RB_ROOT_CACHED; 212167fce70bSChao Yu dcc->rbtree_check = false; 21220b54fb84SJaegeuk Kim 212315469963SJaegeuk Kim init_waitqueue_head(&dcc->discard_wait_queue); 21240b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info = dcc; 21250b54fb84SJaegeuk Kim init_thread: 212615469963SJaegeuk Kim dcc->f2fs_issue_discard = kthread_run(issue_discard_thread, sbi, 212715469963SJaegeuk Kim "f2fs_discard-%u:%u", MAJOR(dev), MINOR(dev)); 212815469963SJaegeuk Kim if (IS_ERR(dcc->f2fs_issue_discard)) { 212915469963SJaegeuk Kim err = PTR_ERR(dcc->f2fs_issue_discard); 2130c8eb7024SChao Yu kfree(dcc); 213115469963SJaegeuk Kim SM_I(sbi)->dcc_info = NULL; 213215469963SJaegeuk Kim return err; 213315469963SJaegeuk Kim } 213415469963SJaegeuk Kim 21350b54fb84SJaegeuk Kim return err; 21360b54fb84SJaegeuk Kim } 21370b54fb84SJaegeuk Kim 2138f099405fSChao Yu static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi) 21390b54fb84SJaegeuk Kim { 21400b54fb84SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 21410b54fb84SJaegeuk Kim 2142f099405fSChao Yu if (!dcc) 2143f099405fSChao Yu return; 2144f099405fSChao Yu 21454d57b86dSChao Yu f2fs_stop_discard_thread(sbi); 2146f099405fSChao Yu 214704f9287aSChao Yu /* 214804f9287aSChao Yu * Recovery can cache discard commands, so in error path of 214904f9287aSChao Yu * fill_super(), it needs to give a chance to handle them. 215004f9287aSChao Yu */ 215104f9287aSChao Yu if (unlikely(atomic_read(&dcc->discard_cmd_cnt))) 215204f9287aSChao Yu f2fs_issue_discard_timeout(sbi); 215304f9287aSChao Yu 2154c8eb7024SChao Yu kfree(dcc); 21550b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info = NULL; 21560b54fb84SJaegeuk Kim } 21570b54fb84SJaegeuk Kim 2158184a5cd2SChao Yu static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) 2159351df4b2SJaegeuk Kim { 2160351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 2161184a5cd2SChao Yu 2162184a5cd2SChao Yu if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap)) { 2163351df4b2SJaegeuk Kim sit_i->dirty_sentries++; 2164184a5cd2SChao Yu return false; 2165184a5cd2SChao Yu } 2166184a5cd2SChao Yu 2167184a5cd2SChao Yu return true; 2168351df4b2SJaegeuk Kim } 2169351df4b2SJaegeuk Kim 2170351df4b2SJaegeuk Kim static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type, 2171351df4b2SJaegeuk Kim unsigned int segno, int modified) 2172351df4b2SJaegeuk Kim { 2173351df4b2SJaegeuk Kim struct seg_entry *se = get_seg_entry(sbi, segno); 2174*5f029c04SYi Zhuang 2175351df4b2SJaegeuk Kim se->type = type; 2176351df4b2SJaegeuk Kim if (modified) 2177351df4b2SJaegeuk Kim __mark_sit_entry_dirty(sbi, segno); 2178351df4b2SJaegeuk Kim } 2179351df4b2SJaegeuk Kim 2180c5d02785SChao Yu static inline unsigned long long get_segment_mtime(struct f2fs_sb_info *sbi, 2181c5d02785SChao Yu block_t blkaddr) 21826f3a01aeSChao Yu { 21836f3a01aeSChao Yu unsigned int segno = GET_SEGNO(sbi, blkaddr); 2184c5d02785SChao Yu 2185c5d02785SChao Yu if (segno == NULL_SEGNO) 2186c5d02785SChao Yu return 0; 2187c5d02785SChao Yu return get_seg_entry(sbi, segno)->mtime; 2188c5d02785SChao Yu } 2189c5d02785SChao Yu 2190c5d02785SChao Yu static void update_segment_mtime(struct f2fs_sb_info *sbi, block_t blkaddr, 2191c5d02785SChao Yu unsigned long long old_mtime) 2192c5d02785SChao Yu { 2193c5d02785SChao Yu struct seg_entry *se; 2194c5d02785SChao Yu unsigned int segno = GET_SEGNO(sbi, blkaddr); 2195c5d02785SChao Yu unsigned long long ctime = get_mtime(sbi, false); 2196c5d02785SChao Yu unsigned long long mtime = old_mtime ? old_mtime : ctime; 2197c5d02785SChao Yu 2198c5d02785SChao Yu if (segno == NULL_SEGNO) 2199c5d02785SChao Yu return; 2200c5d02785SChao Yu 2201c5d02785SChao Yu se = get_seg_entry(sbi, segno); 22026f3a01aeSChao Yu 22036f3a01aeSChao Yu if (!se->mtime) 22046f3a01aeSChao Yu se->mtime = mtime; 22056f3a01aeSChao Yu else 22066f3a01aeSChao Yu se->mtime = div_u64(se->mtime * se->valid_blocks + mtime, 22076f3a01aeSChao Yu se->valid_blocks + 1); 22086f3a01aeSChao Yu 2209c5d02785SChao Yu if (ctime > SIT_I(sbi)->max_mtime) 2210c5d02785SChao Yu SIT_I(sbi)->max_mtime = ctime; 22116f3a01aeSChao Yu } 22126f3a01aeSChao Yu 2213351df4b2SJaegeuk Kim static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) 2214351df4b2SJaegeuk Kim { 2215351df4b2SJaegeuk Kim struct seg_entry *se; 2216351df4b2SJaegeuk Kim unsigned int segno, offset; 2217351df4b2SJaegeuk Kim long int new_vblocks; 22186415fedcSYunlong Song bool exist; 22196415fedcSYunlong Song #ifdef CONFIG_F2FS_CHECK_FS 22206415fedcSYunlong Song bool mir_exist; 22216415fedcSYunlong Song #endif 2222351df4b2SJaegeuk Kim 2223351df4b2SJaegeuk Kim segno = GET_SEGNO(sbi, blkaddr); 2224351df4b2SJaegeuk Kim 2225351df4b2SJaegeuk Kim se = get_seg_entry(sbi, segno); 2226351df4b2SJaegeuk Kim new_vblocks = se->valid_blocks + del; 2227491c0854SJaegeuk Kim offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 2228351df4b2SJaegeuk Kim 22299feffe14SZhihao Cheng f2fs_bug_on(sbi, (new_vblocks < 0 || 2230de881df9SAravind Ramesh (new_vblocks > f2fs_usable_blks_in_seg(sbi, segno)))); 2231351df4b2SJaegeuk Kim 2232351df4b2SJaegeuk Kim se->valid_blocks = new_vblocks; 2233351df4b2SJaegeuk Kim 2234351df4b2SJaegeuk Kim /* Update valid block bitmap */ 2235351df4b2SJaegeuk Kim if (del > 0) { 22366415fedcSYunlong Song exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); 2237355e7891SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 22386415fedcSYunlong Song mir_exist = f2fs_test_and_set_bit(offset, 22396415fedcSYunlong Song se->cur_valid_map_mir); 22406415fedcSYunlong Song if (unlikely(exist != mir_exist)) { 2241dcbb4c10SJoe Perches f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d", 22426415fedcSYunlong Song blkaddr, exist); 224305796763SJaegeuk Kim f2fs_bug_on(sbi, 1); 2244355e7891SChao Yu } 22456415fedcSYunlong Song #endif 22466415fedcSYunlong Song if (unlikely(exist)) { 2247dcbb4c10SJoe Perches f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", 2248dcbb4c10SJoe Perches blkaddr); 22496415fedcSYunlong Song f2fs_bug_on(sbi, 1); 225035ee82caSYunlong Song se->valid_blocks--; 225135ee82caSYunlong Song del = 0; 22526415fedcSYunlong Song } 22536415fedcSYunlong Song 22547d20c8abSChao Yu if (!f2fs_test_and_set_bit(offset, se->discard_map)) 2255a66cdd98SJaegeuk Kim sbi->discard_blks--; 2256720037f9SJaegeuk Kim 2257899fee36SChao Yu /* 2258899fee36SChao Yu * SSR should never reuse block which is checkpointed 2259899fee36SChao Yu * or newly invalidated. 2260899fee36SChao Yu */ 2261899fee36SChao Yu if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) { 2262720037f9SJaegeuk Kim if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) 2263720037f9SJaegeuk Kim se->ckpt_valid_blocks++; 2264720037f9SJaegeuk Kim } 2265351df4b2SJaegeuk Kim } else { 22666415fedcSYunlong Song exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map); 2267355e7891SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 22686415fedcSYunlong Song mir_exist = f2fs_test_and_clear_bit(offset, 22696415fedcSYunlong Song se->cur_valid_map_mir); 22706415fedcSYunlong Song if (unlikely(exist != mir_exist)) { 2271dcbb4c10SJoe Perches f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", 22726415fedcSYunlong Song blkaddr, exist); 227305796763SJaegeuk Kim f2fs_bug_on(sbi, 1); 2274355e7891SChao Yu } 22756415fedcSYunlong Song #endif 22766415fedcSYunlong Song if (unlikely(!exist)) { 2277dcbb4c10SJoe Perches f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", 2278dcbb4c10SJoe Perches blkaddr); 22796415fedcSYunlong Song f2fs_bug_on(sbi, 1); 228035ee82caSYunlong Song se->valid_blocks++; 228135ee82caSYunlong Song del = 0; 22824354994fSDaniel Rosenberg } else if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { 22834354994fSDaniel Rosenberg /* 22844354994fSDaniel Rosenberg * If checkpoints are off, we must not reuse data that 22854354994fSDaniel Rosenberg * was used in the previous checkpoint. If it was used 22864354994fSDaniel Rosenberg * before, we must track that to know how much space we 22874354994fSDaniel Rosenberg * really have. 22884354994fSDaniel Rosenberg */ 2289c9c8ed50SChao Yu if (f2fs_test_bit(offset, se->ckpt_valid_map)) { 2290c9c8ed50SChao Yu spin_lock(&sbi->stat_lock); 22914354994fSDaniel Rosenberg sbi->unusable_block_count++; 2292c9c8ed50SChao Yu spin_unlock(&sbi->stat_lock); 2293c9c8ed50SChao Yu } 22946415fedcSYunlong Song } 22956415fedcSYunlong Song 22967d20c8abSChao Yu if (f2fs_test_and_clear_bit(offset, se->discard_map)) 2297a66cdd98SJaegeuk Kim sbi->discard_blks++; 2298351df4b2SJaegeuk Kim } 2299351df4b2SJaegeuk Kim if (!f2fs_test_bit(offset, se->ckpt_valid_map)) 2300351df4b2SJaegeuk Kim se->ckpt_valid_blocks += del; 2301351df4b2SJaegeuk Kim 2302351df4b2SJaegeuk Kim __mark_sit_entry_dirty(sbi, segno); 2303351df4b2SJaegeuk Kim 2304351df4b2SJaegeuk Kim /* update total number of valid blocks to be written in ckpt area */ 2305351df4b2SJaegeuk Kim SIT_I(sbi)->written_valid_blocks += del; 2306351df4b2SJaegeuk Kim 23072c70c5e3SChao Yu if (__is_large_section(sbi)) 2308351df4b2SJaegeuk Kim get_sec_entry(sbi, segno)->valid_blocks += del; 2309351df4b2SJaegeuk Kim } 2310351df4b2SJaegeuk Kim 23114d57b86dSChao Yu void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) 2312351df4b2SJaegeuk Kim { 2313351df4b2SJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, addr); 2314351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 2315351df4b2SJaegeuk Kim 23169850cf4aSJaegeuk Kim f2fs_bug_on(sbi, addr == NULL_ADDR); 23174c8ff709SChao Yu if (addr == NEW_ADDR || addr == COMPRESS_ADDR) 2318351df4b2SJaegeuk Kim return; 2319351df4b2SJaegeuk Kim 23206aa58d8aSChao Yu invalidate_mapping_pages(META_MAPPING(sbi), addr, addr); 23216aa58d8aSChao Yu 2322351df4b2SJaegeuk Kim /* add it into sit main buffer */ 23233d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 2324351df4b2SJaegeuk Kim 2325c5d02785SChao Yu update_segment_mtime(sbi, addr, 0); 2326351df4b2SJaegeuk Kim update_sit_entry(sbi, addr, -1); 2327351df4b2SJaegeuk Kim 2328351df4b2SJaegeuk Kim /* add it into dirty seglist */ 2329351df4b2SJaegeuk Kim locate_dirty_segment(sbi, segno); 2330351df4b2SJaegeuk Kim 23313d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 2332351df4b2SJaegeuk Kim } 2333351df4b2SJaegeuk Kim 23344d57b86dSChao Yu bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) 23356e2c64adSJaegeuk Kim { 23366e2c64adSJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 23376e2c64adSJaegeuk Kim unsigned int segno, offset; 23386e2c64adSJaegeuk Kim struct seg_entry *se; 23396e2c64adSJaegeuk Kim bool is_cp = false; 23406e2c64adSJaegeuk Kim 234193770ab7SChao Yu if (!__is_valid_data_blkaddr(blkaddr)) 23426e2c64adSJaegeuk Kim return true; 23436e2c64adSJaegeuk Kim 23443d26fa6bSChao Yu down_read(&sit_i->sentry_lock); 23456e2c64adSJaegeuk Kim 23466e2c64adSJaegeuk Kim segno = GET_SEGNO(sbi, blkaddr); 23476e2c64adSJaegeuk Kim se = get_seg_entry(sbi, segno); 23486e2c64adSJaegeuk Kim offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 23496e2c64adSJaegeuk Kim 23506e2c64adSJaegeuk Kim if (f2fs_test_bit(offset, se->ckpt_valid_map)) 23516e2c64adSJaegeuk Kim is_cp = true; 23526e2c64adSJaegeuk Kim 23533d26fa6bSChao Yu up_read(&sit_i->sentry_lock); 23546e2c64adSJaegeuk Kim 23556e2c64adSJaegeuk Kim return is_cp; 23566e2c64adSJaegeuk Kim } 23576e2c64adSJaegeuk Kim 23580a8165d7SJaegeuk Kim /* 2359351df4b2SJaegeuk Kim * This function should be resided under the curseg_mutex lock 2360351df4b2SJaegeuk Kim */ 2361351df4b2SJaegeuk Kim static void __add_sum_entry(struct f2fs_sb_info *sbi, int type, 2362e79efe3bSHaicheng Li struct f2fs_summary *sum) 2363351df4b2SJaegeuk Kim { 2364351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2365351df4b2SJaegeuk Kim void *addr = curseg->sum_blk; 2366*5f029c04SYi Zhuang 2367e79efe3bSHaicheng Li addr += curseg->next_blkoff * sizeof(struct f2fs_summary); 2368351df4b2SJaegeuk Kim memcpy(addr, sum, sizeof(struct f2fs_summary)); 2369351df4b2SJaegeuk Kim } 2370351df4b2SJaegeuk Kim 23710a8165d7SJaegeuk Kim /* 2372351df4b2SJaegeuk Kim * Calculate the number of current summary pages for writing 2373351df4b2SJaegeuk Kim */ 23744d57b86dSChao Yu int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) 2375351df4b2SJaegeuk Kim { 2376351df4b2SJaegeuk Kim int valid_sum_count = 0; 23779a47938bSFan Li int i, sum_in_page; 2378351df4b2SJaegeuk Kim 2379351df4b2SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 2380351df4b2SJaegeuk Kim if (sbi->ckpt->alloc_type[i] == SSR) 2381351df4b2SJaegeuk Kim valid_sum_count += sbi->blocks_per_seg; 23823fa06d7bSChao Yu else { 23833fa06d7bSChao Yu if (for_ra) 23843fa06d7bSChao Yu valid_sum_count += le16_to_cpu( 23853fa06d7bSChao Yu F2FS_CKPT(sbi)->cur_data_blkoff[i]); 2386351df4b2SJaegeuk Kim else 2387351df4b2SJaegeuk Kim valid_sum_count += curseg_blkoff(sbi, i); 2388351df4b2SJaegeuk Kim } 23893fa06d7bSChao Yu } 2390351df4b2SJaegeuk Kim 239109cbfeafSKirill A. Shutemov sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE - 23929a47938bSFan Li SUM_FOOTER_SIZE) / SUMMARY_SIZE; 23939a47938bSFan Li if (valid_sum_count <= sum_in_page) 2394351df4b2SJaegeuk Kim return 1; 23959a47938bSFan Li else if ((valid_sum_count - sum_in_page) <= 239609cbfeafSKirill A. Shutemov (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) 2397351df4b2SJaegeuk Kim return 2; 2398351df4b2SJaegeuk Kim return 3; 2399351df4b2SJaegeuk Kim } 2400351df4b2SJaegeuk Kim 24010a8165d7SJaegeuk Kim /* 2402351df4b2SJaegeuk Kim * Caller should put this summary page 2403351df4b2SJaegeuk Kim */ 24044d57b86dSChao Yu struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) 2405351df4b2SJaegeuk Kim { 240686f33603SJaegeuk Kim if (unlikely(f2fs_cp_error(sbi))) 240786f33603SJaegeuk Kim return ERR_PTR(-EIO); 240886f33603SJaegeuk Kim return f2fs_get_meta_page_retry(sbi, GET_SUM_BLOCK(sbi, segno)); 2409351df4b2SJaegeuk Kim } 2410351df4b2SJaegeuk Kim 24114d57b86dSChao Yu void f2fs_update_meta_page(struct f2fs_sb_info *sbi, 24124d57b86dSChao Yu void *src, block_t blk_addr) 2413381722d2SChao Yu { 24144d57b86dSChao Yu struct page *page = f2fs_grab_meta_page(sbi, blk_addr); 2415381722d2SChao Yu 24160537b811SChao Yu memcpy(page_address(page), src, PAGE_SIZE); 2417381722d2SChao Yu set_page_dirty(page); 2418381722d2SChao Yu f2fs_put_page(page, 1); 2419381722d2SChao Yu } 2420381722d2SChao Yu 2421351df4b2SJaegeuk Kim static void write_sum_page(struct f2fs_sb_info *sbi, 2422351df4b2SJaegeuk Kim struct f2fs_summary_block *sum_blk, block_t blk_addr) 2423351df4b2SJaegeuk Kim { 24244d57b86dSChao Yu f2fs_update_meta_page(sbi, (void *)sum_blk, blk_addr); 2425351df4b2SJaegeuk Kim } 2426351df4b2SJaegeuk Kim 2427b7ad7512SChao Yu static void write_current_sum_page(struct f2fs_sb_info *sbi, 2428b7ad7512SChao Yu int type, block_t blk_addr) 2429b7ad7512SChao Yu { 2430b7ad7512SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 24314d57b86dSChao Yu struct page *page = f2fs_grab_meta_page(sbi, blk_addr); 2432b7ad7512SChao Yu struct f2fs_summary_block *src = curseg->sum_blk; 2433b7ad7512SChao Yu struct f2fs_summary_block *dst; 2434b7ad7512SChao Yu 2435b7ad7512SChao Yu dst = (struct f2fs_summary_block *)page_address(page); 243681114baaSChao Yu memset(dst, 0, PAGE_SIZE); 2437b7ad7512SChao Yu 2438b7ad7512SChao Yu mutex_lock(&curseg->curseg_mutex); 2439b7ad7512SChao Yu 2440b7ad7512SChao Yu down_read(&curseg->journal_rwsem); 2441b7ad7512SChao Yu memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE); 2442b7ad7512SChao Yu up_read(&curseg->journal_rwsem); 2443b7ad7512SChao Yu 2444b7ad7512SChao Yu memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE); 2445b7ad7512SChao Yu memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE); 2446b7ad7512SChao Yu 2447b7ad7512SChao Yu mutex_unlock(&curseg->curseg_mutex); 2448b7ad7512SChao Yu 2449b7ad7512SChao Yu set_page_dirty(page); 2450b7ad7512SChao Yu f2fs_put_page(page, 1); 2451b7ad7512SChao Yu } 2452b7ad7512SChao Yu 2453093749e2SChao Yu static int is_next_segment_free(struct f2fs_sb_info *sbi, 2454093749e2SChao Yu struct curseg_info *curseg, int type) 2455a7881893SJaegeuk Kim { 2456a7881893SJaegeuk Kim unsigned int segno = curseg->segno + 1; 2457a7881893SJaegeuk Kim struct free_segmap_info *free_i = FREE_I(sbi); 2458a7881893SJaegeuk Kim 2459a7881893SJaegeuk Kim if (segno < MAIN_SEGS(sbi) && segno % sbi->segs_per_sec) 2460a7881893SJaegeuk Kim return !test_bit(segno, free_i->free_segmap); 2461a7881893SJaegeuk Kim return 0; 2462a7881893SJaegeuk Kim } 2463a7881893SJaegeuk Kim 24640a8165d7SJaegeuk Kim /* 2465351df4b2SJaegeuk Kim * Find a new segment from the free segments bitmap to right order 2466351df4b2SJaegeuk Kim * This function should be returned with success, otherwise BUG 2467351df4b2SJaegeuk Kim */ 2468351df4b2SJaegeuk Kim static void get_new_segment(struct f2fs_sb_info *sbi, 2469351df4b2SJaegeuk Kim unsigned int *newseg, bool new_sec, int dir) 2470351df4b2SJaegeuk Kim { 2471351df4b2SJaegeuk Kim struct free_segmap_info *free_i = FREE_I(sbi); 2472351df4b2SJaegeuk Kim unsigned int segno, secno, zoneno; 24737cd8558bSJaegeuk Kim unsigned int total_zones = MAIN_SECS(sbi) / sbi->secs_per_zone; 24744ddb1a4dSJaegeuk Kim unsigned int hint = GET_SEC_FROM_SEG(sbi, *newseg); 24754ddb1a4dSJaegeuk Kim unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg); 2476351df4b2SJaegeuk Kim unsigned int left_start = hint; 2477351df4b2SJaegeuk Kim bool init = true; 2478351df4b2SJaegeuk Kim int go_left = 0; 2479351df4b2SJaegeuk Kim int i; 2480351df4b2SJaegeuk Kim 24811a118ccfSChao Yu spin_lock(&free_i->segmap_lock); 2482351df4b2SJaegeuk Kim 2483351df4b2SJaegeuk Kim if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { 2484351df4b2SJaegeuk Kim segno = find_next_zero_bit(free_i->free_segmap, 24854ddb1a4dSJaegeuk Kim GET_SEG_FROM_SEC(sbi, hint + 1), *newseg + 1); 24864ddb1a4dSJaegeuk Kim if (segno < GET_SEG_FROM_SEC(sbi, hint + 1)) 2487351df4b2SJaegeuk Kim goto got_it; 2488351df4b2SJaegeuk Kim } 2489351df4b2SJaegeuk Kim find_other_zone: 24907cd8558bSJaegeuk Kim secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint); 24917cd8558bSJaegeuk Kim if (secno >= MAIN_SECS(sbi)) { 2492351df4b2SJaegeuk Kim if (dir == ALLOC_RIGHT) { 2493351df4b2SJaegeuk Kim secno = find_next_zero_bit(free_i->free_secmap, 24947cd8558bSJaegeuk Kim MAIN_SECS(sbi), 0); 24957cd8558bSJaegeuk Kim f2fs_bug_on(sbi, secno >= MAIN_SECS(sbi)); 2496351df4b2SJaegeuk Kim } else { 2497351df4b2SJaegeuk Kim go_left = 1; 2498351df4b2SJaegeuk Kim left_start = hint - 1; 2499351df4b2SJaegeuk Kim } 2500351df4b2SJaegeuk Kim } 2501351df4b2SJaegeuk Kim if (go_left == 0) 2502351df4b2SJaegeuk Kim goto skip_left; 2503351df4b2SJaegeuk Kim 2504351df4b2SJaegeuk Kim while (test_bit(left_start, free_i->free_secmap)) { 2505351df4b2SJaegeuk Kim if (left_start > 0) { 2506351df4b2SJaegeuk Kim left_start--; 2507351df4b2SJaegeuk Kim continue; 2508351df4b2SJaegeuk Kim } 2509351df4b2SJaegeuk Kim left_start = find_next_zero_bit(free_i->free_secmap, 25107cd8558bSJaegeuk Kim MAIN_SECS(sbi), 0); 25117cd8558bSJaegeuk Kim f2fs_bug_on(sbi, left_start >= MAIN_SECS(sbi)); 2512351df4b2SJaegeuk Kim break; 2513351df4b2SJaegeuk Kim } 2514351df4b2SJaegeuk Kim secno = left_start; 2515351df4b2SJaegeuk Kim skip_left: 25164ddb1a4dSJaegeuk Kim segno = GET_SEG_FROM_SEC(sbi, secno); 25174ddb1a4dSJaegeuk Kim zoneno = GET_ZONE_FROM_SEC(sbi, secno); 2518351df4b2SJaegeuk Kim 2519351df4b2SJaegeuk Kim /* give up on finding another zone */ 2520351df4b2SJaegeuk Kim if (!init) 2521351df4b2SJaegeuk Kim goto got_it; 2522351df4b2SJaegeuk Kim if (sbi->secs_per_zone == 1) 2523351df4b2SJaegeuk Kim goto got_it; 2524351df4b2SJaegeuk Kim if (zoneno == old_zoneno) 2525351df4b2SJaegeuk Kim goto got_it; 2526351df4b2SJaegeuk Kim if (dir == ALLOC_LEFT) { 2527351df4b2SJaegeuk Kim if (!go_left && zoneno + 1 >= total_zones) 2528351df4b2SJaegeuk Kim goto got_it; 2529351df4b2SJaegeuk Kim if (go_left && zoneno == 0) 2530351df4b2SJaegeuk Kim goto got_it; 2531351df4b2SJaegeuk Kim } 2532351df4b2SJaegeuk Kim for (i = 0; i < NR_CURSEG_TYPE; i++) 2533351df4b2SJaegeuk Kim if (CURSEG_I(sbi, i)->zone == zoneno) 2534351df4b2SJaegeuk Kim break; 2535351df4b2SJaegeuk Kim 2536351df4b2SJaegeuk Kim if (i < NR_CURSEG_TYPE) { 2537351df4b2SJaegeuk Kim /* zone is in user, try another */ 2538351df4b2SJaegeuk Kim if (go_left) 2539351df4b2SJaegeuk Kim hint = zoneno * sbi->secs_per_zone - 1; 2540351df4b2SJaegeuk Kim else if (zoneno + 1 >= total_zones) 2541351df4b2SJaegeuk Kim hint = 0; 2542351df4b2SJaegeuk Kim else 2543351df4b2SJaegeuk Kim hint = (zoneno + 1) * sbi->secs_per_zone; 2544351df4b2SJaegeuk Kim init = false; 2545351df4b2SJaegeuk Kim goto find_other_zone; 2546351df4b2SJaegeuk Kim } 2547351df4b2SJaegeuk Kim got_it: 2548351df4b2SJaegeuk Kim /* set it as dirty segment in free segmap */ 25499850cf4aSJaegeuk Kim f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap)); 2550351df4b2SJaegeuk Kim __set_inuse(sbi, segno); 2551351df4b2SJaegeuk Kim *newseg = segno; 25521a118ccfSChao Yu spin_unlock(&free_i->segmap_lock); 2553351df4b2SJaegeuk Kim } 2554351df4b2SJaegeuk Kim 2555351df4b2SJaegeuk Kim static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) 2556351df4b2SJaegeuk Kim { 2557351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2558351df4b2SJaegeuk Kim struct summary_footer *sum_footer; 2559093749e2SChao Yu unsigned short seg_type = curseg->seg_type; 2560351df4b2SJaegeuk Kim 2561d0b9e42aSChao Yu curseg->inited = true; 2562351df4b2SJaegeuk Kim curseg->segno = curseg->next_segno; 25634ddb1a4dSJaegeuk Kim curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno); 2564351df4b2SJaegeuk Kim curseg->next_blkoff = 0; 2565351df4b2SJaegeuk Kim curseg->next_segno = NULL_SEGNO; 2566351df4b2SJaegeuk Kim 2567351df4b2SJaegeuk Kim sum_footer = &(curseg->sum_blk->footer); 2568351df4b2SJaegeuk Kim memset(sum_footer, 0, sizeof(struct summary_footer)); 2569093749e2SChao Yu 2570093749e2SChao Yu sanity_check_seg_type(sbi, seg_type); 2571093749e2SChao Yu 2572093749e2SChao Yu if (IS_DATASEG(seg_type)) 2573351df4b2SJaegeuk Kim SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); 2574093749e2SChao Yu if (IS_NODESEG(seg_type)) 2575351df4b2SJaegeuk Kim SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); 2576093749e2SChao Yu __set_sit_entry_type(sbi, seg_type, curseg->segno, modified); 2577351df4b2SJaegeuk Kim } 2578351df4b2SJaegeuk Kim 25797a20b8a6SJaegeuk Kim static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) 25807a20b8a6SJaegeuk Kim { 2581d0b9e42aSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2582093749e2SChao Yu unsigned short seg_type = curseg->seg_type; 2583093749e2SChao Yu 2584093749e2SChao Yu sanity_check_seg_type(sbi, seg_type); 2585d0b9e42aSChao Yu 2586a7881893SJaegeuk Kim /* if segs_per_sec is large than 1, we need to keep original policy. */ 25872c70c5e3SChao Yu if (__is_large_section(sbi)) 2588d0b9e42aSChao Yu return curseg->segno; 2589d0b9e42aSChao Yu 2590d0b9e42aSChao Yu /* inmem log may not locate on any segment after mount */ 2591d0b9e42aSChao Yu if (!curseg->inited) 2592d0b9e42aSChao Yu return 0; 2593a7881893SJaegeuk Kim 25944354994fSDaniel Rosenberg if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 25954354994fSDaniel Rosenberg return 0; 25964354994fSDaniel Rosenberg 2597b94929d9SYunlong Song if (test_opt(sbi, NOHEAP) && 2598093749e2SChao Yu (seg_type == CURSEG_HOT_DATA || IS_NODESEG(seg_type))) 25997a20b8a6SJaegeuk Kim return 0; 26007a20b8a6SJaegeuk Kim 2601e066b83cSJaegeuk Kim if (SIT_I(sbi)->last_victim[ALLOC_NEXT]) 2602e066b83cSJaegeuk Kim return SIT_I(sbi)->last_victim[ALLOC_NEXT]; 260307939627SJaegeuk Kim 260407939627SJaegeuk Kim /* find segments from 0 to reuse freed segments */ 260563189b78SChao Yu if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE) 260607939627SJaegeuk Kim return 0; 260707939627SJaegeuk Kim 2608d0b9e42aSChao Yu return curseg->segno; 26097a20b8a6SJaegeuk Kim } 26107a20b8a6SJaegeuk Kim 26110a8165d7SJaegeuk Kim /* 2612351df4b2SJaegeuk Kim * Allocate a current working segment. 2613351df4b2SJaegeuk Kim * This function always allocates a free segment in LFS manner. 2614351df4b2SJaegeuk Kim */ 2615351df4b2SJaegeuk Kim static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) 2616351df4b2SJaegeuk Kim { 2617351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2618d0b9e42aSChao Yu unsigned short seg_type = curseg->seg_type; 2619351df4b2SJaegeuk Kim unsigned int segno = curseg->segno; 2620351df4b2SJaegeuk Kim int dir = ALLOC_LEFT; 2621351df4b2SJaegeuk Kim 2622d0b9e42aSChao Yu if (curseg->inited) 2623351df4b2SJaegeuk Kim write_sum_page(sbi, curseg->sum_blk, 262481fb5e87SHaicheng Li GET_SUM_BLOCK(sbi, segno)); 2625d0b9e42aSChao Yu if (seg_type == CURSEG_WARM_DATA || seg_type == CURSEG_COLD_DATA) 2626351df4b2SJaegeuk Kim dir = ALLOC_RIGHT; 2627351df4b2SJaegeuk Kim 2628351df4b2SJaegeuk Kim if (test_opt(sbi, NOHEAP)) 2629351df4b2SJaegeuk Kim dir = ALLOC_RIGHT; 2630351df4b2SJaegeuk Kim 26317a20b8a6SJaegeuk Kim segno = __get_next_segno(sbi, type); 2632351df4b2SJaegeuk Kim get_new_segment(sbi, &segno, new_sec, dir); 2633351df4b2SJaegeuk Kim curseg->next_segno = segno; 2634351df4b2SJaegeuk Kim reset_curseg(sbi, type, 1); 2635351df4b2SJaegeuk Kim curseg->alloc_type = LFS; 2636351df4b2SJaegeuk Kim } 2637351df4b2SJaegeuk Kim 2638351df4b2SJaegeuk Kim static void __next_free_blkoff(struct f2fs_sb_info *sbi, 2639351df4b2SJaegeuk Kim struct curseg_info *seg, block_t start) 2640351df4b2SJaegeuk Kim { 2641351df4b2SJaegeuk Kim struct seg_entry *se = get_seg_entry(sbi, seg->segno); 2642e81c93cfSChangman Lee int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 264360a3b782SJaegeuk Kim unsigned long *target_map = SIT_I(sbi)->tmp_map; 2644e81c93cfSChangman Lee unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 2645e81c93cfSChangman Lee unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 2646e81c93cfSChangman Lee int i, pos; 2647e81c93cfSChangman Lee 2648e81c93cfSChangman Lee for (i = 0; i < entries; i++) 2649e81c93cfSChangman Lee target_map[i] = ckpt_map[i] | cur_map[i]; 2650e81c93cfSChangman Lee 2651e81c93cfSChangman Lee pos = __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start); 2652e81c93cfSChangman Lee 2653e81c93cfSChangman Lee seg->next_blkoff = pos; 2654351df4b2SJaegeuk Kim } 2655351df4b2SJaegeuk Kim 26560a8165d7SJaegeuk Kim /* 2657351df4b2SJaegeuk Kim * If a segment is written by LFS manner, next block offset is just obtained 2658351df4b2SJaegeuk Kim * by increasing the current block offset. However, if a segment is written by 2659351df4b2SJaegeuk Kim * SSR manner, next block offset obtained by calling __next_free_blkoff 2660351df4b2SJaegeuk Kim */ 2661351df4b2SJaegeuk Kim static void __refresh_next_blkoff(struct f2fs_sb_info *sbi, 2662351df4b2SJaegeuk Kim struct curseg_info *seg) 2663351df4b2SJaegeuk Kim { 2664351df4b2SJaegeuk Kim if (seg->alloc_type == SSR) 2665351df4b2SJaegeuk Kim __next_free_blkoff(sbi, seg, seg->next_blkoff + 1); 2666351df4b2SJaegeuk Kim else 2667351df4b2SJaegeuk Kim seg->next_blkoff++; 2668351df4b2SJaegeuk Kim } 2669351df4b2SJaegeuk Kim 267061461fc9SChao Yu bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno) 267161461fc9SChao Yu { 267261461fc9SChao Yu struct seg_entry *se = get_seg_entry(sbi, segno); 267361461fc9SChao Yu int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 267461461fc9SChao Yu unsigned long *target_map = SIT_I(sbi)->tmp_map; 267561461fc9SChao Yu unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 267661461fc9SChao Yu unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 267761461fc9SChao Yu int i, pos; 267861461fc9SChao Yu 267961461fc9SChao Yu for (i = 0; i < entries; i++) 268061461fc9SChao Yu target_map[i] = ckpt_map[i] | cur_map[i]; 268161461fc9SChao Yu 268261461fc9SChao Yu pos = __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, 0); 268361461fc9SChao Yu 268461461fc9SChao Yu return pos < sbi->blocks_per_seg; 268561461fc9SChao Yu } 268661461fc9SChao Yu 26870a8165d7SJaegeuk Kim /* 2688351df4b2SJaegeuk Kim * This function always allocates a used segment(from dirty seglist) by SSR 2689351df4b2SJaegeuk Kim * manner, so it should recover the existing segment information of valid blocks 2690351df4b2SJaegeuk Kim */ 2691093749e2SChao Yu static void change_curseg(struct f2fs_sb_info *sbi, int type, bool flush) 2692351df4b2SJaegeuk Kim { 2693351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 2694351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2695351df4b2SJaegeuk Kim unsigned int new_segno = curseg->next_segno; 2696351df4b2SJaegeuk Kim struct f2fs_summary_block *sum_node; 2697351df4b2SJaegeuk Kim struct page *sum_page; 2698351df4b2SJaegeuk Kim 2699093749e2SChao Yu if (flush) 2700351df4b2SJaegeuk Kim write_sum_page(sbi, curseg->sum_blk, 2701351df4b2SJaegeuk Kim GET_SUM_BLOCK(sbi, curseg->segno)); 2702093749e2SChao Yu 2703351df4b2SJaegeuk Kim __set_test_and_inuse(sbi, new_segno); 2704351df4b2SJaegeuk Kim 2705351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 2706351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, new_segno, PRE); 2707351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, new_segno, DIRTY); 2708351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 2709351df4b2SJaegeuk Kim 2710351df4b2SJaegeuk Kim reset_curseg(sbi, type, 1); 2711351df4b2SJaegeuk Kim curseg->alloc_type = SSR; 2712351df4b2SJaegeuk Kim __next_free_blkoff(sbi, curseg, 0); 2713351df4b2SJaegeuk Kim 27144d57b86dSChao Yu sum_page = f2fs_get_sum_page(sbi, new_segno); 271586f33603SJaegeuk Kim if (IS_ERR(sum_page)) { 271686f33603SJaegeuk Kim /* GC won't be able to use stale summary pages by cp_error */ 271786f33603SJaegeuk Kim memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE); 271886f33603SJaegeuk Kim return; 271986f33603SJaegeuk Kim } 2720351df4b2SJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 2721351df4b2SJaegeuk Kim memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); 2722351df4b2SJaegeuk Kim f2fs_put_page(sum_page, 1); 2723351df4b2SJaegeuk Kim } 2724351df4b2SJaegeuk Kim 2725093749e2SChao Yu static int get_ssr_segment(struct f2fs_sb_info *sbi, int type, 2726093749e2SChao Yu int alloc_mode, unsigned long long age); 2727093749e2SChao Yu 2728093749e2SChao Yu static void get_atssr_segment(struct f2fs_sb_info *sbi, int type, 2729093749e2SChao Yu int target_type, int alloc_mode, 2730093749e2SChao Yu unsigned long long age) 2731093749e2SChao Yu { 2732093749e2SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2733093749e2SChao Yu 2734093749e2SChao Yu curseg->seg_type = target_type; 2735093749e2SChao Yu 2736093749e2SChao Yu if (get_ssr_segment(sbi, type, alloc_mode, age)) { 2737093749e2SChao Yu struct seg_entry *se = get_seg_entry(sbi, curseg->next_segno); 2738093749e2SChao Yu 2739093749e2SChao Yu curseg->seg_type = se->type; 2740093749e2SChao Yu change_curseg(sbi, type, true); 2741093749e2SChao Yu } else { 2742093749e2SChao Yu /* allocate cold segment by default */ 2743093749e2SChao Yu curseg->seg_type = CURSEG_COLD_DATA; 2744093749e2SChao Yu new_curseg(sbi, type, true); 2745093749e2SChao Yu } 2746093749e2SChao Yu stat_inc_seg_type(sbi, curseg); 2747093749e2SChao Yu } 2748093749e2SChao Yu 2749093749e2SChao Yu static void __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) 2750093749e2SChao Yu { 2751093749e2SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_ALL_DATA_ATGC); 2752093749e2SChao Yu 2753093749e2SChao Yu if (!sbi->am.atgc_enabled) 2754093749e2SChao Yu return; 2755093749e2SChao Yu 2756093749e2SChao Yu down_read(&SM_I(sbi)->curseg_lock); 2757093749e2SChao Yu 2758093749e2SChao Yu mutex_lock(&curseg->curseg_mutex); 2759093749e2SChao Yu down_write(&SIT_I(sbi)->sentry_lock); 2760093749e2SChao Yu 2761093749e2SChao Yu get_atssr_segment(sbi, CURSEG_ALL_DATA_ATGC, CURSEG_COLD_DATA, SSR, 0); 2762093749e2SChao Yu 2763093749e2SChao Yu up_write(&SIT_I(sbi)->sentry_lock); 2764093749e2SChao Yu mutex_unlock(&curseg->curseg_mutex); 2765093749e2SChao Yu 2766093749e2SChao Yu up_read(&SM_I(sbi)->curseg_lock); 2767093749e2SChao Yu 2768093749e2SChao Yu } 2769093749e2SChao Yu void f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi) 2770093749e2SChao Yu { 2771093749e2SChao Yu __f2fs_init_atgc_curseg(sbi); 2772093749e2SChao Yu } 2773093749e2SChao Yu 2774093749e2SChao Yu static void __f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi, int type) 2775d0b9e42aSChao Yu { 2776d0b9e42aSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2777d0b9e42aSChao Yu 2778d0b9e42aSChao Yu mutex_lock(&curseg->curseg_mutex); 2779d0b9e42aSChao Yu if (!curseg->inited) 2780d0b9e42aSChao Yu goto out; 2781d0b9e42aSChao Yu 2782d0b9e42aSChao Yu if (get_valid_blocks(sbi, curseg->segno, false)) { 2783d0b9e42aSChao Yu write_sum_page(sbi, curseg->sum_blk, 2784d0b9e42aSChao Yu GET_SUM_BLOCK(sbi, curseg->segno)); 2785d0b9e42aSChao Yu } else { 2786d0b9e42aSChao Yu mutex_lock(&DIRTY_I(sbi)->seglist_lock); 2787d0b9e42aSChao Yu __set_test_and_free(sbi, curseg->segno, true); 2788d0b9e42aSChao Yu mutex_unlock(&DIRTY_I(sbi)->seglist_lock); 2789d0b9e42aSChao Yu } 2790d0b9e42aSChao Yu out: 2791d0b9e42aSChao Yu mutex_unlock(&curseg->curseg_mutex); 2792d0b9e42aSChao Yu } 2793d0b9e42aSChao Yu 2794093749e2SChao Yu void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi) 2795093749e2SChao Yu { 2796093749e2SChao Yu __f2fs_save_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED); 2797093749e2SChao Yu 2798093749e2SChao Yu if (sbi->am.atgc_enabled) 2799093749e2SChao Yu __f2fs_save_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC); 2800093749e2SChao Yu } 2801093749e2SChao Yu 2802093749e2SChao Yu static void __f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi, int type) 2803d0b9e42aSChao Yu { 2804d0b9e42aSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2805d0b9e42aSChao Yu 2806d0b9e42aSChao Yu mutex_lock(&curseg->curseg_mutex); 2807d0b9e42aSChao Yu if (!curseg->inited) 2808d0b9e42aSChao Yu goto out; 2809d0b9e42aSChao Yu if (get_valid_blocks(sbi, curseg->segno, false)) 2810d0b9e42aSChao Yu goto out; 2811d0b9e42aSChao Yu 2812d0b9e42aSChao Yu mutex_lock(&DIRTY_I(sbi)->seglist_lock); 2813d0b9e42aSChao Yu __set_test_and_inuse(sbi, curseg->segno); 2814d0b9e42aSChao Yu mutex_unlock(&DIRTY_I(sbi)->seglist_lock); 2815d0b9e42aSChao Yu out: 2816d0b9e42aSChao Yu mutex_unlock(&curseg->curseg_mutex); 2817d0b9e42aSChao Yu } 2818d0b9e42aSChao Yu 2819093749e2SChao Yu void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi) 2820093749e2SChao Yu { 2821093749e2SChao Yu __f2fs_restore_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED); 2822093749e2SChao Yu 2823093749e2SChao Yu if (sbi->am.atgc_enabled) 2824093749e2SChao Yu __f2fs_restore_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC); 2825093749e2SChao Yu } 2826093749e2SChao Yu 2827093749e2SChao Yu static int get_ssr_segment(struct f2fs_sb_info *sbi, int type, 2828093749e2SChao Yu int alloc_mode, unsigned long long age) 282943727527SJaegeuk Kim { 283043727527SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 283143727527SJaegeuk Kim const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; 2832e066b83cSJaegeuk Kim unsigned segno = NULL_SEGNO; 2833093749e2SChao Yu unsigned short seg_type = curseg->seg_type; 2834d27c3d89SChao Yu int i, cnt; 2835d27c3d89SChao Yu bool reversed = false; 2836c192f7a4SJaegeuk Kim 2837093749e2SChao Yu sanity_check_seg_type(sbi, seg_type); 2838093749e2SChao Yu 28394d57b86dSChao Yu /* f2fs_need_SSR() already forces to do this */ 2840093749e2SChao Yu if (!v_ops->get_victim(sbi, &segno, BG_GC, seg_type, alloc_mode, age)) { 2841e066b83cSJaegeuk Kim curseg->next_segno = segno; 2842c192f7a4SJaegeuk Kim return 1; 2843e066b83cSJaegeuk Kim } 284443727527SJaegeuk Kim 284570d625cbSJaegeuk Kim /* For node segments, let's do SSR more intensively */ 2846093749e2SChao Yu if (IS_NODESEG(seg_type)) { 2847093749e2SChao Yu if (seg_type >= CURSEG_WARM_NODE) { 2848d27c3d89SChao Yu reversed = true; 2849d27c3d89SChao Yu i = CURSEG_COLD_NODE; 2850d27c3d89SChao Yu } else { 285170d625cbSJaegeuk Kim i = CURSEG_HOT_NODE; 2852d27c3d89SChao Yu } 2853d27c3d89SChao Yu cnt = NR_CURSEG_NODE_TYPE; 2854d27c3d89SChao Yu } else { 2855093749e2SChao Yu if (seg_type >= CURSEG_WARM_DATA) { 2856d27c3d89SChao Yu reversed = true; 2857d27c3d89SChao Yu i = CURSEG_COLD_DATA; 285870d625cbSJaegeuk Kim } else { 285970d625cbSJaegeuk Kim i = CURSEG_HOT_DATA; 2860d27c3d89SChao Yu } 2861d27c3d89SChao Yu cnt = NR_CURSEG_DATA_TYPE; 286270d625cbSJaegeuk Kim } 286343727527SJaegeuk Kim 2864d27c3d89SChao Yu for (; cnt-- > 0; reversed ? i-- : i++) { 2865093749e2SChao Yu if (i == seg_type) 2866c192f7a4SJaegeuk Kim continue; 2867093749e2SChao Yu if (!v_ops->get_victim(sbi, &segno, BG_GC, i, alloc_mode, age)) { 2868e066b83cSJaegeuk Kim curseg->next_segno = segno; 286943727527SJaegeuk Kim return 1; 2870c192f7a4SJaegeuk Kim } 2871e066b83cSJaegeuk Kim } 28724354994fSDaniel Rosenberg 28734354994fSDaniel Rosenberg /* find valid_blocks=0 in dirty list */ 28744354994fSDaniel Rosenberg if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { 28754354994fSDaniel Rosenberg segno = get_free_segment(sbi); 28764354994fSDaniel Rosenberg if (segno != NULL_SEGNO) { 28774354994fSDaniel Rosenberg curseg->next_segno = segno; 28784354994fSDaniel Rosenberg return 1; 28794354994fSDaniel Rosenberg } 28804354994fSDaniel Rosenberg } 288143727527SJaegeuk Kim return 0; 288243727527SJaegeuk Kim } 288343727527SJaegeuk Kim 2884351df4b2SJaegeuk Kim /* 2885351df4b2SJaegeuk Kim * flush out current segment and replace it with new segment 2886351df4b2SJaegeuk Kim * This function should be returned with success, otherwise BUG 2887351df4b2SJaegeuk Kim */ 2888351df4b2SJaegeuk Kim static void allocate_segment_by_default(struct f2fs_sb_info *sbi, 2889351df4b2SJaegeuk Kim int type, bool force) 2890351df4b2SJaegeuk Kim { 2891a7881893SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2892a7881893SJaegeuk Kim 28937b405275SGu Zheng if (force) 2894351df4b2SJaegeuk Kim new_curseg(sbi, type, true); 28955b6c6be2SJaegeuk Kim else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) && 2896093749e2SChao Yu curseg->seg_type == CURSEG_WARM_NODE) 2897351df4b2SJaegeuk Kim new_curseg(sbi, type, false); 2898093749e2SChao Yu else if (curseg->alloc_type == LFS && 2899093749e2SChao Yu is_next_segment_free(sbi, curseg, type) && 29004354994fSDaniel Rosenberg likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 2901a7881893SJaegeuk Kim new_curseg(sbi, type, false); 2902093749e2SChao Yu else if (f2fs_need_SSR(sbi) && 2903093749e2SChao Yu get_ssr_segment(sbi, type, SSR, 0)) 2904093749e2SChao Yu change_curseg(sbi, type, true); 2905351df4b2SJaegeuk Kim else 2906351df4b2SJaegeuk Kim new_curseg(sbi, type, false); 2907dcdfff65SJaegeuk Kim 2908a7881893SJaegeuk Kim stat_inc_seg_type(sbi, curseg); 2909351df4b2SJaegeuk Kim } 2910351df4b2SJaegeuk Kim 29110ef81833SChao Yu void f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type, 291204f0b2eaSQiuyang Sun unsigned int start, unsigned int end) 291304f0b2eaSQiuyang Sun { 291404f0b2eaSQiuyang Sun struct curseg_info *curseg = CURSEG_I(sbi, type); 291504f0b2eaSQiuyang Sun unsigned int segno; 291604f0b2eaSQiuyang Sun 291704f0b2eaSQiuyang Sun down_read(&SM_I(sbi)->curseg_lock); 291804f0b2eaSQiuyang Sun mutex_lock(&curseg->curseg_mutex); 291904f0b2eaSQiuyang Sun down_write(&SIT_I(sbi)->sentry_lock); 292004f0b2eaSQiuyang Sun 292104f0b2eaSQiuyang Sun segno = CURSEG_I(sbi, type)->segno; 292204f0b2eaSQiuyang Sun if (segno < start || segno > end) 292304f0b2eaSQiuyang Sun goto unlock; 292404f0b2eaSQiuyang Sun 2925093749e2SChao Yu if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type, SSR, 0)) 2926093749e2SChao Yu change_curseg(sbi, type, true); 292704f0b2eaSQiuyang Sun else 292804f0b2eaSQiuyang Sun new_curseg(sbi, type, true); 292904f0b2eaSQiuyang Sun 293004f0b2eaSQiuyang Sun stat_inc_seg_type(sbi, curseg); 293104f0b2eaSQiuyang Sun 293204f0b2eaSQiuyang Sun locate_dirty_segment(sbi, segno); 293304f0b2eaSQiuyang Sun unlock: 293404f0b2eaSQiuyang Sun up_write(&SIT_I(sbi)->sentry_lock); 293504f0b2eaSQiuyang Sun 293604f0b2eaSQiuyang Sun if (segno != curseg->segno) 2937dcbb4c10SJoe Perches f2fs_notice(sbi, "For resize: curseg of type %d: %u ==> %u", 293804f0b2eaSQiuyang Sun type, segno, curseg->segno); 293904f0b2eaSQiuyang Sun 294004f0b2eaSQiuyang Sun mutex_unlock(&curseg->curseg_mutex); 294104f0b2eaSQiuyang Sun up_read(&SM_I(sbi)->curseg_lock); 294204f0b2eaSQiuyang Sun } 294304f0b2eaSQiuyang Sun 2944e1175f02SChao Yu static void __allocate_new_segment(struct f2fs_sb_info *sbi, int type, 2945e1175f02SChao Yu bool new_sec) 2946351df4b2SJaegeuk Kim { 2947901d745fSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 29486ae1be13SJaegeuk Kim unsigned int old_segno; 2949901d745fSChao Yu 2950d0b9e42aSChao Yu if (!curseg->inited) 2951d0b9e42aSChao Yu goto alloc; 2952d0b9e42aSChao Yu 2953e1175f02SChao Yu if (curseg->next_blkoff || 2954e1175f02SChao Yu get_valid_blocks(sbi, curseg->segno, new_sec)) 2955e1175f02SChao Yu goto alloc; 2956e1175f02SChao Yu 295761461fc9SChao Yu if (!get_ckpt_valid_blocks(sbi, curseg->segno, new_sec)) 2958901d745fSChao Yu return; 2959d0b9e42aSChao Yu alloc: 2960901d745fSChao Yu old_segno = curseg->segno; 2961901d745fSChao Yu SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true); 2962901d745fSChao Yu locate_dirty_segment(sbi, old_segno); 2963901d745fSChao Yu } 2964901d745fSChao Yu 2965e1175f02SChao Yu static void __allocate_new_section(struct f2fs_sb_info *sbi, int type) 2966e1175f02SChao Yu { 2967e1175f02SChao Yu __allocate_new_segment(sbi, type, true); 2968e1175f02SChao Yu } 2969e1175f02SChao Yu 2970e1175f02SChao Yu void f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type) 2971901d745fSChao Yu { 2972823d13e1SChao Yu down_read(&SM_I(sbi)->curseg_lock); 2973901d745fSChao Yu down_write(&SIT_I(sbi)->sentry_lock); 2974e1175f02SChao Yu __allocate_new_section(sbi, type); 2975901d745fSChao Yu up_write(&SIT_I(sbi)->sentry_lock); 2976823d13e1SChao Yu up_read(&SM_I(sbi)->curseg_lock); 2977901d745fSChao Yu } 2978901d745fSChao Yu 2979901d745fSChao Yu void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi) 2980901d745fSChao Yu { 2981351df4b2SJaegeuk Kim int i; 2982351df4b2SJaegeuk Kim 2983823d13e1SChao Yu down_read(&SM_I(sbi)->curseg_lock); 29843d26fa6bSChao Yu down_write(&SIT_I(sbi)->sentry_lock); 2985901d745fSChao Yu for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) 2986e1175f02SChao Yu __allocate_new_segment(sbi, i, false); 29873d26fa6bSChao Yu up_write(&SIT_I(sbi)->sentry_lock); 2988823d13e1SChao Yu up_read(&SM_I(sbi)->curseg_lock); 2989351df4b2SJaegeuk Kim } 2990351df4b2SJaegeuk Kim 2991351df4b2SJaegeuk Kim static const struct segment_allocation default_salloc_ops = { 2992351df4b2SJaegeuk Kim .allocate_segment = allocate_segment_by_default, 2993351df4b2SJaegeuk Kim }; 2994351df4b2SJaegeuk Kim 29954d57b86dSChao Yu bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, 29964d57b86dSChao Yu struct cp_control *cpc) 299725290fa5SJaegeuk Kim { 299825290fa5SJaegeuk Kim __u64 trim_start = cpc->trim_start; 299925290fa5SJaegeuk Kim bool has_candidate = false; 300025290fa5SJaegeuk Kim 30013d26fa6bSChao Yu down_write(&SIT_I(sbi)->sentry_lock); 300225290fa5SJaegeuk Kim for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { 300325290fa5SJaegeuk Kim if (add_discard_addrs(sbi, cpc, true)) { 300425290fa5SJaegeuk Kim has_candidate = true; 300525290fa5SJaegeuk Kim break; 300625290fa5SJaegeuk Kim } 300725290fa5SJaegeuk Kim } 30083d26fa6bSChao Yu up_write(&SIT_I(sbi)->sentry_lock); 300925290fa5SJaegeuk Kim 301025290fa5SJaegeuk Kim cpc->trim_start = trim_start; 301125290fa5SJaegeuk Kim return has_candidate; 301225290fa5SJaegeuk Kim } 301325290fa5SJaegeuk Kim 301401f9cf6dSChao Yu static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, 30159a997188SJaegeuk Kim struct discard_policy *dpolicy, 30169a997188SJaegeuk Kim unsigned int start, unsigned int end) 30179a997188SJaegeuk Kim { 30189a997188SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 30199a997188SJaegeuk Kim struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 30209a997188SJaegeuk Kim struct rb_node **insert_p = NULL, *insert_parent = NULL; 30219a997188SJaegeuk Kim struct discard_cmd *dc; 30229a997188SJaegeuk Kim struct blk_plug plug; 30239a997188SJaegeuk Kim int issued; 302401f9cf6dSChao Yu unsigned int trimmed = 0; 30259a997188SJaegeuk Kim 30269a997188SJaegeuk Kim next: 30279a997188SJaegeuk Kim issued = 0; 30289a997188SJaegeuk Kim 30299a997188SJaegeuk Kim mutex_lock(&dcc->cmd_lock); 303067fce70bSChao Yu if (unlikely(dcc->rbtree_check)) 303167fce70bSChao Yu f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi, 30322e9b2bb2SChao Yu &dcc->root, false)); 30339a997188SJaegeuk Kim 30344d57b86dSChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, 30359a997188SJaegeuk Kim NULL, start, 30369a997188SJaegeuk Kim (struct rb_entry **)&prev_dc, 30379a997188SJaegeuk Kim (struct rb_entry **)&next_dc, 30384dada3fdSChao Yu &insert_p, &insert_parent, true, NULL); 30399a997188SJaegeuk Kim if (!dc) 30409a997188SJaegeuk Kim dc = next_dc; 30419a997188SJaegeuk Kim 30429a997188SJaegeuk Kim blk_start_plug(&plug); 30439a997188SJaegeuk Kim 30449a997188SJaegeuk Kim while (dc && dc->lstart <= end) { 30459a997188SJaegeuk Kim struct rb_node *node; 30466b9cb124SChao Yu int err = 0; 30479a997188SJaegeuk Kim 30489a997188SJaegeuk Kim if (dc->len < dpolicy->granularity) 30499a997188SJaegeuk Kim goto skip; 30509a997188SJaegeuk Kim 30519a997188SJaegeuk Kim if (dc->state != D_PREP) { 30529a997188SJaegeuk Kim list_move_tail(&dc->list, &dcc->fstrim_list); 30539a997188SJaegeuk Kim goto skip; 30549a997188SJaegeuk Kim } 30559a997188SJaegeuk Kim 30566b9cb124SChao Yu err = __submit_discard_cmd(sbi, dpolicy, dc, &issued); 30579a997188SJaegeuk Kim 305835ec7d57SChao Yu if (issued >= dpolicy->max_requests) { 30599a997188SJaegeuk Kim start = dc->lstart + dc->len; 30609a997188SJaegeuk Kim 30616b9cb124SChao Yu if (err) 30626b9cb124SChao Yu __remove_discard_cmd(sbi, dc); 30636b9cb124SChao Yu 30649a997188SJaegeuk Kim blk_finish_plug(&plug); 30659a997188SJaegeuk Kim mutex_unlock(&dcc->cmd_lock); 306601f9cf6dSChao Yu trimmed += __wait_all_discard_cmd(sbi, NULL); 30675df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, DEFAULT_IO_TIMEOUT); 30689a997188SJaegeuk Kim goto next; 30699a997188SJaegeuk Kim } 30709a997188SJaegeuk Kim skip: 30719a997188SJaegeuk Kim node = rb_next(&dc->rb_node); 30726b9cb124SChao Yu if (err) 30736b9cb124SChao Yu __remove_discard_cmd(sbi, dc); 30749a997188SJaegeuk Kim dc = rb_entry_safe(node, struct discard_cmd, rb_node); 30759a997188SJaegeuk Kim 30769a997188SJaegeuk Kim if (fatal_signal_pending(current)) 30779a997188SJaegeuk Kim break; 30789a997188SJaegeuk Kim } 30799a997188SJaegeuk Kim 30809a997188SJaegeuk Kim blk_finish_plug(&plug); 30819a997188SJaegeuk Kim mutex_unlock(&dcc->cmd_lock); 308201f9cf6dSChao Yu 308301f9cf6dSChao Yu return trimmed; 30849a997188SJaegeuk Kim } 30859a997188SJaegeuk Kim 30864b2fecc8SJaegeuk Kim int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) 30874b2fecc8SJaegeuk Kim { 3088f7ef9b83SJaegeuk Kim __u64 start = F2FS_BYTES_TO_BLK(range->start); 3089f7ef9b83SJaegeuk Kim __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; 3090377224c4SChao Yu unsigned int start_segno, end_segno; 30918412663dSChao Yu block_t start_block, end_block; 30924b2fecc8SJaegeuk Kim struct cp_control cpc; 309378997b56SChao Yu struct discard_policy dpolicy; 30940ea80512SChao Yu unsigned long long trimmed = 0; 3095c34f42e2SChao Yu int err = 0; 3096b0332a0fSChao Yu bool need_align = f2fs_lfs_mode(sbi) && __is_large_section(sbi); 30974b2fecc8SJaegeuk Kim 3098836b5a63SJaegeuk Kim if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) 30994b2fecc8SJaegeuk Kim return -EINVAL; 31004b2fecc8SJaegeuk Kim 31013f16ecd9SChao Yu if (end < MAIN_BLKADDR(sbi)) 31023f16ecd9SChao Yu goto out; 31034b2fecc8SJaegeuk Kim 3104ed214a11SYunlei He if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { 3105dcbb4c10SJoe Perches f2fs_warn(sbi, "Found FS corruption, run fsck to fix."); 310610f966bbSChao Yu return -EFSCORRUPTED; 3107ed214a11SYunlei He } 3108ed214a11SYunlei He 31094b2fecc8SJaegeuk Kim /* start/end segment number in main_area */ 31107cd8558bSJaegeuk Kim start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); 31117cd8558bSJaegeuk Kim end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : 31127cd8558bSJaegeuk Kim GET_SEGNO(sbi, end); 3113ad6672bbSYunlong Song if (need_align) { 3114ad6672bbSYunlong Song start_segno = rounddown(start_segno, sbi->segs_per_sec); 3115ad6672bbSYunlong Song end_segno = roundup(end_segno + 1, sbi->segs_per_sec) - 1; 3116ad6672bbSYunlong Song } 31178412663dSChao Yu 31184b2fecc8SJaegeuk Kim cpc.reason = CP_DISCARD; 3119836b5a63SJaegeuk Kim cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); 3120377224c4SChao Yu cpc.trim_start = start_segno; 3121377224c4SChao Yu cpc.trim_end = end_segno; 3122a66cdd98SJaegeuk Kim 3123a66cdd98SJaegeuk Kim if (sbi->discard_blks == 0) 3124377224c4SChao Yu goto out; 3125bba681cbSJaegeuk Kim 3126fb24fea7SChao Yu down_write(&sbi->gc_lock); 31274d57b86dSChao Yu err = f2fs_write_checkpoint(sbi, &cpc); 3128fb24fea7SChao Yu up_write(&sbi->gc_lock); 3129e9328353SChao Yu if (err) 3130377224c4SChao Yu goto out; 31318412663dSChao Yu 3132e555da9fSJaegeuk Kim /* 3133e555da9fSJaegeuk Kim * We filed discard candidates, but actually we don't need to wait for 3134e555da9fSJaegeuk Kim * all of them, since they'll be issued in idle time along with runtime 3135e555da9fSJaegeuk Kim * discard option. User configuration looks like using runtime discard 3136e555da9fSJaegeuk Kim * or periodic fstrim instead of it. 3137e555da9fSJaegeuk Kim */ 31387d20c8abSChao Yu if (f2fs_realtime_discard_enable(sbi)) 31395a615492SJaegeuk Kim goto out; 31405a615492SJaegeuk Kim 31415a615492SJaegeuk Kim start_block = START_BLOCK(sbi, start_segno); 31425a615492SJaegeuk Kim end_block = START_BLOCK(sbi, end_segno + 1); 31435a615492SJaegeuk Kim 31445a615492SJaegeuk Kim __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); 314501f9cf6dSChao Yu trimmed = __issue_discard_cmd_range(sbi, &dpolicy, 314601f9cf6dSChao Yu start_block, end_block); 31475a615492SJaegeuk Kim 314801f9cf6dSChao Yu trimmed += __wait_discard_cmd_range(sbi, &dpolicy, 31490ea80512SChao Yu start_block, end_block); 3150377224c4SChao Yu out: 31516eae2694SChao Yu if (!err) 31526eae2694SChao Yu range->len = F2FS_BLK_TO_BYTES(trimmed); 3153c34f42e2SChao Yu return err; 31544b2fecc8SJaegeuk Kim } 31554b2fecc8SJaegeuk Kim 3156093749e2SChao Yu static bool __has_curseg_space(struct f2fs_sb_info *sbi, 3157093749e2SChao Yu struct curseg_info *curseg) 3158351df4b2SJaegeuk Kim { 3159de881df9SAravind Ramesh return curseg->next_blkoff < f2fs_usable_blks_in_seg(sbi, 3160de881df9SAravind Ramesh curseg->segno); 3161351df4b2SJaegeuk Kim } 3162351df4b2SJaegeuk Kim 31634d57b86dSChao Yu int f2fs_rw_hint_to_seg_type(enum rw_hint hint) 31644f0a03d3SHyunchul Lee { 31654f0a03d3SHyunchul Lee switch (hint) { 31664f0a03d3SHyunchul Lee case WRITE_LIFE_SHORT: 31674f0a03d3SHyunchul Lee return CURSEG_HOT_DATA; 31684f0a03d3SHyunchul Lee case WRITE_LIFE_EXTREME: 31694f0a03d3SHyunchul Lee return CURSEG_COLD_DATA; 31704f0a03d3SHyunchul Lee default: 31714f0a03d3SHyunchul Lee return CURSEG_WARM_DATA; 31724f0a03d3SHyunchul Lee } 31734f0a03d3SHyunchul Lee } 31744f0a03d3SHyunchul Lee 31750cdd3195SHyunchul Lee /* This returns write hints for each segment type. This hints will be 31760cdd3195SHyunchul Lee * passed down to block layer. There are mapping tables which depend on 31770cdd3195SHyunchul Lee * the mount option 'whint_mode'. 31780cdd3195SHyunchul Lee * 31790cdd3195SHyunchul Lee * 1) whint_mode=off. F2FS only passes down WRITE_LIFE_NOT_SET. 31800cdd3195SHyunchul Lee * 31810cdd3195SHyunchul Lee * 2) whint_mode=user-based. F2FS tries to pass down hints given by users. 31820cdd3195SHyunchul Lee * 31830cdd3195SHyunchul Lee * User F2FS Block 31840cdd3195SHyunchul Lee * ---- ---- ----- 31850cdd3195SHyunchul Lee * META WRITE_LIFE_NOT_SET 31860cdd3195SHyunchul Lee * HOT_NODE " 31870cdd3195SHyunchul Lee * WARM_NODE " 31880cdd3195SHyunchul Lee * COLD_NODE " 31890cdd3195SHyunchul Lee * ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME 31900cdd3195SHyunchul Lee * extension list " " 31910cdd3195SHyunchul Lee * 31920cdd3195SHyunchul Lee * -- buffered io 31930cdd3195SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 31940cdd3195SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 31950cdd3195SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET 31960cdd3195SHyunchul Lee * WRITE_LIFE_NONE " " 31970cdd3195SHyunchul Lee * WRITE_LIFE_MEDIUM " " 31980cdd3195SHyunchul Lee * WRITE_LIFE_LONG " " 31990cdd3195SHyunchul Lee * 32000cdd3195SHyunchul Lee * -- direct io 32010cdd3195SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 32020cdd3195SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 32030cdd3195SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET 32040cdd3195SHyunchul Lee * WRITE_LIFE_NONE " WRITE_LIFE_NONE 32050cdd3195SHyunchul Lee * WRITE_LIFE_MEDIUM " WRITE_LIFE_MEDIUM 32060cdd3195SHyunchul Lee * WRITE_LIFE_LONG " WRITE_LIFE_LONG 32070cdd3195SHyunchul Lee * 3208f2e703f9SHyunchul Lee * 3) whint_mode=fs-based. F2FS passes down hints with its policy. 3209f2e703f9SHyunchul Lee * 3210f2e703f9SHyunchul Lee * User F2FS Block 3211f2e703f9SHyunchul Lee * ---- ---- ----- 3212f2e703f9SHyunchul Lee * META WRITE_LIFE_MEDIUM; 3213f2e703f9SHyunchul Lee * HOT_NODE WRITE_LIFE_NOT_SET 3214f2e703f9SHyunchul Lee * WARM_NODE " 3215f2e703f9SHyunchul Lee * COLD_NODE WRITE_LIFE_NONE 3216f2e703f9SHyunchul Lee * ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME 3217f2e703f9SHyunchul Lee * extension list " " 3218f2e703f9SHyunchul Lee * 3219f2e703f9SHyunchul Lee * -- buffered io 3220f2e703f9SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 3221f2e703f9SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 3222f2e703f9SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_LONG 3223f2e703f9SHyunchul Lee * WRITE_LIFE_NONE " " 3224f2e703f9SHyunchul Lee * WRITE_LIFE_MEDIUM " " 3225f2e703f9SHyunchul Lee * WRITE_LIFE_LONG " " 3226f2e703f9SHyunchul Lee * 3227f2e703f9SHyunchul Lee * -- direct io 3228f2e703f9SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 3229f2e703f9SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 3230f2e703f9SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET 3231f2e703f9SHyunchul Lee * WRITE_LIFE_NONE " WRITE_LIFE_NONE 3232f2e703f9SHyunchul Lee * WRITE_LIFE_MEDIUM " WRITE_LIFE_MEDIUM 3233f2e703f9SHyunchul Lee * WRITE_LIFE_LONG " WRITE_LIFE_LONG 32340cdd3195SHyunchul Lee */ 32350cdd3195SHyunchul Lee 32364d57b86dSChao Yu enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi, 32370cdd3195SHyunchul Lee enum page_type type, enum temp_type temp) 32380cdd3195SHyunchul Lee { 323963189b78SChao Yu if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_USER) { 32400cdd3195SHyunchul Lee if (type == DATA) { 3241f2e703f9SHyunchul Lee if (temp == WARM) 3242f2e703f9SHyunchul Lee return WRITE_LIFE_NOT_SET; 3243f2e703f9SHyunchul Lee else if (temp == HOT) 32440cdd3195SHyunchul Lee return WRITE_LIFE_SHORT; 3245f2e703f9SHyunchul Lee else if (temp == COLD) 3246f2e703f9SHyunchul Lee return WRITE_LIFE_EXTREME; 32470cdd3195SHyunchul Lee } else { 32480cdd3195SHyunchul Lee return WRITE_LIFE_NOT_SET; 32490cdd3195SHyunchul Lee } 325063189b78SChao Yu } else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS) { 3251f2e703f9SHyunchul Lee if (type == DATA) { 3252f2e703f9SHyunchul Lee if (temp == WARM) 3253f2e703f9SHyunchul Lee return WRITE_LIFE_LONG; 3254f2e703f9SHyunchul Lee else if (temp == HOT) 3255f2e703f9SHyunchul Lee return WRITE_LIFE_SHORT; 3256f2e703f9SHyunchul Lee else if (temp == COLD) 3257f2e703f9SHyunchul Lee return WRITE_LIFE_EXTREME; 3258f2e703f9SHyunchul Lee } else if (type == NODE) { 3259f2e703f9SHyunchul Lee if (temp == WARM || temp == HOT) 32600cdd3195SHyunchul Lee return WRITE_LIFE_NOT_SET; 3261f2e703f9SHyunchul Lee else if (temp == COLD) 3262f2e703f9SHyunchul Lee return WRITE_LIFE_NONE; 3263f2e703f9SHyunchul Lee } else if (type == META) { 3264f2e703f9SHyunchul Lee return WRITE_LIFE_MEDIUM; 32650cdd3195SHyunchul Lee } 32660cdd3195SHyunchul Lee } 3267f2e703f9SHyunchul Lee return WRITE_LIFE_NOT_SET; 3268f2e703f9SHyunchul Lee } 32690cdd3195SHyunchul Lee 327081377bd6SJaegeuk Kim static int __get_segment_type_2(struct f2fs_io_info *fio) 3271351df4b2SJaegeuk Kim { 327281377bd6SJaegeuk Kim if (fio->type == DATA) 3273351df4b2SJaegeuk Kim return CURSEG_HOT_DATA; 3274351df4b2SJaegeuk Kim else 3275351df4b2SJaegeuk Kim return CURSEG_HOT_NODE; 3276351df4b2SJaegeuk Kim } 3277351df4b2SJaegeuk Kim 327881377bd6SJaegeuk Kim static int __get_segment_type_4(struct f2fs_io_info *fio) 3279351df4b2SJaegeuk Kim { 328081377bd6SJaegeuk Kim if (fio->type == DATA) { 328181377bd6SJaegeuk Kim struct inode *inode = fio->page->mapping->host; 3282351df4b2SJaegeuk Kim 3283351df4b2SJaegeuk Kim if (S_ISDIR(inode->i_mode)) 3284351df4b2SJaegeuk Kim return CURSEG_HOT_DATA; 3285351df4b2SJaegeuk Kim else 3286351df4b2SJaegeuk Kim return CURSEG_COLD_DATA; 3287351df4b2SJaegeuk Kim } else { 328881377bd6SJaegeuk Kim if (IS_DNODE(fio->page) && is_cold_node(fio->page)) 3289a344b9fdSJaegeuk Kim return CURSEG_WARM_NODE; 3290351df4b2SJaegeuk Kim else 3291351df4b2SJaegeuk Kim return CURSEG_COLD_NODE; 3292351df4b2SJaegeuk Kim } 3293351df4b2SJaegeuk Kim } 3294351df4b2SJaegeuk Kim 329581377bd6SJaegeuk Kim static int __get_segment_type_6(struct f2fs_io_info *fio) 3296351df4b2SJaegeuk Kim { 329781377bd6SJaegeuk Kim if (fio->type == DATA) { 329881377bd6SJaegeuk Kim struct inode *inode = fio->page->mapping->host; 3299351df4b2SJaegeuk Kim 3300093749e2SChao Yu if (is_cold_data(fio->page)) { 3301ac2d750bSWeichao Guo if (fio->sbi->am.atgc_enabled && 3302ac2d750bSWeichao Guo (fio->io_type == FS_DATA_IO) && 3303ac2d750bSWeichao Guo (fio->sbi->gc_mode != GC_URGENT_HIGH)) 3304093749e2SChao Yu return CURSEG_ALL_DATA_ATGC; 3305093749e2SChao Yu else 3306093749e2SChao Yu return CURSEG_COLD_DATA; 3307093749e2SChao Yu } 3308602a16d5SDaeho Jeong if (file_is_cold(inode) || f2fs_need_compress_data(inode)) 3309351df4b2SJaegeuk Kim return CURSEG_COLD_DATA; 3310b6a06cbbSChao Yu if (file_is_hot(inode) || 3311b4c3ca8bSChao Yu is_inode_flag_set(inode, FI_HOT_DATA) || 33122079f115SChao Yu f2fs_is_atomic_file(inode) || 33132079f115SChao Yu f2fs_is_volatile_file(inode)) 3314ef095d19SJaegeuk Kim return CURSEG_HOT_DATA; 33154d57b86dSChao Yu return f2fs_rw_hint_to_seg_type(inode->i_write_hint); 3316351df4b2SJaegeuk Kim } else { 331781377bd6SJaegeuk Kim if (IS_DNODE(fio->page)) 331881377bd6SJaegeuk Kim return is_cold_node(fio->page) ? CURSEG_WARM_NODE : 3319351df4b2SJaegeuk Kim CURSEG_HOT_NODE; 3320351df4b2SJaegeuk Kim return CURSEG_COLD_NODE; 3321351df4b2SJaegeuk Kim } 3322351df4b2SJaegeuk Kim } 3323351df4b2SJaegeuk Kim 332481377bd6SJaegeuk Kim static int __get_segment_type(struct f2fs_io_info *fio) 3325351df4b2SJaegeuk Kim { 3326a912b54dSJaegeuk Kim int type = 0; 3327a912b54dSJaegeuk Kim 332863189b78SChao Yu switch (F2FS_OPTION(fio->sbi).active_logs) { 3329351df4b2SJaegeuk Kim case 2: 3330a912b54dSJaegeuk Kim type = __get_segment_type_2(fio); 3331a912b54dSJaegeuk Kim break; 3332351df4b2SJaegeuk Kim case 4: 3333a912b54dSJaegeuk Kim type = __get_segment_type_4(fio); 3334a912b54dSJaegeuk Kim break; 3335a912b54dSJaegeuk Kim case 6: 3336a912b54dSJaegeuk Kim type = __get_segment_type_6(fio); 3337a912b54dSJaegeuk Kim break; 3338a912b54dSJaegeuk Kim default: 3339a912b54dSJaegeuk Kim f2fs_bug_on(fio->sbi, true); 3340351df4b2SJaegeuk Kim } 334181377bd6SJaegeuk Kim 3342a912b54dSJaegeuk Kim if (IS_HOT(type)) 3343a912b54dSJaegeuk Kim fio->temp = HOT; 3344a912b54dSJaegeuk Kim else if (IS_WARM(type)) 3345a912b54dSJaegeuk Kim fio->temp = WARM; 3346a912b54dSJaegeuk Kim else 3347a912b54dSJaegeuk Kim fio->temp = COLD; 3348a912b54dSJaegeuk Kim return type; 3349351df4b2SJaegeuk Kim } 3350351df4b2SJaegeuk Kim 33514d57b86dSChao Yu void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, 3352351df4b2SJaegeuk Kim block_t old_blkaddr, block_t *new_blkaddr, 3353fb830fc5SChao Yu struct f2fs_summary *sum, int type, 3354093749e2SChao Yu struct f2fs_io_info *fio) 3355351df4b2SJaegeuk Kim { 3356351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 33576ae1be13SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 3358c5d02785SChao Yu unsigned long long old_mtime; 3359093749e2SChao Yu bool from_gc = (type == CURSEG_ALL_DATA_ATGC); 3360093749e2SChao Yu struct seg_entry *se = NULL; 3361351df4b2SJaegeuk Kim 33622b60311dSChao Yu down_read(&SM_I(sbi)->curseg_lock); 33632b60311dSChao Yu 3364351df4b2SJaegeuk Kim mutex_lock(&curseg->curseg_mutex); 33653d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 3366351df4b2SJaegeuk Kim 3367093749e2SChao Yu if (from_gc) { 3368093749e2SChao Yu f2fs_bug_on(sbi, GET_SEGNO(sbi, old_blkaddr) == NULL_SEGNO); 3369093749e2SChao Yu se = get_seg_entry(sbi, GET_SEGNO(sbi, old_blkaddr)); 3370093749e2SChao Yu sanity_check_seg_type(sbi, se->type); 3371093749e2SChao Yu f2fs_bug_on(sbi, IS_NODESEG(se->type)); 3372093749e2SChao Yu } 3373351df4b2SJaegeuk Kim *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 3374351df4b2SJaegeuk Kim 3375093749e2SChao Yu f2fs_bug_on(sbi, curseg->next_blkoff >= sbi->blocks_per_seg); 3376093749e2SChao Yu 33774e6a8d9bSJaegeuk Kim f2fs_wait_discard_bio(sbi, *new_blkaddr); 33784e6a8d9bSJaegeuk Kim 3379351df4b2SJaegeuk Kim /* 3380351df4b2SJaegeuk Kim * __add_sum_entry should be resided under the curseg_mutex 3381351df4b2SJaegeuk Kim * because, this function updates a summary entry in the 3382351df4b2SJaegeuk Kim * current summary block. 3383351df4b2SJaegeuk Kim */ 3384e79efe3bSHaicheng Li __add_sum_entry(sbi, type, sum); 3385351df4b2SJaegeuk Kim 3386351df4b2SJaegeuk Kim __refresh_next_blkoff(sbi, curseg); 3387dcdfff65SJaegeuk Kim 3388dcdfff65SJaegeuk Kim stat_inc_block_count(sbi, curseg); 3389351df4b2SJaegeuk Kim 3390c5d02785SChao Yu if (from_gc) { 3391c5d02785SChao Yu old_mtime = get_segment_mtime(sbi, old_blkaddr); 3392c5d02785SChao Yu } else { 3393c5d02785SChao Yu update_segment_mtime(sbi, old_blkaddr, 0); 3394c5d02785SChao Yu old_mtime = 0; 3395c5d02785SChao Yu } 3396c5d02785SChao Yu update_segment_mtime(sbi, *new_blkaddr, old_mtime); 3397c5d02785SChao Yu 339865f1b80bSYunlong Song /* 339965f1b80bSYunlong Song * SIT information should be updated before segment allocation, 340065f1b80bSYunlong Song * since SSR needs latest valid block information. 340165f1b80bSYunlong Song */ 340265f1b80bSYunlong Song update_sit_entry(sbi, *new_blkaddr, 1); 340365f1b80bSYunlong Song if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) 340465f1b80bSYunlong Song update_sit_entry(sbi, old_blkaddr, -1); 340565f1b80bSYunlong Song 3406093749e2SChao Yu if (!__has_curseg_space(sbi, curseg)) { 3407093749e2SChao Yu if (from_gc) 3408093749e2SChao Yu get_atssr_segment(sbi, type, se->type, 3409093749e2SChao Yu AT_SSR, se->mtime); 3410093749e2SChao Yu else 34113436c4bdSYunlong Song sit_i->s_ops->allocate_segment(sbi, type, false); 3412093749e2SChao Yu } 3413c6f82fe9SJaegeuk Kim /* 341465f1b80bSYunlong Song * segment dirty status should be updated after segment allocation, 341565f1b80bSYunlong Song * so we just need to update status only one time after previous 341665f1b80bSYunlong Song * segment being closed. 3417c6f82fe9SJaegeuk Kim */ 341865f1b80bSYunlong Song locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); 341965f1b80bSYunlong Song locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr)); 34203436c4bdSYunlong Song 34213d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 3422351df4b2SJaegeuk Kim 3423704956ecSChao Yu if (page && IS_NODESEG(type)) { 3424351df4b2SJaegeuk Kim fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); 3425351df4b2SJaegeuk Kim 3426704956ecSChao Yu f2fs_inode_chksum_set(sbi, page); 3427704956ecSChao Yu } 3428704956ecSChao Yu 3429f608c38cSChao Yu if (fio) { 3430fb830fc5SChao Yu struct f2fs_bio_info *io; 3431fb830fc5SChao Yu 343225ae837eSChao Yu if (F2FS_IO_ALIGNED(sbi)) 343325ae837eSChao Yu fio->retry = false; 343425ae837eSChao Yu 3435fb830fc5SChao Yu INIT_LIST_HEAD(&fio->list); 3436fb830fc5SChao Yu fio->in_list = true; 3437fb830fc5SChao Yu io = sbi->write_io[fio->type] + fio->temp; 3438fb830fc5SChao Yu spin_lock(&io->io_lock); 3439fb830fc5SChao Yu list_add_tail(&fio->list, &io->io_list); 3440fb830fc5SChao Yu spin_unlock(&io->io_lock); 3441fb830fc5SChao Yu } 3442fb830fc5SChao Yu 3443bfad7c2dSJaegeuk Kim mutex_unlock(&curseg->curseg_mutex); 34442b60311dSChao Yu 34452b60311dSChao Yu up_read(&SM_I(sbi)->curseg_lock); 3446bfad7c2dSJaegeuk Kim } 3447bfad7c2dSJaegeuk Kim 344839d787beSChao Yu static void update_device_state(struct f2fs_io_info *fio) 344939d787beSChao Yu { 345039d787beSChao Yu struct f2fs_sb_info *sbi = fio->sbi; 345139d787beSChao Yu unsigned int devidx; 345239d787beSChao Yu 34530916878dSDamien Le Moal if (!f2fs_is_multi_device(sbi)) 345439d787beSChao Yu return; 345539d787beSChao Yu 345639d787beSChao Yu devidx = f2fs_target_device_index(sbi, fio->new_blkaddr); 345739d787beSChao Yu 345839d787beSChao Yu /* update device state for fsync */ 34594d57b86dSChao Yu f2fs_set_dirty_device(sbi, fio->ino, devidx, FLUSH_INO); 34601228b482SChao Yu 34611228b482SChao Yu /* update device state for checkpoint */ 34621228b482SChao Yu if (!f2fs_test_bit(devidx, (char *)&sbi->dirty_device)) { 34631228b482SChao Yu spin_lock(&sbi->dev_lock); 34641228b482SChao Yu f2fs_set_bit(devidx, (char *)&sbi->dirty_device); 34651228b482SChao Yu spin_unlock(&sbi->dev_lock); 34661228b482SChao Yu } 346739d787beSChao Yu } 346839d787beSChao Yu 346905ca3632SJaegeuk Kim static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) 3470bfad7c2dSJaegeuk Kim { 347181377bd6SJaegeuk Kim int type = __get_segment_type(fio); 3472b0332a0fSChao Yu bool keep_order = (f2fs_lfs_mode(fio->sbi) && type == CURSEG_COLD_DATA); 3473bfad7c2dSJaegeuk Kim 3474107a805dSChao Yu if (keep_order) 3475107a805dSChao Yu down_read(&fio->sbi->io_order_lock); 34760a595ebaSJaegeuk Kim reallocate: 34774d57b86dSChao Yu f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, 3478093749e2SChao Yu &fio->new_blkaddr, sum, type, fio); 34796aa58d8aSChao Yu if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) 34806aa58d8aSChao Yu invalidate_mapping_pages(META_MAPPING(fio->sbi), 34816aa58d8aSChao Yu fio->old_blkaddr, fio->old_blkaddr); 3482bfad7c2dSJaegeuk Kim 3483351df4b2SJaegeuk Kim /* writeout dirty page into bdev */ 3484fe16efe6SChao Yu f2fs_submit_page_write(fio); 3485fe16efe6SChao Yu if (fio->retry) { 34860a595ebaSJaegeuk Kim fio->old_blkaddr = fio->new_blkaddr; 34870a595ebaSJaegeuk Kim goto reallocate; 34880a595ebaSJaegeuk Kim } 3489fe16efe6SChao Yu 3490fe16efe6SChao Yu update_device_state(fio); 3491fe16efe6SChao Yu 3492107a805dSChao Yu if (keep_order) 3493107a805dSChao Yu up_read(&fio->sbi->io_order_lock); 3494351df4b2SJaegeuk Kim } 3495351df4b2SJaegeuk Kim 34964d57b86dSChao Yu void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page, 3497b0af6d49SChao Yu enum iostat_type io_type) 3498351df4b2SJaegeuk Kim { 3499458e6197SJaegeuk Kim struct f2fs_io_info fio = { 350005ca3632SJaegeuk Kim .sbi = sbi, 3501458e6197SJaegeuk Kim .type = META, 35020cdd3195SHyunchul Lee .temp = HOT, 350304d328deSMike Christie .op = REQ_OP_WRITE, 350470fd7614SChristoph Hellwig .op_flags = REQ_SYNC | REQ_META | REQ_PRIO, 35057a9d7548SChao Yu .old_blkaddr = page->index, 35067a9d7548SChao Yu .new_blkaddr = page->index, 350705ca3632SJaegeuk Kim .page = page, 35084375a336SJaegeuk Kim .encrypted_page = NULL, 3509fb830fc5SChao Yu .in_list = false, 3510458e6197SJaegeuk Kim }; 3511458e6197SJaegeuk Kim 35122b947003SChao Yu if (unlikely(page->index >= MAIN_BLKADDR(sbi))) 351304d328deSMike Christie fio.op_flags &= ~REQ_META; 35142b947003SChao Yu 3515351df4b2SJaegeuk Kim set_page_writeback(page); 351617c50035SJaegeuk Kim ClearPageError(page); 3517b9109b0eSJaegeuk Kim f2fs_submit_page_write(&fio); 3518b0af6d49SChao Yu 3519b63e7be5SChao Yu stat_inc_meta_count(sbi, page->index); 3520b0af6d49SChao Yu f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE); 3521351df4b2SJaegeuk Kim } 3522351df4b2SJaegeuk Kim 35234d57b86dSChao Yu void f2fs_do_write_node_page(unsigned int nid, struct f2fs_io_info *fio) 3524351df4b2SJaegeuk Kim { 3525351df4b2SJaegeuk Kim struct f2fs_summary sum; 352605ca3632SJaegeuk Kim 3527351df4b2SJaegeuk Kim set_summary(&sum, nid, 0, 0); 352805ca3632SJaegeuk Kim do_write_page(&sum, fio); 3529b0af6d49SChao Yu 3530b0af6d49SChao Yu f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); 3531351df4b2SJaegeuk Kim } 3532351df4b2SJaegeuk Kim 35334d57b86dSChao Yu void f2fs_outplace_write_data(struct dnode_of_data *dn, 35344d57b86dSChao Yu struct f2fs_io_info *fio) 3535351df4b2SJaegeuk Kim { 353605ca3632SJaegeuk Kim struct f2fs_sb_info *sbi = fio->sbi; 3537351df4b2SJaegeuk Kim struct f2fs_summary sum; 3538351df4b2SJaegeuk Kim 35399850cf4aSJaegeuk Kim f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR); 35407735730dSChao Yu set_summary(&sum, dn->nid, dn->ofs_in_node, fio->version); 354105ca3632SJaegeuk Kim do_write_page(&sum, fio); 3542f28b3434SChao Yu f2fs_update_data_blkaddr(dn, fio->new_blkaddr); 3543b0af6d49SChao Yu 3544b0af6d49SChao Yu f2fs_update_iostat(sbi, fio->io_type, F2FS_BLKSIZE); 3545351df4b2SJaegeuk Kim } 3546351df4b2SJaegeuk Kim 35474d57b86dSChao Yu int f2fs_inplace_write_data(struct f2fs_io_info *fio) 3548351df4b2SJaegeuk Kim { 3549b0af6d49SChao Yu int err; 3550d21b0f23SYunlei He struct f2fs_sb_info *sbi = fio->sbi; 355105573d6cSChao Yu unsigned int segno; 3552b0af6d49SChao Yu 35537a9d7548SChao Yu fio->new_blkaddr = fio->old_blkaddr; 35540cdd3195SHyunchul Lee /* i/o temperature is needed for passing down write hints */ 35550cdd3195SHyunchul Lee __get_segment_type(fio); 3556d21b0f23SYunlei He 355705573d6cSChao Yu segno = GET_SEGNO(sbi, fio->new_blkaddr); 355805573d6cSChao Yu 355905573d6cSChao Yu if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) { 356005573d6cSChao Yu set_sbi_flag(sbi, SBI_NEED_FSCK); 35612d821c12SChao Yu f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.", 35622d821c12SChao Yu __func__, segno); 356310f966bbSChao Yu return -EFSCORRUPTED; 356405573d6cSChao Yu } 3565d21b0f23SYunlei He 356605ca3632SJaegeuk Kim stat_inc_inplace_blocks(fio->sbi); 3567b0af6d49SChao Yu 35680e7f4197SJaegeuk Kim if (fio->bio && !(SM_I(sbi)->ipu_policy & (1 << F2FS_IPU_NOCACHE))) 35698648de2cSChao Yu err = f2fs_merge_page_bio(fio); 35708648de2cSChao Yu else 3571b0af6d49SChao Yu err = f2fs_submit_page_bio(fio); 3572e46f6bd8SChao Yu if (!err) { 357339d787beSChao Yu update_device_state(fio); 3574b0af6d49SChao Yu f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); 3575e46f6bd8SChao Yu } 3576b0af6d49SChao Yu 3577b0af6d49SChao Yu return err; 3578351df4b2SJaegeuk Kim } 3579351df4b2SJaegeuk Kim 35802b60311dSChao Yu static inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi, 35812b60311dSChao Yu unsigned int segno) 35822b60311dSChao Yu { 35832b60311dSChao Yu int i; 35842b60311dSChao Yu 35852b60311dSChao Yu for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) { 35862b60311dSChao Yu if (CURSEG_I(sbi, i)->segno == segno) 35872b60311dSChao Yu break; 35882b60311dSChao Yu } 35892b60311dSChao Yu return i; 35902b60311dSChao Yu } 35912b60311dSChao Yu 35924d57b86dSChao Yu void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, 359319f106bcSChao Yu block_t old_blkaddr, block_t new_blkaddr, 3594c5d02785SChao Yu bool recover_curseg, bool recover_newaddr, 3595c5d02785SChao Yu bool from_gc) 3596351df4b2SJaegeuk Kim { 3597351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 3598351df4b2SJaegeuk Kim struct curseg_info *curseg; 3599351df4b2SJaegeuk Kim unsigned int segno, old_cursegno; 3600351df4b2SJaegeuk Kim struct seg_entry *se; 3601351df4b2SJaegeuk Kim int type; 360219f106bcSChao Yu unsigned short old_blkoff; 3603753a8ed0SWang Xiaojun unsigned char old_alloc_type; 3604351df4b2SJaegeuk Kim 3605351df4b2SJaegeuk Kim segno = GET_SEGNO(sbi, new_blkaddr); 3606351df4b2SJaegeuk Kim se = get_seg_entry(sbi, segno); 3607351df4b2SJaegeuk Kim type = se->type; 3608351df4b2SJaegeuk Kim 36092b60311dSChao Yu down_write(&SM_I(sbi)->curseg_lock); 36102b60311dSChao Yu 361119f106bcSChao Yu if (!recover_curseg) { 361219f106bcSChao Yu /* for recovery flow */ 3613351df4b2SJaegeuk Kim if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { 3614351df4b2SJaegeuk Kim if (old_blkaddr == NULL_ADDR) 3615351df4b2SJaegeuk Kim type = CURSEG_COLD_DATA; 3616351df4b2SJaegeuk Kim else 3617351df4b2SJaegeuk Kim type = CURSEG_WARM_DATA; 3618351df4b2SJaegeuk Kim } 361919f106bcSChao Yu } else { 36202b60311dSChao Yu if (IS_CURSEG(sbi, segno)) { 36212b60311dSChao Yu /* se->type is volatile as SSR allocation */ 36222b60311dSChao Yu type = __f2fs_get_curseg(sbi, segno); 36232b60311dSChao Yu f2fs_bug_on(sbi, type == NO_CHECK_TYPE); 36242b60311dSChao Yu } else { 362519f106bcSChao Yu type = CURSEG_WARM_DATA; 362619f106bcSChao Yu } 36272b60311dSChao Yu } 362819f106bcSChao Yu 36292c190504SYunlong Song f2fs_bug_on(sbi, !IS_DATASEG(type)); 3630351df4b2SJaegeuk Kim curseg = CURSEG_I(sbi, type); 3631351df4b2SJaegeuk Kim 3632351df4b2SJaegeuk Kim mutex_lock(&curseg->curseg_mutex); 36333d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 3634351df4b2SJaegeuk Kim 3635351df4b2SJaegeuk Kim old_cursegno = curseg->segno; 363619f106bcSChao Yu old_blkoff = curseg->next_blkoff; 3637753a8ed0SWang Xiaojun old_alloc_type = curseg->alloc_type; 3638351df4b2SJaegeuk Kim 3639351df4b2SJaegeuk Kim /* change the current segment */ 3640351df4b2SJaegeuk Kim if (segno != curseg->segno) { 3641351df4b2SJaegeuk Kim curseg->next_segno = segno; 3642093749e2SChao Yu change_curseg(sbi, type, true); 3643351df4b2SJaegeuk Kim } 3644351df4b2SJaegeuk Kim 3645491c0854SJaegeuk Kim curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); 3646e79efe3bSHaicheng Li __add_sum_entry(sbi, type, sum); 3647351df4b2SJaegeuk Kim 3648c5d02785SChao Yu if (!recover_curseg || recover_newaddr) { 3649c5d02785SChao Yu if (!from_gc) 3650c5d02785SChao Yu update_segment_mtime(sbi, new_blkaddr, 0); 36516e2c64adSJaegeuk Kim update_sit_entry(sbi, new_blkaddr, 1); 3652c5d02785SChao Yu } 36536aa58d8aSChao Yu if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) { 36546aa58d8aSChao Yu invalidate_mapping_pages(META_MAPPING(sbi), 36556aa58d8aSChao Yu old_blkaddr, old_blkaddr); 3656c5d02785SChao Yu if (!from_gc) 3657c5d02785SChao Yu update_segment_mtime(sbi, old_blkaddr, 0); 36586e2c64adSJaegeuk Kim update_sit_entry(sbi, old_blkaddr, -1); 36596aa58d8aSChao Yu } 36606e2c64adSJaegeuk Kim 36616e2c64adSJaegeuk Kim locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); 36626e2c64adSJaegeuk Kim locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr)); 36636e2c64adSJaegeuk Kim 3664351df4b2SJaegeuk Kim locate_dirty_segment(sbi, old_cursegno); 3665351df4b2SJaegeuk Kim 366619f106bcSChao Yu if (recover_curseg) { 366719f106bcSChao Yu if (old_cursegno != curseg->segno) { 366819f106bcSChao Yu curseg->next_segno = old_cursegno; 3669093749e2SChao Yu change_curseg(sbi, type, true); 367019f106bcSChao Yu } 367119f106bcSChao Yu curseg->next_blkoff = old_blkoff; 3672753a8ed0SWang Xiaojun curseg->alloc_type = old_alloc_type; 367319f106bcSChao Yu } 367419f106bcSChao Yu 36753d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 3676351df4b2SJaegeuk Kim mutex_unlock(&curseg->curseg_mutex); 36772b60311dSChao Yu up_write(&SM_I(sbi)->curseg_lock); 3678351df4b2SJaegeuk Kim } 3679351df4b2SJaegeuk Kim 3680528e3459SChao Yu void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, 3681528e3459SChao Yu block_t old_addr, block_t new_addr, 368228bc106bSChao Yu unsigned char version, bool recover_curseg, 368328bc106bSChao Yu bool recover_newaddr) 3684528e3459SChao Yu { 3685528e3459SChao Yu struct f2fs_summary sum; 3686528e3459SChao Yu 3687528e3459SChao Yu set_summary(&sum, dn->nid, dn->ofs_in_node, version); 3688528e3459SChao Yu 36894d57b86dSChao Yu f2fs_do_replace_block(sbi, &sum, old_addr, new_addr, 3690c5d02785SChao Yu recover_curseg, recover_newaddr, false); 3691528e3459SChao Yu 3692f28b3434SChao Yu f2fs_update_data_blkaddr(dn, new_addr); 3693528e3459SChao Yu } 3694528e3459SChao Yu 369593dfe2acSJaegeuk Kim void f2fs_wait_on_page_writeback(struct page *page, 3696bae0ee7aSChao Yu enum page_type type, bool ordered, bool locked) 369793dfe2acSJaegeuk Kim { 369893dfe2acSJaegeuk Kim if (PageWriteback(page)) { 36994081363fSJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_P_SB(page); 37004081363fSJaegeuk Kim 37010b20fcecSChao Yu /* submit cached LFS IO */ 3702bab475c5SChao Yu f2fs_submit_merged_write_cond(sbi, NULL, page, 0, type); 37030b20fcecSChao Yu /* sbumit cached IPU IO */ 37040b20fcecSChao Yu f2fs_submit_merged_ipu_write(sbi, NULL, page); 3705bae0ee7aSChao Yu if (ordered) { 370693dfe2acSJaegeuk Kim wait_on_page_writeback(page); 3707bae0ee7aSChao Yu f2fs_bug_on(sbi, locked && PageWriteback(page)); 3708bae0ee7aSChao Yu } else { 3709fec1d657SJaegeuk Kim wait_for_stable_page(page); 371093dfe2acSJaegeuk Kim } 371193dfe2acSJaegeuk Kim } 3712bae0ee7aSChao Yu } 371393dfe2acSJaegeuk Kim 37140ded69f6SJaegeuk Kim void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr) 371508b39fbdSChao Yu { 37160ded69f6SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 371708b39fbdSChao Yu struct page *cpage; 371808b39fbdSChao Yu 37190ded69f6SJaegeuk Kim if (!f2fs_post_read_required(inode)) 37200ded69f6SJaegeuk Kim return; 37210ded69f6SJaegeuk Kim 372293770ab7SChao Yu if (!__is_valid_data_blkaddr(blkaddr)) 372308b39fbdSChao Yu return; 372408b39fbdSChao Yu 372508b39fbdSChao Yu cpage = find_lock_page(META_MAPPING(sbi), blkaddr); 372608b39fbdSChao Yu if (cpage) { 3727bae0ee7aSChao Yu f2fs_wait_on_page_writeback(cpage, DATA, true, true); 372808b39fbdSChao Yu f2fs_put_page(cpage, 1); 372908b39fbdSChao Yu } 373008b39fbdSChao Yu } 373108b39fbdSChao Yu 37321e78e8bdSSahitya Tummala void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, 37331e78e8bdSSahitya Tummala block_t len) 37341e78e8bdSSahitya Tummala { 37351e78e8bdSSahitya Tummala block_t i; 37361e78e8bdSSahitya Tummala 37371e78e8bdSSahitya Tummala for (i = 0; i < len; i++) 37381e78e8bdSSahitya Tummala f2fs_wait_on_block_writeback(inode, blkaddr + i); 37391e78e8bdSSahitya Tummala } 37401e78e8bdSSahitya Tummala 37417735730dSChao Yu static int read_compacted_summaries(struct f2fs_sb_info *sbi) 3742351df4b2SJaegeuk Kim { 3743351df4b2SJaegeuk Kim struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 3744351df4b2SJaegeuk Kim struct curseg_info *seg_i; 3745351df4b2SJaegeuk Kim unsigned char *kaddr; 3746351df4b2SJaegeuk Kim struct page *page; 3747351df4b2SJaegeuk Kim block_t start; 3748351df4b2SJaegeuk Kim int i, j, offset; 3749351df4b2SJaegeuk Kim 3750351df4b2SJaegeuk Kim start = start_sum_block(sbi); 3751351df4b2SJaegeuk Kim 37524d57b86dSChao Yu page = f2fs_get_meta_page(sbi, start++); 37537735730dSChao Yu if (IS_ERR(page)) 37547735730dSChao Yu return PTR_ERR(page); 3755351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 3756351df4b2SJaegeuk Kim 3757351df4b2SJaegeuk Kim /* Step 1: restore nat cache */ 3758351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); 3759b7ad7512SChao Yu memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE); 3760351df4b2SJaegeuk Kim 3761351df4b2SJaegeuk Kim /* Step 2: restore sit cache */ 3762351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); 3763b7ad7512SChao Yu memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); 3764351df4b2SJaegeuk Kim offset = 2 * SUM_JOURNAL_SIZE; 3765351df4b2SJaegeuk Kim 3766351df4b2SJaegeuk Kim /* Step 3: restore summary entries */ 3767351df4b2SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 3768351df4b2SJaegeuk Kim unsigned short blk_off; 3769351df4b2SJaegeuk Kim unsigned int segno; 3770351df4b2SJaegeuk Kim 3771351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, i); 3772351df4b2SJaegeuk Kim segno = le32_to_cpu(ckpt->cur_data_segno[i]); 3773351df4b2SJaegeuk Kim blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); 3774351df4b2SJaegeuk Kim seg_i->next_segno = segno; 3775351df4b2SJaegeuk Kim reset_curseg(sbi, i, 0); 3776351df4b2SJaegeuk Kim seg_i->alloc_type = ckpt->alloc_type[i]; 3777351df4b2SJaegeuk Kim seg_i->next_blkoff = blk_off; 3778351df4b2SJaegeuk Kim 3779351df4b2SJaegeuk Kim if (seg_i->alloc_type == SSR) 3780351df4b2SJaegeuk Kim blk_off = sbi->blocks_per_seg; 3781351df4b2SJaegeuk Kim 3782351df4b2SJaegeuk Kim for (j = 0; j < blk_off; j++) { 3783351df4b2SJaegeuk Kim struct f2fs_summary *s; 3784*5f029c04SYi Zhuang 3785351df4b2SJaegeuk Kim s = (struct f2fs_summary *)(kaddr + offset); 3786351df4b2SJaegeuk Kim seg_i->sum_blk->entries[j] = *s; 3787351df4b2SJaegeuk Kim offset += SUMMARY_SIZE; 378809cbfeafSKirill A. Shutemov if (offset + SUMMARY_SIZE <= PAGE_SIZE - 3789351df4b2SJaegeuk Kim SUM_FOOTER_SIZE) 3790351df4b2SJaegeuk Kim continue; 3791351df4b2SJaegeuk Kim 3792351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 3793351df4b2SJaegeuk Kim page = NULL; 3794351df4b2SJaegeuk Kim 37954d57b86dSChao Yu page = f2fs_get_meta_page(sbi, start++); 37967735730dSChao Yu if (IS_ERR(page)) 37977735730dSChao Yu return PTR_ERR(page); 3798351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 3799351df4b2SJaegeuk Kim offset = 0; 3800351df4b2SJaegeuk Kim } 3801351df4b2SJaegeuk Kim } 3802351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 38037735730dSChao Yu return 0; 3804351df4b2SJaegeuk Kim } 3805351df4b2SJaegeuk Kim 3806351df4b2SJaegeuk Kim static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) 3807351df4b2SJaegeuk Kim { 3808351df4b2SJaegeuk Kim struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 3809351df4b2SJaegeuk Kim struct f2fs_summary_block *sum; 3810351df4b2SJaegeuk Kim struct curseg_info *curseg; 3811351df4b2SJaegeuk Kim struct page *new; 3812351df4b2SJaegeuk Kim unsigned short blk_off; 3813351df4b2SJaegeuk Kim unsigned int segno = 0; 3814351df4b2SJaegeuk Kim block_t blk_addr = 0; 38157735730dSChao Yu int err = 0; 3816351df4b2SJaegeuk Kim 3817351df4b2SJaegeuk Kim /* get segment number and block addr */ 3818351df4b2SJaegeuk Kim if (IS_DATASEG(type)) { 3819351df4b2SJaegeuk Kim segno = le32_to_cpu(ckpt->cur_data_segno[type]); 3820351df4b2SJaegeuk Kim blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - 3821351df4b2SJaegeuk Kim CURSEG_HOT_DATA]); 3822119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) 3823d0b9e42aSChao Yu blk_addr = sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type); 3824351df4b2SJaegeuk Kim else 3825351df4b2SJaegeuk Kim blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); 3826351df4b2SJaegeuk Kim } else { 3827351df4b2SJaegeuk Kim segno = le32_to_cpu(ckpt->cur_node_segno[type - 3828351df4b2SJaegeuk Kim CURSEG_HOT_NODE]); 3829351df4b2SJaegeuk Kim blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - 3830351df4b2SJaegeuk Kim CURSEG_HOT_NODE]); 3831119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) 3832351df4b2SJaegeuk Kim blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, 3833351df4b2SJaegeuk Kim type - CURSEG_HOT_NODE); 3834351df4b2SJaegeuk Kim else 3835351df4b2SJaegeuk Kim blk_addr = GET_SUM_BLOCK(sbi, segno); 3836351df4b2SJaegeuk Kim } 3837351df4b2SJaegeuk Kim 38384d57b86dSChao Yu new = f2fs_get_meta_page(sbi, blk_addr); 38397735730dSChao Yu if (IS_ERR(new)) 38407735730dSChao Yu return PTR_ERR(new); 3841351df4b2SJaegeuk Kim sum = (struct f2fs_summary_block *)page_address(new); 3842351df4b2SJaegeuk Kim 3843351df4b2SJaegeuk Kim if (IS_NODESEG(type)) { 3844119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) { 3845351df4b2SJaegeuk Kim struct f2fs_summary *ns = &sum->entries[0]; 3846351df4b2SJaegeuk Kim int i; 3847*5f029c04SYi Zhuang 3848351df4b2SJaegeuk Kim for (i = 0; i < sbi->blocks_per_seg; i++, ns++) { 3849351df4b2SJaegeuk Kim ns->version = 0; 3850351df4b2SJaegeuk Kim ns->ofs_in_node = 0; 3851351df4b2SJaegeuk Kim } 3852351df4b2SJaegeuk Kim } else { 38537735730dSChao Yu err = f2fs_restore_node_summary(sbi, segno, sum); 38547735730dSChao Yu if (err) 38557735730dSChao Yu goto out; 3856351df4b2SJaegeuk Kim } 3857351df4b2SJaegeuk Kim } 3858351df4b2SJaegeuk Kim 3859351df4b2SJaegeuk Kim /* set uncompleted segment to curseg */ 3860351df4b2SJaegeuk Kim curseg = CURSEG_I(sbi, type); 3861351df4b2SJaegeuk Kim mutex_lock(&curseg->curseg_mutex); 3862b7ad7512SChao Yu 3863b7ad7512SChao Yu /* update journal info */ 3864b7ad7512SChao Yu down_write(&curseg->journal_rwsem); 3865b7ad7512SChao Yu memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE); 3866b7ad7512SChao Yu up_write(&curseg->journal_rwsem); 3867b7ad7512SChao Yu 3868b7ad7512SChao Yu memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE); 3869b7ad7512SChao Yu memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE); 3870351df4b2SJaegeuk Kim curseg->next_segno = segno; 3871351df4b2SJaegeuk Kim reset_curseg(sbi, type, 0); 3872351df4b2SJaegeuk Kim curseg->alloc_type = ckpt->alloc_type[type]; 3873351df4b2SJaegeuk Kim curseg->next_blkoff = blk_off; 3874351df4b2SJaegeuk Kim mutex_unlock(&curseg->curseg_mutex); 38757735730dSChao Yu out: 3876351df4b2SJaegeuk Kim f2fs_put_page(new, 1); 38777735730dSChao Yu return err; 3878351df4b2SJaegeuk Kim } 3879351df4b2SJaegeuk Kim 3880351df4b2SJaegeuk Kim static int restore_curseg_summaries(struct f2fs_sb_info *sbi) 3881351df4b2SJaegeuk Kim { 388221d3f8e1SJin Qian struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal; 388321d3f8e1SJin Qian struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal; 3884351df4b2SJaegeuk Kim int type = CURSEG_HOT_DATA; 3885e4fc5fbfSChao Yu int err; 3886351df4b2SJaegeuk Kim 3887aaec2b1dSChao Yu if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) { 38884d57b86dSChao Yu int npages = f2fs_npages_for_summary_flush(sbi, true); 38893fa06d7bSChao Yu 38903fa06d7bSChao Yu if (npages >= 2) 38914d57b86dSChao Yu f2fs_ra_meta_pages(sbi, start_sum_block(sbi), npages, 389226879fb1SChao Yu META_CP, true); 38933fa06d7bSChao Yu 3894351df4b2SJaegeuk Kim /* restore for compacted data summary */ 38957735730dSChao Yu err = read_compacted_summaries(sbi); 38967735730dSChao Yu if (err) 38977735730dSChao Yu return err; 3898351df4b2SJaegeuk Kim type = CURSEG_HOT_NODE; 3899351df4b2SJaegeuk Kim } 3900351df4b2SJaegeuk Kim 3901119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) 3902d0b9e42aSChao Yu f2fs_ra_meta_pages(sbi, 3903d0b9e42aSChao Yu sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type), 3904d0b9e42aSChao Yu NR_CURSEG_PERSIST_TYPE - type, META_CP, true); 39053fa06d7bSChao Yu 3906e4fc5fbfSChao Yu for (; type <= CURSEG_COLD_NODE; type++) { 3907e4fc5fbfSChao Yu err = read_normal_summaries(sbi, type); 3908e4fc5fbfSChao Yu if (err) 3909e4fc5fbfSChao Yu return err; 3910e4fc5fbfSChao Yu } 3911e4fc5fbfSChao Yu 391221d3f8e1SJin Qian /* sanity check for summary blocks */ 391321d3f8e1SJin Qian if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || 39149227d522SSahitya Tummala sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) { 3915dcbb4c10SJoe Perches f2fs_err(sbi, "invalid journal entries nats %u sits %u\n", 39169227d522SSahitya Tummala nats_in_cursum(nat_j), sits_in_cursum(sit_j)); 391721d3f8e1SJin Qian return -EINVAL; 39189227d522SSahitya Tummala } 391921d3f8e1SJin Qian 3920351df4b2SJaegeuk Kim return 0; 3921351df4b2SJaegeuk Kim } 3922351df4b2SJaegeuk Kim 3923351df4b2SJaegeuk Kim static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) 3924351df4b2SJaegeuk Kim { 3925351df4b2SJaegeuk Kim struct page *page; 3926351df4b2SJaegeuk Kim unsigned char *kaddr; 3927351df4b2SJaegeuk Kim struct f2fs_summary *summary; 3928351df4b2SJaegeuk Kim struct curseg_info *seg_i; 3929351df4b2SJaegeuk Kim int written_size = 0; 3930351df4b2SJaegeuk Kim int i, j; 3931351df4b2SJaegeuk Kim 39324d57b86dSChao Yu page = f2fs_grab_meta_page(sbi, blkaddr++); 3933351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 393481114baaSChao Yu memset(kaddr, 0, PAGE_SIZE); 3935351df4b2SJaegeuk Kim 3936351df4b2SJaegeuk Kim /* Step 1: write nat cache */ 3937351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); 3938b7ad7512SChao Yu memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE); 3939351df4b2SJaegeuk Kim written_size += SUM_JOURNAL_SIZE; 3940351df4b2SJaegeuk Kim 3941351df4b2SJaegeuk Kim /* Step 2: write sit cache */ 3942351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); 3943b7ad7512SChao Yu memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE); 3944351df4b2SJaegeuk Kim written_size += SUM_JOURNAL_SIZE; 3945351df4b2SJaegeuk Kim 3946351df4b2SJaegeuk Kim /* Step 3: write summary entries */ 3947351df4b2SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 3948351df4b2SJaegeuk Kim unsigned short blkoff; 3949*5f029c04SYi Zhuang 3950351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, i); 3951351df4b2SJaegeuk Kim if (sbi->ckpt->alloc_type[i] == SSR) 3952351df4b2SJaegeuk Kim blkoff = sbi->blocks_per_seg; 3953351df4b2SJaegeuk Kim else 3954351df4b2SJaegeuk Kim blkoff = curseg_blkoff(sbi, i); 3955351df4b2SJaegeuk Kim 3956351df4b2SJaegeuk Kim for (j = 0; j < blkoff; j++) { 3957351df4b2SJaegeuk Kim if (!page) { 39584d57b86dSChao Yu page = f2fs_grab_meta_page(sbi, blkaddr++); 3959351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 396081114baaSChao Yu memset(kaddr, 0, PAGE_SIZE); 3961351df4b2SJaegeuk Kim written_size = 0; 3962351df4b2SJaegeuk Kim } 3963351df4b2SJaegeuk Kim summary = (struct f2fs_summary *)(kaddr + written_size); 3964351df4b2SJaegeuk Kim *summary = seg_i->sum_blk->entries[j]; 3965351df4b2SJaegeuk Kim written_size += SUMMARY_SIZE; 3966351df4b2SJaegeuk Kim 396709cbfeafSKirill A. Shutemov if (written_size + SUMMARY_SIZE <= PAGE_SIZE - 3968351df4b2SJaegeuk Kim SUM_FOOTER_SIZE) 3969351df4b2SJaegeuk Kim continue; 3970351df4b2SJaegeuk Kim 3971e8d61a74SChao Yu set_page_dirty(page); 3972351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 3973351df4b2SJaegeuk Kim page = NULL; 3974351df4b2SJaegeuk Kim } 3975351df4b2SJaegeuk Kim } 3976e8d61a74SChao Yu if (page) { 3977e8d61a74SChao Yu set_page_dirty(page); 3978351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 3979351df4b2SJaegeuk Kim } 3980e8d61a74SChao Yu } 3981351df4b2SJaegeuk Kim 3982351df4b2SJaegeuk Kim static void write_normal_summaries(struct f2fs_sb_info *sbi, 3983351df4b2SJaegeuk Kim block_t blkaddr, int type) 3984351df4b2SJaegeuk Kim { 3985351df4b2SJaegeuk Kim int i, end; 3986*5f029c04SYi Zhuang 3987351df4b2SJaegeuk Kim if (IS_DATASEG(type)) 3988351df4b2SJaegeuk Kim end = type + NR_CURSEG_DATA_TYPE; 3989351df4b2SJaegeuk Kim else 3990351df4b2SJaegeuk Kim end = type + NR_CURSEG_NODE_TYPE; 3991351df4b2SJaegeuk Kim 3992b7ad7512SChao Yu for (i = type; i < end; i++) 3993b7ad7512SChao Yu write_current_sum_page(sbi, i, blkaddr + (i - type)); 3994351df4b2SJaegeuk Kim } 3995351df4b2SJaegeuk Kim 39964d57b86dSChao Yu void f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) 3997351df4b2SJaegeuk Kim { 3998aaec2b1dSChao Yu if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) 3999351df4b2SJaegeuk Kim write_compacted_summaries(sbi, start_blk); 4000351df4b2SJaegeuk Kim else 4001351df4b2SJaegeuk Kim write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); 4002351df4b2SJaegeuk Kim } 4003351df4b2SJaegeuk Kim 40044d57b86dSChao Yu void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) 4005351df4b2SJaegeuk Kim { 4006351df4b2SJaegeuk Kim write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); 4007351df4b2SJaegeuk Kim } 4008351df4b2SJaegeuk Kim 40094d57b86dSChao Yu int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, 4010351df4b2SJaegeuk Kim unsigned int val, int alloc) 4011351df4b2SJaegeuk Kim { 4012351df4b2SJaegeuk Kim int i; 4013351df4b2SJaegeuk Kim 4014351df4b2SJaegeuk Kim if (type == NAT_JOURNAL) { 4015dfc08a12SChao Yu for (i = 0; i < nats_in_cursum(journal); i++) { 4016dfc08a12SChao Yu if (le32_to_cpu(nid_in_journal(journal, i)) == val) 4017351df4b2SJaegeuk Kim return i; 4018351df4b2SJaegeuk Kim } 4019dfc08a12SChao Yu if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL)) 4020dfc08a12SChao Yu return update_nats_in_cursum(journal, 1); 4021351df4b2SJaegeuk Kim } else if (type == SIT_JOURNAL) { 4022dfc08a12SChao Yu for (i = 0; i < sits_in_cursum(journal); i++) 4023dfc08a12SChao Yu if (le32_to_cpu(segno_in_journal(journal, i)) == val) 4024351df4b2SJaegeuk Kim return i; 4025dfc08a12SChao Yu if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL)) 4026dfc08a12SChao Yu return update_sits_in_cursum(journal, 1); 4027351df4b2SJaegeuk Kim } 4028351df4b2SJaegeuk Kim return -1; 4029351df4b2SJaegeuk Kim } 4030351df4b2SJaegeuk Kim 4031351df4b2SJaegeuk Kim static struct page *get_current_sit_page(struct f2fs_sb_info *sbi, 4032351df4b2SJaegeuk Kim unsigned int segno) 4033351df4b2SJaegeuk Kim { 403486f33603SJaegeuk Kim return f2fs_get_meta_page(sbi, current_sit_addr(sbi, segno)); 4035351df4b2SJaegeuk Kim } 4036351df4b2SJaegeuk Kim 4037351df4b2SJaegeuk Kim static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, 4038351df4b2SJaegeuk Kim unsigned int start) 4039351df4b2SJaegeuk Kim { 4040351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 4041068c3cd8SYunlei He struct page *page; 4042351df4b2SJaegeuk Kim pgoff_t src_off, dst_off; 4043351df4b2SJaegeuk Kim 4044351df4b2SJaegeuk Kim src_off = current_sit_addr(sbi, start); 4045351df4b2SJaegeuk Kim dst_off = next_sit_addr(sbi, src_off); 4046351df4b2SJaegeuk Kim 40474d57b86dSChao Yu page = f2fs_grab_meta_page(sbi, dst_off); 4048068c3cd8SYunlei He seg_info_to_sit_page(sbi, page, start); 4049351df4b2SJaegeuk Kim 4050068c3cd8SYunlei He set_page_dirty(page); 4051351df4b2SJaegeuk Kim set_to_next_sit(sit_i, start); 4052351df4b2SJaegeuk Kim 4053068c3cd8SYunlei He return page; 4054351df4b2SJaegeuk Kim } 4055351df4b2SJaegeuk Kim 4056184a5cd2SChao Yu static struct sit_entry_set *grab_sit_entry_set(void) 4057184a5cd2SChao Yu { 4058184a5cd2SChao Yu struct sit_entry_set *ses = 405980c54505SJaegeuk Kim f2fs_kmem_cache_alloc(sit_entry_set_slab, GFP_NOFS); 4060184a5cd2SChao Yu 4061184a5cd2SChao Yu ses->entry_cnt = 0; 4062184a5cd2SChao Yu INIT_LIST_HEAD(&ses->set_list); 4063184a5cd2SChao Yu return ses; 4064184a5cd2SChao Yu } 4065184a5cd2SChao Yu 4066184a5cd2SChao Yu static void release_sit_entry_set(struct sit_entry_set *ses) 4067184a5cd2SChao Yu { 4068184a5cd2SChao Yu list_del(&ses->set_list); 4069184a5cd2SChao Yu kmem_cache_free(sit_entry_set_slab, ses); 4070184a5cd2SChao Yu } 4071184a5cd2SChao Yu 4072184a5cd2SChao Yu static void adjust_sit_entry_set(struct sit_entry_set *ses, 4073184a5cd2SChao Yu struct list_head *head) 4074184a5cd2SChao Yu { 4075184a5cd2SChao Yu struct sit_entry_set *next = ses; 4076184a5cd2SChao Yu 4077184a5cd2SChao Yu if (list_is_last(&ses->set_list, head)) 4078184a5cd2SChao Yu return; 4079184a5cd2SChao Yu 4080184a5cd2SChao Yu list_for_each_entry_continue(next, head, set_list) 4081184a5cd2SChao Yu if (ses->entry_cnt <= next->entry_cnt) 4082184a5cd2SChao Yu break; 4083184a5cd2SChao Yu 4084184a5cd2SChao Yu list_move_tail(&ses->set_list, &next->set_list); 4085184a5cd2SChao Yu } 4086184a5cd2SChao Yu 4087184a5cd2SChao Yu static void add_sit_entry(unsigned int segno, struct list_head *head) 4088184a5cd2SChao Yu { 4089184a5cd2SChao Yu struct sit_entry_set *ses; 4090184a5cd2SChao Yu unsigned int start_segno = START_SEGNO(segno); 4091184a5cd2SChao Yu 4092184a5cd2SChao Yu list_for_each_entry(ses, head, set_list) { 4093184a5cd2SChao Yu if (ses->start_segno == start_segno) { 4094184a5cd2SChao Yu ses->entry_cnt++; 4095184a5cd2SChao Yu adjust_sit_entry_set(ses, head); 4096184a5cd2SChao Yu return; 4097184a5cd2SChao Yu } 4098184a5cd2SChao Yu } 4099184a5cd2SChao Yu 4100184a5cd2SChao Yu ses = grab_sit_entry_set(); 4101184a5cd2SChao Yu 4102184a5cd2SChao Yu ses->start_segno = start_segno; 4103184a5cd2SChao Yu ses->entry_cnt++; 4104184a5cd2SChao Yu list_add(&ses->set_list, head); 4105184a5cd2SChao Yu } 4106184a5cd2SChao Yu 4107184a5cd2SChao Yu static void add_sits_in_set(struct f2fs_sb_info *sbi) 4108184a5cd2SChao Yu { 4109184a5cd2SChao Yu struct f2fs_sm_info *sm_info = SM_I(sbi); 4110184a5cd2SChao Yu struct list_head *set_list = &sm_info->sit_entry_set; 4111184a5cd2SChao Yu unsigned long *bitmap = SIT_I(sbi)->dirty_sentries_bitmap; 4112184a5cd2SChao Yu unsigned int segno; 4113184a5cd2SChao Yu 41147cd8558bSJaegeuk Kim for_each_set_bit(segno, bitmap, MAIN_SEGS(sbi)) 4115184a5cd2SChao Yu add_sit_entry(segno, set_list); 4116184a5cd2SChao Yu } 4117184a5cd2SChao Yu 4118184a5cd2SChao Yu static void remove_sits_in_journal(struct f2fs_sb_info *sbi) 4119351df4b2SJaegeuk Kim { 4120351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 4121b7ad7512SChao Yu struct f2fs_journal *journal = curseg->journal; 4122351df4b2SJaegeuk Kim int i; 4123351df4b2SJaegeuk Kim 4124b7ad7512SChao Yu down_write(&curseg->journal_rwsem); 4125dfc08a12SChao Yu for (i = 0; i < sits_in_cursum(journal); i++) { 4126351df4b2SJaegeuk Kim unsigned int segno; 4127184a5cd2SChao Yu bool dirtied; 4128184a5cd2SChao Yu 4129dfc08a12SChao Yu segno = le32_to_cpu(segno_in_journal(journal, i)); 4130184a5cd2SChao Yu dirtied = __mark_sit_entry_dirty(sbi, segno); 4131184a5cd2SChao Yu 4132184a5cd2SChao Yu if (!dirtied) 4133184a5cd2SChao Yu add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); 4134351df4b2SJaegeuk Kim } 4135dfc08a12SChao Yu update_sits_in_cursum(journal, -i); 4136b7ad7512SChao Yu up_write(&curseg->journal_rwsem); 4137351df4b2SJaegeuk Kim } 4138351df4b2SJaegeuk Kim 41390a8165d7SJaegeuk Kim /* 4140351df4b2SJaegeuk Kim * CP calls this function, which flushes SIT entries including sit_journal, 4141351df4b2SJaegeuk Kim * and moves prefree segs to free segs. 4142351df4b2SJaegeuk Kim */ 41434d57b86dSChao Yu void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) 4144351df4b2SJaegeuk Kim { 4145351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 4146351df4b2SJaegeuk Kim unsigned long *bitmap = sit_i->dirty_sentries_bitmap; 4147351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 4148b7ad7512SChao Yu struct f2fs_journal *journal = curseg->journal; 4149184a5cd2SChao Yu struct sit_entry_set *ses, *tmp; 4150184a5cd2SChao Yu struct list_head *head = &SM_I(sbi)->sit_entry_set; 415104f0b2eaSQiuyang Sun bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); 41524b2fecc8SJaegeuk Kim struct seg_entry *se; 4153351df4b2SJaegeuk Kim 41543d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 4155351df4b2SJaegeuk Kim 41562b11a74bSWanpeng Li if (!sit_i->dirty_sentries) 41572b11a74bSWanpeng Li goto out; 41582b11a74bSWanpeng Li 4159351df4b2SJaegeuk Kim /* 4160184a5cd2SChao Yu * add and account sit entries of dirty bitmap in sit entry 4161184a5cd2SChao Yu * set temporarily 4162351df4b2SJaegeuk Kim */ 4163184a5cd2SChao Yu add_sits_in_set(sbi); 4164351df4b2SJaegeuk Kim 4165184a5cd2SChao Yu /* 4166184a5cd2SChao Yu * if there are no enough space in journal to store dirty sit 4167184a5cd2SChao Yu * entries, remove all entries from journal and add and account 4168184a5cd2SChao Yu * them in sit entry set. 4169184a5cd2SChao Yu */ 417004f0b2eaSQiuyang Sun if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) || 417104f0b2eaSQiuyang Sun !to_journal) 4172184a5cd2SChao Yu remove_sits_in_journal(sbi); 4173184a5cd2SChao Yu 4174184a5cd2SChao Yu /* 4175184a5cd2SChao Yu * there are two steps to flush sit entries: 4176184a5cd2SChao Yu * #1, flush sit entries to journal in current cold data summary block. 4177184a5cd2SChao Yu * #2, flush sit entries to sit page. 4178184a5cd2SChao Yu */ 4179184a5cd2SChao Yu list_for_each_entry_safe(ses, tmp, head, set_list) { 41804a257ed6SJaegeuk Kim struct page *page = NULL; 4181184a5cd2SChao Yu struct f2fs_sit_block *raw_sit = NULL; 4182184a5cd2SChao Yu unsigned int start_segno = ses->start_segno; 4183184a5cd2SChao Yu unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, 41847cd8558bSJaegeuk Kim (unsigned long)MAIN_SEGS(sbi)); 4185184a5cd2SChao Yu unsigned int segno = start_segno; 4186184a5cd2SChao Yu 4187184a5cd2SChao Yu if (to_journal && 4188dfc08a12SChao Yu !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) 4189184a5cd2SChao Yu to_journal = false; 4190184a5cd2SChao Yu 4191b7ad7512SChao Yu if (to_journal) { 4192b7ad7512SChao Yu down_write(&curseg->journal_rwsem); 4193b7ad7512SChao Yu } else { 4194184a5cd2SChao Yu page = get_next_sit_page(sbi, start_segno); 4195184a5cd2SChao Yu raw_sit = page_address(page); 4196184a5cd2SChao Yu } 4197184a5cd2SChao Yu 4198184a5cd2SChao Yu /* flush dirty sit entries in region of current sit set */ 4199184a5cd2SChao Yu for_each_set_bit_from(segno, bitmap, end) { 4200184a5cd2SChao Yu int offset, sit_offset; 42014b2fecc8SJaegeuk Kim 42024b2fecc8SJaegeuk Kim se = get_seg_entry(sbi, segno); 420356b07e7eSZhikang Zhang #ifdef CONFIG_F2FS_CHECK_FS 420456b07e7eSZhikang Zhang if (memcmp(se->cur_valid_map, se->cur_valid_map_mir, 420556b07e7eSZhikang Zhang SIT_VBLOCK_MAP_SIZE)) 420656b07e7eSZhikang Zhang f2fs_bug_on(sbi, 1); 420756b07e7eSZhikang Zhang #endif 4208351df4b2SJaegeuk Kim 4209b2955550SJaegeuk Kim /* add discard candidates */ 4210c473f1a9SChao Yu if (!(cpc->reason & CP_DISCARD)) { 42114b2fecc8SJaegeuk Kim cpc->trim_start = segno; 421225290fa5SJaegeuk Kim add_discard_addrs(sbi, cpc, false); 42134b2fecc8SJaegeuk Kim } 4214b2955550SJaegeuk Kim 4215184a5cd2SChao Yu if (to_journal) { 42164d57b86dSChao Yu offset = f2fs_lookup_journal_in_cursum(journal, 4217184a5cd2SChao Yu SIT_JOURNAL, segno, 1); 4218184a5cd2SChao Yu f2fs_bug_on(sbi, offset < 0); 4219dfc08a12SChao Yu segno_in_journal(journal, offset) = 4220184a5cd2SChao Yu cpu_to_le32(segno); 4221184a5cd2SChao Yu seg_info_to_raw_sit(se, 4222dfc08a12SChao Yu &sit_in_journal(journal, offset)); 422356b07e7eSZhikang Zhang check_block_count(sbi, segno, 422456b07e7eSZhikang Zhang &sit_in_journal(journal, offset)); 4225184a5cd2SChao Yu } else { 4226184a5cd2SChao Yu sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); 4227184a5cd2SChao Yu seg_info_to_raw_sit(se, 4228184a5cd2SChao Yu &raw_sit->entries[sit_offset]); 422956b07e7eSZhikang Zhang check_block_count(sbi, segno, 423056b07e7eSZhikang Zhang &raw_sit->entries[sit_offset]); 4231351df4b2SJaegeuk Kim } 4232351df4b2SJaegeuk Kim 4233351df4b2SJaegeuk Kim __clear_bit(segno, bitmap); 4234351df4b2SJaegeuk Kim sit_i->dirty_sentries--; 4235184a5cd2SChao Yu ses->entry_cnt--; 4236351df4b2SJaegeuk Kim } 4237184a5cd2SChao Yu 4238b7ad7512SChao Yu if (to_journal) 4239b7ad7512SChao Yu up_write(&curseg->journal_rwsem); 4240b7ad7512SChao Yu else 4241184a5cd2SChao Yu f2fs_put_page(page, 1); 4242184a5cd2SChao Yu 4243184a5cd2SChao Yu f2fs_bug_on(sbi, ses->entry_cnt); 4244184a5cd2SChao Yu release_sit_entry_set(ses); 4245184a5cd2SChao Yu } 4246184a5cd2SChao Yu 4247184a5cd2SChao Yu f2fs_bug_on(sbi, !list_empty(head)); 4248184a5cd2SChao Yu f2fs_bug_on(sbi, sit_i->dirty_sentries); 4249184a5cd2SChao Yu out: 4250c473f1a9SChao Yu if (cpc->reason & CP_DISCARD) { 4251650d3c4eSYunlei He __u64 trim_start = cpc->trim_start; 4252650d3c4eSYunlei He 42534b2fecc8SJaegeuk Kim for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) 425425290fa5SJaegeuk Kim add_discard_addrs(sbi, cpc, false); 4255650d3c4eSYunlei He 4256650d3c4eSYunlei He cpc->trim_start = trim_start; 42574b2fecc8SJaegeuk Kim } 42583d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 4259351df4b2SJaegeuk Kim 4260351df4b2SJaegeuk Kim set_prefree_as_free_segments(sbi); 4261351df4b2SJaegeuk Kim } 4262351df4b2SJaegeuk Kim 4263351df4b2SJaegeuk Kim static int build_sit_info(struct f2fs_sb_info *sbi) 4264351df4b2SJaegeuk Kim { 4265351df4b2SJaegeuk Kim struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); 4266351df4b2SJaegeuk Kim struct sit_info *sit_i; 4267351df4b2SJaegeuk Kim unsigned int sit_segs, start; 42682fde3dd1SChao Yu char *src_bitmap, *bitmap; 4269bbf9f7d9SSahitya Tummala unsigned int bitmap_size, main_bitmap_size, sit_bitmap_size; 4270351df4b2SJaegeuk Kim 4271351df4b2SJaegeuk Kim /* allocate memory for SIT information */ 4272acbf054dSChao Yu sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); 4273351df4b2SJaegeuk Kim if (!sit_i) 4274351df4b2SJaegeuk Kim return -ENOMEM; 4275351df4b2SJaegeuk Kim 4276351df4b2SJaegeuk Kim SM_I(sbi)->sit_info = sit_i; 4277351df4b2SJaegeuk Kim 42789d2a789cSKees Cook sit_i->sentries = 42799d2a789cSKees Cook f2fs_kvzalloc(sbi, array_size(sizeof(struct seg_entry), 42809d2a789cSKees Cook MAIN_SEGS(sbi)), 42819d2a789cSKees Cook GFP_KERNEL); 4282351df4b2SJaegeuk Kim if (!sit_i->sentries) 4283351df4b2SJaegeuk Kim return -ENOMEM; 4284351df4b2SJaegeuk Kim 4285bbf9f7d9SSahitya Tummala main_bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 4286bbf9f7d9SSahitya Tummala sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, main_bitmap_size, 4287628b3d14SChao Yu GFP_KERNEL); 4288351df4b2SJaegeuk Kim if (!sit_i->dirty_sentries_bitmap) 4289351df4b2SJaegeuk Kim return -ENOMEM; 4290351df4b2SJaegeuk Kim 42912fde3dd1SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 42922fde3dd1SChao Yu bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * 4; 42932fde3dd1SChao Yu #else 42942fde3dd1SChao Yu bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * 3; 42952fde3dd1SChao Yu #endif 42962fde3dd1SChao Yu sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 42972fde3dd1SChao Yu if (!sit_i->bitmap) 42983e025740SJaegeuk Kim return -ENOMEM; 42993e025740SJaegeuk Kim 43002fde3dd1SChao Yu bitmap = sit_i->bitmap; 43012fde3dd1SChao Yu 43022fde3dd1SChao Yu for (start = 0; start < MAIN_SEGS(sbi); start++) { 43032fde3dd1SChao Yu sit_i->sentries[start].cur_valid_map = bitmap; 43042fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 43052fde3dd1SChao Yu 43062fde3dd1SChao Yu sit_i->sentries[start].ckpt_valid_map = bitmap; 43072fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 43082fde3dd1SChao Yu 4309355e7891SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 43102fde3dd1SChao Yu sit_i->sentries[start].cur_valid_map_mir = bitmap; 43112fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 4312355e7891SChao Yu #endif 4313355e7891SChao Yu 43142fde3dd1SChao Yu sit_i->sentries[start].discard_map = bitmap; 43152fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 4316351df4b2SJaegeuk Kim } 4317351df4b2SJaegeuk Kim 4318acbf054dSChao Yu sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); 431960a3b782SJaegeuk Kim if (!sit_i->tmp_map) 432060a3b782SJaegeuk Kim return -ENOMEM; 432160a3b782SJaegeuk Kim 43222c70c5e3SChao Yu if (__is_large_section(sbi)) { 43239d2a789cSKees Cook sit_i->sec_entries = 43249d2a789cSKees Cook f2fs_kvzalloc(sbi, array_size(sizeof(struct sec_entry), 43259d2a789cSKees Cook MAIN_SECS(sbi)), 43269d2a789cSKees Cook GFP_KERNEL); 4327351df4b2SJaegeuk Kim if (!sit_i->sec_entries) 4328351df4b2SJaegeuk Kim return -ENOMEM; 4329351df4b2SJaegeuk Kim } 4330351df4b2SJaegeuk Kim 4331351df4b2SJaegeuk Kim /* get information related with SIT */ 4332351df4b2SJaegeuk Kim sit_segs = le32_to_cpu(raw_super->segment_count_sit) >> 1; 4333351df4b2SJaegeuk Kim 4334351df4b2SJaegeuk Kim /* setup SIT bitmap from ckeckpoint pack */ 4335bbf9f7d9SSahitya Tummala sit_bitmap_size = __bitmap_size(sbi, SIT_BITMAP); 4336351df4b2SJaegeuk Kim src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); 4337351df4b2SJaegeuk Kim 4338bbf9f7d9SSahitya Tummala sit_i->sit_bitmap = kmemdup(src_bitmap, sit_bitmap_size, GFP_KERNEL); 4339ae27d62eSChao Yu if (!sit_i->sit_bitmap) 4340351df4b2SJaegeuk Kim return -ENOMEM; 4341351df4b2SJaegeuk Kim 4342ae27d62eSChao Yu #ifdef CONFIG_F2FS_CHECK_FS 4343bbf9f7d9SSahitya Tummala sit_i->sit_bitmap_mir = kmemdup(src_bitmap, 4344bbf9f7d9SSahitya Tummala sit_bitmap_size, GFP_KERNEL); 4345ae27d62eSChao Yu if (!sit_i->sit_bitmap_mir) 4346ae27d62eSChao Yu return -ENOMEM; 4347bbf9f7d9SSahitya Tummala 4348bbf9f7d9SSahitya Tummala sit_i->invalid_segmap = f2fs_kvzalloc(sbi, 4349bbf9f7d9SSahitya Tummala main_bitmap_size, GFP_KERNEL); 4350bbf9f7d9SSahitya Tummala if (!sit_i->invalid_segmap) 4351bbf9f7d9SSahitya Tummala return -ENOMEM; 4352ae27d62eSChao Yu #endif 4353ae27d62eSChao Yu 4354351df4b2SJaegeuk Kim /* init SIT information */ 4355351df4b2SJaegeuk Kim sit_i->s_ops = &default_salloc_ops; 4356351df4b2SJaegeuk Kim 4357351df4b2SJaegeuk Kim sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); 4358351df4b2SJaegeuk Kim sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; 4359c79b7ff1SJaegeuk Kim sit_i->written_valid_blocks = 0; 4360bbf9f7d9SSahitya Tummala sit_i->bitmap_size = sit_bitmap_size; 4361351df4b2SJaegeuk Kim sit_i->dirty_sentries = 0; 4362351df4b2SJaegeuk Kim sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; 4363351df4b2SJaegeuk Kim sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); 4364a7e679b5SJaegeuk Kim sit_i->mounted_time = ktime_get_boottime_seconds(); 43653d26fa6bSChao Yu init_rwsem(&sit_i->sentry_lock); 4366351df4b2SJaegeuk Kim return 0; 4367351df4b2SJaegeuk Kim } 4368351df4b2SJaegeuk Kim 4369351df4b2SJaegeuk Kim static int build_free_segmap(struct f2fs_sb_info *sbi) 4370351df4b2SJaegeuk Kim { 4371351df4b2SJaegeuk Kim struct free_segmap_info *free_i; 4372351df4b2SJaegeuk Kim unsigned int bitmap_size, sec_bitmap_size; 4373351df4b2SJaegeuk Kim 4374351df4b2SJaegeuk Kim /* allocate memory for free segmap information */ 4375acbf054dSChao Yu free_i = f2fs_kzalloc(sbi, sizeof(struct free_segmap_info), GFP_KERNEL); 4376351df4b2SJaegeuk Kim if (!free_i) 4377351df4b2SJaegeuk Kim return -ENOMEM; 4378351df4b2SJaegeuk Kim 4379351df4b2SJaegeuk Kim SM_I(sbi)->free_info = free_i; 4380351df4b2SJaegeuk Kim 43817cd8558bSJaegeuk Kim bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 4382628b3d14SChao Yu free_i->free_segmap = f2fs_kvmalloc(sbi, bitmap_size, GFP_KERNEL); 4383351df4b2SJaegeuk Kim if (!free_i->free_segmap) 4384351df4b2SJaegeuk Kim return -ENOMEM; 4385351df4b2SJaegeuk Kim 43867cd8558bSJaegeuk Kim sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 4387628b3d14SChao Yu free_i->free_secmap = f2fs_kvmalloc(sbi, sec_bitmap_size, GFP_KERNEL); 4388351df4b2SJaegeuk Kim if (!free_i->free_secmap) 4389351df4b2SJaegeuk Kim return -ENOMEM; 4390351df4b2SJaegeuk Kim 4391351df4b2SJaegeuk Kim /* set all segments as dirty temporarily */ 4392351df4b2SJaegeuk Kim memset(free_i->free_segmap, 0xff, bitmap_size); 4393351df4b2SJaegeuk Kim memset(free_i->free_secmap, 0xff, sec_bitmap_size); 4394351df4b2SJaegeuk Kim 4395351df4b2SJaegeuk Kim /* init free segmap information */ 43967cd8558bSJaegeuk Kim free_i->start_segno = GET_SEGNO_FROM_SEG0(sbi, MAIN_BLKADDR(sbi)); 4397351df4b2SJaegeuk Kim free_i->free_segments = 0; 4398351df4b2SJaegeuk Kim free_i->free_sections = 0; 43991a118ccfSChao Yu spin_lock_init(&free_i->segmap_lock); 4400351df4b2SJaegeuk Kim return 0; 4401351df4b2SJaegeuk Kim } 4402351df4b2SJaegeuk Kim 4403351df4b2SJaegeuk Kim static int build_curseg(struct f2fs_sb_info *sbi) 4404351df4b2SJaegeuk Kim { 44051042d60fSNamjae Jeon struct curseg_info *array; 4406351df4b2SJaegeuk Kim int i; 4407351df4b2SJaegeuk Kim 4408d0b9e42aSChao Yu array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, 4409d0b9e42aSChao Yu sizeof(*array)), GFP_KERNEL); 4410351df4b2SJaegeuk Kim if (!array) 4411351df4b2SJaegeuk Kim return -ENOMEM; 4412351df4b2SJaegeuk Kim 4413351df4b2SJaegeuk Kim SM_I(sbi)->curseg_array = array; 4414351df4b2SJaegeuk Kim 4415d0b9e42aSChao Yu for (i = 0; i < NO_CHECK_TYPE; i++) { 4416351df4b2SJaegeuk Kim mutex_init(&array[i].curseg_mutex); 4417acbf054dSChao Yu array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); 4418351df4b2SJaegeuk Kim if (!array[i].sum_blk) 4419351df4b2SJaegeuk Kim return -ENOMEM; 4420b7ad7512SChao Yu init_rwsem(&array[i].journal_rwsem); 4421acbf054dSChao Yu array[i].journal = f2fs_kzalloc(sbi, 4422acbf054dSChao Yu sizeof(struct f2fs_journal), GFP_KERNEL); 4423b7ad7512SChao Yu if (!array[i].journal) 4424b7ad7512SChao Yu return -ENOMEM; 4425d0b9e42aSChao Yu if (i < NR_PERSISTENT_LOG) 4426d0b9e42aSChao Yu array[i].seg_type = CURSEG_HOT_DATA + i; 4427d0b9e42aSChao Yu else if (i == CURSEG_COLD_DATA_PINNED) 4428d0b9e42aSChao Yu array[i].seg_type = CURSEG_COLD_DATA; 4429093749e2SChao Yu else if (i == CURSEG_ALL_DATA_ATGC) 4430093749e2SChao Yu array[i].seg_type = CURSEG_COLD_DATA; 4431351df4b2SJaegeuk Kim array[i].segno = NULL_SEGNO; 4432351df4b2SJaegeuk Kim array[i].next_blkoff = 0; 4433d0b9e42aSChao Yu array[i].inited = false; 4434351df4b2SJaegeuk Kim } 4435351df4b2SJaegeuk Kim return restore_curseg_summaries(sbi); 4436351df4b2SJaegeuk Kim } 4437351df4b2SJaegeuk Kim 4438c39a1b34SJaegeuk Kim static int build_sit_entries(struct f2fs_sb_info *sbi) 4439351df4b2SJaegeuk Kim { 4440351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 4441351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 4442b7ad7512SChao Yu struct f2fs_journal *journal = curseg->journal; 44439c094040SYunlei He struct seg_entry *se; 44449c094040SYunlei He struct f2fs_sit_entry sit; 444574de593aSChao Yu int sit_blk_cnt = SIT_BLK_CNT(sbi); 444674de593aSChao Yu unsigned int i, start, end; 444774de593aSChao Yu unsigned int readed, start_blk = 0; 4448c39a1b34SJaegeuk Kim int err = 0; 44498a29c126SJaegeuk Kim block_t total_node_blocks = 0; 4450351df4b2SJaegeuk Kim 445174de593aSChao Yu do { 44524d57b86dSChao Yu readed = f2fs_ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES, 4453664ba972SJaegeuk Kim META_SIT, true); 445474de593aSChao Yu 445574de593aSChao Yu start = start_blk * sit_i->sents_per_block; 445674de593aSChao Yu end = (start_blk + readed) * sit_i->sents_per_block; 445774de593aSChao Yu 44587cd8558bSJaegeuk Kim for (; start < end && start < MAIN_SEGS(sbi); start++) { 4459351df4b2SJaegeuk Kim struct f2fs_sit_block *sit_blk; 4460351df4b2SJaegeuk Kim struct page *page; 4461351df4b2SJaegeuk Kim 44629c094040SYunlei He se = &sit_i->sentries[start]; 4463351df4b2SJaegeuk Kim page = get_current_sit_page(sbi, start); 4464edc55aafSJaegeuk Kim if (IS_ERR(page)) 4465edc55aafSJaegeuk Kim return PTR_ERR(page); 4466351df4b2SJaegeuk Kim sit_blk = (struct f2fs_sit_block *)page_address(page); 4467351df4b2SJaegeuk Kim sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; 4468351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 4469d600af23SChao Yu 4470c39a1b34SJaegeuk Kim err = check_block_count(sbi, start, &sit); 4471c39a1b34SJaegeuk Kim if (err) 4472c39a1b34SJaegeuk Kim return err; 4473351df4b2SJaegeuk Kim seg_info_from_raw_sit(se, &sit); 44748a29c126SJaegeuk Kim if (IS_NODESEG(se->type)) 44758a29c126SJaegeuk Kim total_node_blocks += se->valid_blocks; 4476a66cdd98SJaegeuk Kim 4477a66cdd98SJaegeuk Kim /* build discard map only one time */ 44781f43e2adSChao Yu if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { 44791f43e2adSChao Yu memset(se->discard_map, 0xff, 44803e025740SJaegeuk Kim SIT_VBLOCK_MAP_SIZE); 44811f43e2adSChao Yu } else { 44821f43e2adSChao Yu memcpy(se->discard_map, 44831f43e2adSChao Yu se->cur_valid_map, 44841f43e2adSChao Yu SIT_VBLOCK_MAP_SIZE); 44851f43e2adSChao Yu sbi->discard_blks += 44861f43e2adSChao Yu sbi->blocks_per_seg - 44873e025740SJaegeuk Kim se->valid_blocks; 44883e025740SJaegeuk Kim } 4489a66cdd98SJaegeuk Kim 44902c70c5e3SChao Yu if (__is_large_section(sbi)) 4491d600af23SChao Yu get_sec_entry(sbi, start)->valid_blocks += 4492d600af23SChao Yu se->valid_blocks; 4493351df4b2SJaegeuk Kim } 449474de593aSChao Yu start_blk += readed; 449574de593aSChao Yu } while (start_blk < sit_blk_cnt); 4496d600af23SChao Yu 4497d600af23SChao Yu down_read(&curseg->journal_rwsem); 4498d600af23SChao Yu for (i = 0; i < sits_in_cursum(journal); i++) { 4499d600af23SChao Yu unsigned int old_valid_blocks; 4500d600af23SChao Yu 4501d600af23SChao Yu start = le32_to_cpu(segno_in_journal(journal, i)); 4502b2ca374fSJaegeuk Kim if (start >= MAIN_SEGS(sbi)) { 4503dcbb4c10SJoe Perches f2fs_err(sbi, "Wrong journal entry on segno %u", 4504b2ca374fSJaegeuk Kim start); 450510f966bbSChao Yu err = -EFSCORRUPTED; 4506b2ca374fSJaegeuk Kim break; 4507b2ca374fSJaegeuk Kim } 4508b2ca374fSJaegeuk Kim 4509d600af23SChao Yu se = &sit_i->sentries[start]; 4510d600af23SChao Yu sit = sit_in_journal(journal, i); 4511d600af23SChao Yu 4512d600af23SChao Yu old_valid_blocks = se->valid_blocks; 45138a29c126SJaegeuk Kim if (IS_NODESEG(se->type)) 45148a29c126SJaegeuk Kim total_node_blocks -= old_valid_blocks; 4515d600af23SChao Yu 4516c39a1b34SJaegeuk Kim err = check_block_count(sbi, start, &sit); 4517c39a1b34SJaegeuk Kim if (err) 4518c39a1b34SJaegeuk Kim break; 4519d600af23SChao Yu seg_info_from_raw_sit(se, &sit); 45208a29c126SJaegeuk Kim if (IS_NODESEG(se->type)) 45218a29c126SJaegeuk Kim total_node_blocks += se->valid_blocks; 4522d600af23SChao Yu 45231f43e2adSChao Yu if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { 45247d20c8abSChao Yu memset(se->discard_map, 0xff, SIT_VBLOCK_MAP_SIZE); 45251f43e2adSChao Yu } else { 4526d600af23SChao Yu memcpy(se->discard_map, se->cur_valid_map, 4527d600af23SChao Yu SIT_VBLOCK_MAP_SIZE); 4528a9af3fdcSChao Yu sbi->discard_blks += old_valid_blocks; 4529a9af3fdcSChao Yu sbi->discard_blks -= se->valid_blocks; 4530d600af23SChao Yu } 4531d600af23SChao Yu 45322c70c5e3SChao Yu if (__is_large_section(sbi)) { 4533d600af23SChao Yu get_sec_entry(sbi, start)->valid_blocks += 4534a9af3fdcSChao Yu se->valid_blocks; 4535a9af3fdcSChao Yu get_sec_entry(sbi, start)->valid_blocks -= 4536a9af3fdcSChao Yu old_valid_blocks; 4537a9af3fdcSChao Yu } 4538d600af23SChao Yu } 4539d600af23SChao Yu up_read(&curseg->journal_rwsem); 45408a29c126SJaegeuk Kim 45418a29c126SJaegeuk Kim if (!err && total_node_blocks != valid_node_count(sbi)) { 4542dcbb4c10SJoe Perches f2fs_err(sbi, "SIT is corrupted node# %u vs %u", 45438a29c126SJaegeuk Kim total_node_blocks, valid_node_count(sbi)); 454410f966bbSChao Yu err = -EFSCORRUPTED; 45458a29c126SJaegeuk Kim } 45468a29c126SJaegeuk Kim 4547c39a1b34SJaegeuk Kim return err; 4548351df4b2SJaegeuk Kim } 4549351df4b2SJaegeuk Kim 4550351df4b2SJaegeuk Kim static void init_free_segmap(struct f2fs_sb_info *sbi) 4551351df4b2SJaegeuk Kim { 4552351df4b2SJaegeuk Kim unsigned int start; 4553351df4b2SJaegeuk Kim int type; 4554de881df9SAravind Ramesh struct seg_entry *sentry; 4555351df4b2SJaegeuk Kim 45567cd8558bSJaegeuk Kim for (start = 0; start < MAIN_SEGS(sbi); start++) { 4557de881df9SAravind Ramesh if (f2fs_usable_blks_in_seg(sbi, start) == 0) 4558de881df9SAravind Ramesh continue; 4559de881df9SAravind Ramesh sentry = get_seg_entry(sbi, start); 4560351df4b2SJaegeuk Kim if (!sentry->valid_blocks) 4561351df4b2SJaegeuk Kim __set_free(sbi, start); 4562c79b7ff1SJaegeuk Kim else 4563c79b7ff1SJaegeuk Kim SIT_I(sbi)->written_valid_blocks += 4564c79b7ff1SJaegeuk Kim sentry->valid_blocks; 4565351df4b2SJaegeuk Kim } 4566351df4b2SJaegeuk Kim 4567351df4b2SJaegeuk Kim /* set use the current segments */ 4568351df4b2SJaegeuk Kim for (type = CURSEG_HOT_DATA; type <= CURSEG_COLD_NODE; type++) { 4569351df4b2SJaegeuk Kim struct curseg_info *curseg_t = CURSEG_I(sbi, type); 4570*5f029c04SYi Zhuang 4571351df4b2SJaegeuk Kim __set_test_and_inuse(sbi, curseg_t->segno); 4572351df4b2SJaegeuk Kim } 4573351df4b2SJaegeuk Kim } 4574351df4b2SJaegeuk Kim 4575351df4b2SJaegeuk Kim static void init_dirty_segmap(struct f2fs_sb_info *sbi) 4576351df4b2SJaegeuk Kim { 4577351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 4578351df4b2SJaegeuk Kim struct free_segmap_info *free_i = FREE_I(sbi); 4579da52f8adSJack Qiu unsigned int segno = 0, offset = 0, secno; 4580de881df9SAravind Ramesh block_t valid_blocks, usable_blks_in_seg; 4581123aaf77SShin'ichiro Kawasaki block_t blks_per_sec = BLKS_PER_SEC(sbi); 4582351df4b2SJaegeuk Kim 45838736fbf0SNamjae Jeon while (1) { 4584351df4b2SJaegeuk Kim /* find dirty segment based on free segmap */ 45857cd8558bSJaegeuk Kim segno = find_next_inuse(free_i, MAIN_SEGS(sbi), offset); 45867cd8558bSJaegeuk Kim if (segno >= MAIN_SEGS(sbi)) 4587351df4b2SJaegeuk Kim break; 4588351df4b2SJaegeuk Kim offset = segno + 1; 4589302bd348SJaegeuk Kim valid_blocks = get_valid_blocks(sbi, segno, false); 4590de881df9SAravind Ramesh usable_blks_in_seg = f2fs_usable_blks_in_seg(sbi, segno); 4591de881df9SAravind Ramesh if (valid_blocks == usable_blks_in_seg || !valid_blocks) 4592351df4b2SJaegeuk Kim continue; 4593de881df9SAravind Ramesh if (valid_blocks > usable_blks_in_seg) { 4594ec325b52SJaegeuk Kim f2fs_bug_on(sbi, 1); 4595ec325b52SJaegeuk Kim continue; 4596ec325b52SJaegeuk Kim } 4597351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 4598351df4b2SJaegeuk Kim __locate_dirty_segment(sbi, segno, DIRTY); 4599351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 4600351df4b2SJaegeuk Kim } 4601da52f8adSJack Qiu 4602da52f8adSJack Qiu if (!__is_large_section(sbi)) 4603da52f8adSJack Qiu return; 4604da52f8adSJack Qiu 4605da52f8adSJack Qiu mutex_lock(&dirty_i->seglist_lock); 46065335bfc6SJack Qiu for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { 4607da52f8adSJack Qiu valid_blocks = get_valid_blocks(sbi, segno, true); 4608da52f8adSJack Qiu secno = GET_SEC_FROM_SEG(sbi, segno); 4609da52f8adSJack Qiu 4610da52f8adSJack Qiu if (!valid_blocks || valid_blocks == blks_per_sec) 4611da52f8adSJack Qiu continue; 4612da52f8adSJack Qiu if (IS_CURSEC(sbi, secno)) 4613da52f8adSJack Qiu continue; 4614da52f8adSJack Qiu set_bit(secno, dirty_i->dirty_secmap); 4615da52f8adSJack Qiu } 4616da52f8adSJack Qiu mutex_unlock(&dirty_i->seglist_lock); 4617351df4b2SJaegeuk Kim } 4618351df4b2SJaegeuk Kim 46195ec4e49fSJaegeuk Kim static int init_victim_secmap(struct f2fs_sb_info *sbi) 4620351df4b2SJaegeuk Kim { 4621351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 46227cd8558bSJaegeuk Kim unsigned int bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 4623351df4b2SJaegeuk Kim 4624628b3d14SChao Yu dirty_i->victim_secmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 46255ec4e49fSJaegeuk Kim if (!dirty_i->victim_secmap) 4626351df4b2SJaegeuk Kim return -ENOMEM; 4627351df4b2SJaegeuk Kim return 0; 4628351df4b2SJaegeuk Kim } 4629351df4b2SJaegeuk Kim 4630351df4b2SJaegeuk Kim static int build_dirty_segmap(struct f2fs_sb_info *sbi) 4631351df4b2SJaegeuk Kim { 4632351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i; 4633351df4b2SJaegeuk Kim unsigned int bitmap_size, i; 4634351df4b2SJaegeuk Kim 4635351df4b2SJaegeuk Kim /* allocate memory for dirty segments list information */ 4636acbf054dSChao Yu dirty_i = f2fs_kzalloc(sbi, sizeof(struct dirty_seglist_info), 4637acbf054dSChao Yu GFP_KERNEL); 4638351df4b2SJaegeuk Kim if (!dirty_i) 4639351df4b2SJaegeuk Kim return -ENOMEM; 4640351df4b2SJaegeuk Kim 4641351df4b2SJaegeuk Kim SM_I(sbi)->dirty_info = dirty_i; 4642351df4b2SJaegeuk Kim mutex_init(&dirty_i->seglist_lock); 4643351df4b2SJaegeuk Kim 46447cd8558bSJaegeuk Kim bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 4645351df4b2SJaegeuk Kim 4646351df4b2SJaegeuk Kim for (i = 0; i < NR_DIRTY_TYPE; i++) { 4647628b3d14SChao Yu dirty_i->dirty_segmap[i] = f2fs_kvzalloc(sbi, bitmap_size, 4648628b3d14SChao Yu GFP_KERNEL); 4649351df4b2SJaegeuk Kim if (!dirty_i->dirty_segmap[i]) 4650351df4b2SJaegeuk Kim return -ENOMEM; 4651351df4b2SJaegeuk Kim } 4652351df4b2SJaegeuk Kim 4653da52f8adSJack Qiu if (__is_large_section(sbi)) { 4654da52f8adSJack Qiu bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 4655da52f8adSJack Qiu dirty_i->dirty_secmap = f2fs_kvzalloc(sbi, 4656da52f8adSJack Qiu bitmap_size, GFP_KERNEL); 4657da52f8adSJack Qiu if (!dirty_i->dirty_secmap) 4658da52f8adSJack Qiu return -ENOMEM; 4659da52f8adSJack Qiu } 4660da52f8adSJack Qiu 4661351df4b2SJaegeuk Kim init_dirty_segmap(sbi); 46625ec4e49fSJaegeuk Kim return init_victim_secmap(sbi); 4663351df4b2SJaegeuk Kim } 4664351df4b2SJaegeuk Kim 4665c854f4d6SChao Yu static int sanity_check_curseg(struct f2fs_sb_info *sbi) 4666c854f4d6SChao Yu { 4667c854f4d6SChao Yu int i; 4668c854f4d6SChao Yu 4669c854f4d6SChao Yu /* 4670c854f4d6SChao Yu * In LFS/SSR curseg, .next_blkoff should point to an unused blkaddr; 4671c854f4d6SChao Yu * In LFS curseg, all blkaddr after .next_blkoff should be unused. 4672c854f4d6SChao Yu */ 4673d0b9e42aSChao Yu for (i = 0; i < NR_PERSISTENT_LOG; i++) { 4674c854f4d6SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, i); 4675c854f4d6SChao Yu struct seg_entry *se = get_seg_entry(sbi, curseg->segno); 4676c854f4d6SChao Yu unsigned int blkofs = curseg->next_blkoff; 4677c854f4d6SChao Yu 4678093749e2SChao Yu sanity_check_seg_type(sbi, curseg->seg_type); 4679093749e2SChao Yu 4680c854f4d6SChao Yu if (f2fs_test_bit(blkofs, se->cur_valid_map)) 4681c854f4d6SChao Yu goto out; 4682c854f4d6SChao Yu 4683c854f4d6SChao Yu if (curseg->alloc_type == SSR) 4684c854f4d6SChao Yu continue; 4685c854f4d6SChao Yu 4686c854f4d6SChao Yu for (blkofs += 1; blkofs < sbi->blocks_per_seg; blkofs++) { 4687c854f4d6SChao Yu if (!f2fs_test_bit(blkofs, se->cur_valid_map)) 4688c854f4d6SChao Yu continue; 4689c854f4d6SChao Yu out: 4690dcbb4c10SJoe Perches f2fs_err(sbi, 4691dcbb4c10SJoe Perches "Current segment's next free block offset is inconsistent with bitmap, logtype:%u, segno:%u, type:%u, next_blkoff:%u, blkofs:%u", 4692c854f4d6SChao Yu i, curseg->segno, curseg->alloc_type, 4693c854f4d6SChao Yu curseg->next_blkoff, blkofs); 469410f966bbSChao Yu return -EFSCORRUPTED; 4695c854f4d6SChao Yu } 4696c854f4d6SChao Yu } 4697c854f4d6SChao Yu return 0; 4698c854f4d6SChao Yu } 4699c854f4d6SChao Yu 4700c426d991SShin'ichiro Kawasaki #ifdef CONFIG_BLK_DEV_ZONED 4701c426d991SShin'ichiro Kawasaki 4702d508c94eSShin'ichiro Kawasaki static int check_zone_write_pointer(struct f2fs_sb_info *sbi, 4703d508c94eSShin'ichiro Kawasaki struct f2fs_dev_info *fdev, 4704d508c94eSShin'ichiro Kawasaki struct blk_zone *zone) 4705d508c94eSShin'ichiro Kawasaki { 4706d508c94eSShin'ichiro Kawasaki unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno; 4707d508c94eSShin'ichiro Kawasaki block_t zone_block, wp_block, last_valid_block; 4708d508c94eSShin'ichiro Kawasaki unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; 4709d508c94eSShin'ichiro Kawasaki int i, s, b, ret; 4710d508c94eSShin'ichiro Kawasaki struct seg_entry *se; 4711d508c94eSShin'ichiro Kawasaki 4712d508c94eSShin'ichiro Kawasaki if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ) 4713d508c94eSShin'ichiro Kawasaki return 0; 4714d508c94eSShin'ichiro Kawasaki 4715d508c94eSShin'ichiro Kawasaki wp_block = fdev->start_blk + (zone->wp >> log_sectors_per_block); 4716d508c94eSShin'ichiro Kawasaki wp_segno = GET_SEGNO(sbi, wp_block); 4717d508c94eSShin'ichiro Kawasaki wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); 4718d508c94eSShin'ichiro Kawasaki zone_block = fdev->start_blk + (zone->start >> log_sectors_per_block); 4719d508c94eSShin'ichiro Kawasaki zone_segno = GET_SEGNO(sbi, zone_block); 4720d508c94eSShin'ichiro Kawasaki zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno); 4721d508c94eSShin'ichiro Kawasaki 4722d508c94eSShin'ichiro Kawasaki if (zone_segno >= MAIN_SEGS(sbi)) 4723d508c94eSShin'ichiro Kawasaki return 0; 4724d508c94eSShin'ichiro Kawasaki 4725d508c94eSShin'ichiro Kawasaki /* 4726d508c94eSShin'ichiro Kawasaki * Skip check of zones cursegs point to, since 4727d508c94eSShin'ichiro Kawasaki * fix_curseg_write_pointer() checks them. 4728d508c94eSShin'ichiro Kawasaki */ 4729d508c94eSShin'ichiro Kawasaki for (i = 0; i < NO_CHECK_TYPE; i++) 4730d508c94eSShin'ichiro Kawasaki if (zone_secno == GET_SEC_FROM_SEG(sbi, 4731d508c94eSShin'ichiro Kawasaki CURSEG_I(sbi, i)->segno)) 4732d508c94eSShin'ichiro Kawasaki return 0; 4733d508c94eSShin'ichiro Kawasaki 4734d508c94eSShin'ichiro Kawasaki /* 4735d508c94eSShin'ichiro Kawasaki * Get last valid block of the zone. 4736d508c94eSShin'ichiro Kawasaki */ 4737d508c94eSShin'ichiro Kawasaki last_valid_block = zone_block - 1; 4738d508c94eSShin'ichiro Kawasaki for (s = sbi->segs_per_sec - 1; s >= 0; s--) { 4739d508c94eSShin'ichiro Kawasaki segno = zone_segno + s; 4740d508c94eSShin'ichiro Kawasaki se = get_seg_entry(sbi, segno); 4741d508c94eSShin'ichiro Kawasaki for (b = sbi->blocks_per_seg - 1; b >= 0; b--) 4742d508c94eSShin'ichiro Kawasaki if (f2fs_test_bit(b, se->cur_valid_map)) { 4743d508c94eSShin'ichiro Kawasaki last_valid_block = START_BLOCK(sbi, segno) + b; 4744d508c94eSShin'ichiro Kawasaki break; 4745d508c94eSShin'ichiro Kawasaki } 4746d508c94eSShin'ichiro Kawasaki if (last_valid_block >= zone_block) 4747d508c94eSShin'ichiro Kawasaki break; 4748d508c94eSShin'ichiro Kawasaki } 4749d508c94eSShin'ichiro Kawasaki 4750d508c94eSShin'ichiro Kawasaki /* 4751d508c94eSShin'ichiro Kawasaki * If last valid block is beyond the write pointer, report the 4752d508c94eSShin'ichiro Kawasaki * inconsistency. This inconsistency does not cause write error 4753d508c94eSShin'ichiro Kawasaki * because the zone will not be selected for write operation until 4754d508c94eSShin'ichiro Kawasaki * it get discarded. Just report it. 4755d508c94eSShin'ichiro Kawasaki */ 4756d508c94eSShin'ichiro Kawasaki if (last_valid_block >= wp_block) { 4757d508c94eSShin'ichiro Kawasaki f2fs_notice(sbi, "Valid block beyond write pointer: " 4758d508c94eSShin'ichiro Kawasaki "valid block[0x%x,0x%x] wp[0x%x,0x%x]", 4759d508c94eSShin'ichiro Kawasaki GET_SEGNO(sbi, last_valid_block), 4760d508c94eSShin'ichiro Kawasaki GET_BLKOFF_FROM_SEG0(sbi, last_valid_block), 4761d508c94eSShin'ichiro Kawasaki wp_segno, wp_blkoff); 4762d508c94eSShin'ichiro Kawasaki return 0; 4763d508c94eSShin'ichiro Kawasaki } 4764d508c94eSShin'ichiro Kawasaki 4765d508c94eSShin'ichiro Kawasaki /* 4766d508c94eSShin'ichiro Kawasaki * If there is no valid block in the zone and if write pointer is 4767d508c94eSShin'ichiro Kawasaki * not at zone start, reset the write pointer. 4768d508c94eSShin'ichiro Kawasaki */ 4769d508c94eSShin'ichiro Kawasaki if (last_valid_block + 1 == zone_block && zone->wp != zone->start) { 4770d508c94eSShin'ichiro Kawasaki f2fs_notice(sbi, 4771d508c94eSShin'ichiro Kawasaki "Zone without valid block has non-zero write " 4772d508c94eSShin'ichiro Kawasaki "pointer. Reset the write pointer: wp[0x%x,0x%x]", 4773d508c94eSShin'ichiro Kawasaki wp_segno, wp_blkoff); 4774d508c94eSShin'ichiro Kawasaki ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block, 4775d508c94eSShin'ichiro Kawasaki zone->len >> log_sectors_per_block); 4776d508c94eSShin'ichiro Kawasaki if (ret) { 4777d508c94eSShin'ichiro Kawasaki f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", 4778d508c94eSShin'ichiro Kawasaki fdev->path, ret); 4779d508c94eSShin'ichiro Kawasaki return ret; 4780d508c94eSShin'ichiro Kawasaki } 4781d508c94eSShin'ichiro Kawasaki } 4782d508c94eSShin'ichiro Kawasaki 4783d508c94eSShin'ichiro Kawasaki return 0; 4784d508c94eSShin'ichiro Kawasaki } 4785d508c94eSShin'ichiro Kawasaki 4786c426d991SShin'ichiro Kawasaki static struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi, 4787c426d991SShin'ichiro Kawasaki block_t zone_blkaddr) 4788c426d991SShin'ichiro Kawasaki { 4789c426d991SShin'ichiro Kawasaki int i; 4790c426d991SShin'ichiro Kawasaki 4791c426d991SShin'ichiro Kawasaki for (i = 0; i < sbi->s_ndevs; i++) { 4792c426d991SShin'ichiro Kawasaki if (!bdev_is_zoned(FDEV(i).bdev)) 4793c426d991SShin'ichiro Kawasaki continue; 4794c426d991SShin'ichiro Kawasaki if (sbi->s_ndevs == 1 || (FDEV(i).start_blk <= zone_blkaddr && 4795c426d991SShin'ichiro Kawasaki zone_blkaddr <= FDEV(i).end_blk)) 4796c426d991SShin'ichiro Kawasaki return &FDEV(i); 4797c426d991SShin'ichiro Kawasaki } 4798c426d991SShin'ichiro Kawasaki 4799c426d991SShin'ichiro Kawasaki return NULL; 4800c426d991SShin'ichiro Kawasaki } 4801c426d991SShin'ichiro Kawasaki 4802c426d991SShin'ichiro Kawasaki static int report_one_zone_cb(struct blk_zone *zone, unsigned int idx, 4803*5f029c04SYi Zhuang void *data) 4804*5f029c04SYi Zhuang { 4805c426d991SShin'ichiro Kawasaki memcpy(data, zone, sizeof(struct blk_zone)); 4806c426d991SShin'ichiro Kawasaki return 0; 4807c426d991SShin'ichiro Kawasaki } 4808c426d991SShin'ichiro Kawasaki 4809c426d991SShin'ichiro Kawasaki static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) 4810c426d991SShin'ichiro Kawasaki { 4811c426d991SShin'ichiro Kawasaki struct curseg_info *cs = CURSEG_I(sbi, type); 4812c426d991SShin'ichiro Kawasaki struct f2fs_dev_info *zbd; 4813c426d991SShin'ichiro Kawasaki struct blk_zone zone; 4814c426d991SShin'ichiro Kawasaki unsigned int cs_section, wp_segno, wp_blkoff, wp_sector_off; 4815c426d991SShin'ichiro Kawasaki block_t cs_zone_block, wp_block; 4816c426d991SShin'ichiro Kawasaki unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; 4817c426d991SShin'ichiro Kawasaki sector_t zone_sector; 4818c426d991SShin'ichiro Kawasaki int err; 4819c426d991SShin'ichiro Kawasaki 4820c426d991SShin'ichiro Kawasaki cs_section = GET_SEC_FROM_SEG(sbi, cs->segno); 4821c426d991SShin'ichiro Kawasaki cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section)); 4822c426d991SShin'ichiro Kawasaki 4823c426d991SShin'ichiro Kawasaki zbd = get_target_zoned_dev(sbi, cs_zone_block); 4824c426d991SShin'ichiro Kawasaki if (!zbd) 4825c426d991SShin'ichiro Kawasaki return 0; 4826c426d991SShin'ichiro Kawasaki 4827c426d991SShin'ichiro Kawasaki /* report zone for the sector the curseg points to */ 4828c426d991SShin'ichiro Kawasaki zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) 4829c426d991SShin'ichiro Kawasaki << log_sectors_per_block; 4830c426d991SShin'ichiro Kawasaki err = blkdev_report_zones(zbd->bdev, zone_sector, 1, 4831c426d991SShin'ichiro Kawasaki report_one_zone_cb, &zone); 4832c426d991SShin'ichiro Kawasaki if (err != 1) { 4833c426d991SShin'ichiro Kawasaki f2fs_err(sbi, "Report zone failed: %s errno=(%d)", 4834c426d991SShin'ichiro Kawasaki zbd->path, err); 4835c426d991SShin'ichiro Kawasaki return err; 4836c426d991SShin'ichiro Kawasaki } 4837c426d991SShin'ichiro Kawasaki 4838c426d991SShin'ichiro Kawasaki if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) 4839c426d991SShin'ichiro Kawasaki return 0; 4840c426d991SShin'ichiro Kawasaki 4841c426d991SShin'ichiro Kawasaki wp_block = zbd->start_blk + (zone.wp >> log_sectors_per_block); 4842c426d991SShin'ichiro Kawasaki wp_segno = GET_SEGNO(sbi, wp_block); 4843c426d991SShin'ichiro Kawasaki wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); 4844c426d991SShin'ichiro Kawasaki wp_sector_off = zone.wp & GENMASK(log_sectors_per_block - 1, 0); 4845c426d991SShin'ichiro Kawasaki 4846c426d991SShin'ichiro Kawasaki if (cs->segno == wp_segno && cs->next_blkoff == wp_blkoff && 4847c426d991SShin'ichiro Kawasaki wp_sector_off == 0) 4848c426d991SShin'ichiro Kawasaki return 0; 4849c426d991SShin'ichiro Kawasaki 4850c426d991SShin'ichiro Kawasaki f2fs_notice(sbi, "Unaligned curseg[%d] with write pointer: " 4851c426d991SShin'ichiro Kawasaki "curseg[0x%x,0x%x] wp[0x%x,0x%x]", 4852c426d991SShin'ichiro Kawasaki type, cs->segno, cs->next_blkoff, wp_segno, wp_blkoff); 4853c426d991SShin'ichiro Kawasaki 4854c426d991SShin'ichiro Kawasaki f2fs_notice(sbi, "Assign new section to curseg[%d]: " 4855c426d991SShin'ichiro Kawasaki "curseg[0x%x,0x%x]", type, cs->segno, cs->next_blkoff); 4856c426d991SShin'ichiro Kawasaki allocate_segment_by_default(sbi, type, true); 4857c426d991SShin'ichiro Kawasaki 4858d508c94eSShin'ichiro Kawasaki /* check consistency of the zone curseg pointed to */ 4859d508c94eSShin'ichiro Kawasaki if (check_zone_write_pointer(sbi, zbd, &zone)) 4860d508c94eSShin'ichiro Kawasaki return -EIO; 4861d508c94eSShin'ichiro Kawasaki 4862c426d991SShin'ichiro Kawasaki /* check newly assigned zone */ 4863c426d991SShin'ichiro Kawasaki cs_section = GET_SEC_FROM_SEG(sbi, cs->segno); 4864c426d991SShin'ichiro Kawasaki cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section)); 4865c426d991SShin'ichiro Kawasaki 4866c426d991SShin'ichiro Kawasaki zbd = get_target_zoned_dev(sbi, cs_zone_block); 4867c426d991SShin'ichiro Kawasaki if (!zbd) 4868c426d991SShin'ichiro Kawasaki return 0; 4869c426d991SShin'ichiro Kawasaki 4870c426d991SShin'ichiro Kawasaki zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) 4871c426d991SShin'ichiro Kawasaki << log_sectors_per_block; 4872c426d991SShin'ichiro Kawasaki err = blkdev_report_zones(zbd->bdev, zone_sector, 1, 4873c426d991SShin'ichiro Kawasaki report_one_zone_cb, &zone); 4874c426d991SShin'ichiro Kawasaki if (err != 1) { 4875c426d991SShin'ichiro Kawasaki f2fs_err(sbi, "Report zone failed: %s errno=(%d)", 4876c426d991SShin'ichiro Kawasaki zbd->path, err); 4877c426d991SShin'ichiro Kawasaki return err; 4878c426d991SShin'ichiro Kawasaki } 4879c426d991SShin'ichiro Kawasaki 4880c426d991SShin'ichiro Kawasaki if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) 4881c426d991SShin'ichiro Kawasaki return 0; 4882c426d991SShin'ichiro Kawasaki 4883c426d991SShin'ichiro Kawasaki if (zone.wp != zone.start) { 4884c426d991SShin'ichiro Kawasaki f2fs_notice(sbi, 4885c426d991SShin'ichiro Kawasaki "New zone for curseg[%d] is not yet discarded. " 4886c426d991SShin'ichiro Kawasaki "Reset the zone: curseg[0x%x,0x%x]", 4887c426d991SShin'ichiro Kawasaki type, cs->segno, cs->next_blkoff); 4888c426d991SShin'ichiro Kawasaki err = __f2fs_issue_discard_zone(sbi, zbd->bdev, 4889c426d991SShin'ichiro Kawasaki zone_sector >> log_sectors_per_block, 4890c426d991SShin'ichiro Kawasaki zone.len >> log_sectors_per_block); 4891c426d991SShin'ichiro Kawasaki if (err) { 4892c426d991SShin'ichiro Kawasaki f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", 4893c426d991SShin'ichiro Kawasaki zbd->path, err); 4894c426d991SShin'ichiro Kawasaki return err; 4895c426d991SShin'ichiro Kawasaki } 4896c426d991SShin'ichiro Kawasaki } 4897c426d991SShin'ichiro Kawasaki 4898c426d991SShin'ichiro Kawasaki return 0; 4899c426d991SShin'ichiro Kawasaki } 4900c426d991SShin'ichiro Kawasaki 4901c426d991SShin'ichiro Kawasaki int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) 4902c426d991SShin'ichiro Kawasaki { 4903c426d991SShin'ichiro Kawasaki int i, ret; 4904c426d991SShin'ichiro Kawasaki 4905d0b9e42aSChao Yu for (i = 0; i < NR_PERSISTENT_LOG; i++) { 4906c426d991SShin'ichiro Kawasaki ret = fix_curseg_write_pointer(sbi, i); 4907c426d991SShin'ichiro Kawasaki if (ret) 4908c426d991SShin'ichiro Kawasaki return ret; 4909c426d991SShin'ichiro Kawasaki } 4910c426d991SShin'ichiro Kawasaki 4911c426d991SShin'ichiro Kawasaki return 0; 4912c426d991SShin'ichiro Kawasaki } 4913d508c94eSShin'ichiro Kawasaki 4914d508c94eSShin'ichiro Kawasaki struct check_zone_write_pointer_args { 4915d508c94eSShin'ichiro Kawasaki struct f2fs_sb_info *sbi; 4916d508c94eSShin'ichiro Kawasaki struct f2fs_dev_info *fdev; 4917d508c94eSShin'ichiro Kawasaki }; 4918d508c94eSShin'ichiro Kawasaki 4919d508c94eSShin'ichiro Kawasaki static int check_zone_write_pointer_cb(struct blk_zone *zone, unsigned int idx, 4920*5f029c04SYi Zhuang void *data) 4921*5f029c04SYi Zhuang { 4922d508c94eSShin'ichiro Kawasaki struct check_zone_write_pointer_args *args; 4923*5f029c04SYi Zhuang 4924d508c94eSShin'ichiro Kawasaki args = (struct check_zone_write_pointer_args *)data; 4925d508c94eSShin'ichiro Kawasaki 4926d508c94eSShin'ichiro Kawasaki return check_zone_write_pointer(args->sbi, args->fdev, zone); 4927d508c94eSShin'ichiro Kawasaki } 4928d508c94eSShin'ichiro Kawasaki 4929d508c94eSShin'ichiro Kawasaki int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) 4930d508c94eSShin'ichiro Kawasaki { 4931d508c94eSShin'ichiro Kawasaki int i, ret; 4932d508c94eSShin'ichiro Kawasaki struct check_zone_write_pointer_args args; 4933d508c94eSShin'ichiro Kawasaki 4934d508c94eSShin'ichiro Kawasaki for (i = 0; i < sbi->s_ndevs; i++) { 4935d508c94eSShin'ichiro Kawasaki if (!bdev_is_zoned(FDEV(i).bdev)) 4936d508c94eSShin'ichiro Kawasaki continue; 4937d508c94eSShin'ichiro Kawasaki 4938d508c94eSShin'ichiro Kawasaki args.sbi = sbi; 4939d508c94eSShin'ichiro Kawasaki args.fdev = &FDEV(i); 4940d508c94eSShin'ichiro Kawasaki ret = blkdev_report_zones(FDEV(i).bdev, 0, BLK_ALL_ZONES, 4941d508c94eSShin'ichiro Kawasaki check_zone_write_pointer_cb, &args); 4942d508c94eSShin'ichiro Kawasaki if (ret < 0) 4943d508c94eSShin'ichiro Kawasaki return ret; 4944d508c94eSShin'ichiro Kawasaki } 4945d508c94eSShin'ichiro Kawasaki 4946d508c94eSShin'ichiro Kawasaki return 0; 4947d508c94eSShin'ichiro Kawasaki } 4948de881df9SAravind Ramesh 4949de881df9SAravind Ramesh static bool is_conv_zone(struct f2fs_sb_info *sbi, unsigned int zone_idx, 4950de881df9SAravind Ramesh unsigned int dev_idx) 4951de881df9SAravind Ramesh { 4952de881df9SAravind Ramesh if (!bdev_is_zoned(FDEV(dev_idx).bdev)) 4953de881df9SAravind Ramesh return true; 4954de881df9SAravind Ramesh return !test_bit(zone_idx, FDEV(dev_idx).blkz_seq); 4955de881df9SAravind Ramesh } 4956de881df9SAravind Ramesh 4957de881df9SAravind Ramesh /* Return the zone index in the given device */ 4958de881df9SAravind Ramesh static unsigned int get_zone_idx(struct f2fs_sb_info *sbi, unsigned int secno, 4959de881df9SAravind Ramesh int dev_idx) 4960de881df9SAravind Ramesh { 4961de881df9SAravind Ramesh block_t sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno)); 4962de881df9SAravind Ramesh 4963de881df9SAravind Ramesh return (sec_start_blkaddr - FDEV(dev_idx).start_blk) >> 4964de881df9SAravind Ramesh sbi->log_blocks_per_blkz; 4965de881df9SAravind Ramesh } 4966de881df9SAravind Ramesh 4967de881df9SAravind Ramesh /* 4968de881df9SAravind Ramesh * Return the usable segments in a section based on the zone's 4969de881df9SAravind Ramesh * corresponding zone capacity. Zone is equal to a section. 4970de881df9SAravind Ramesh */ 4971de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_segs_in_sec( 4972de881df9SAravind Ramesh struct f2fs_sb_info *sbi, unsigned int segno) 4973de881df9SAravind Ramesh { 4974de881df9SAravind Ramesh unsigned int dev_idx, zone_idx, unusable_segs_in_sec; 4975de881df9SAravind Ramesh 4976de881df9SAravind Ramesh dev_idx = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno)); 4977de881df9SAravind Ramesh zone_idx = get_zone_idx(sbi, GET_SEC_FROM_SEG(sbi, segno), dev_idx); 4978de881df9SAravind Ramesh 4979de881df9SAravind Ramesh /* Conventional zone's capacity is always equal to zone size */ 4980de881df9SAravind Ramesh if (is_conv_zone(sbi, zone_idx, dev_idx)) 4981de881df9SAravind Ramesh return sbi->segs_per_sec; 4982de881df9SAravind Ramesh 4983de881df9SAravind Ramesh /* 4984de881df9SAravind Ramesh * If the zone_capacity_blocks array is NULL, then zone capacity 4985de881df9SAravind Ramesh * is equal to the zone size for all zones 4986de881df9SAravind Ramesh */ 4987de881df9SAravind Ramesh if (!FDEV(dev_idx).zone_capacity_blocks) 4988de881df9SAravind Ramesh return sbi->segs_per_sec; 4989de881df9SAravind Ramesh 4990de881df9SAravind Ramesh /* Get the segment count beyond zone capacity block */ 4991de881df9SAravind Ramesh unusable_segs_in_sec = (sbi->blocks_per_blkz - 4992de881df9SAravind Ramesh FDEV(dev_idx).zone_capacity_blocks[zone_idx]) >> 4993de881df9SAravind Ramesh sbi->log_blocks_per_seg; 4994de881df9SAravind Ramesh return sbi->segs_per_sec - unusable_segs_in_sec; 4995de881df9SAravind Ramesh } 4996de881df9SAravind Ramesh 4997de881df9SAravind Ramesh /* 4998de881df9SAravind Ramesh * Return the number of usable blocks in a segment. The number of blocks 4999de881df9SAravind Ramesh * returned is always equal to the number of blocks in a segment for 5000de881df9SAravind Ramesh * segments fully contained within a sequential zone capacity or a 5001de881df9SAravind Ramesh * conventional zone. For segments partially contained in a sequential 5002de881df9SAravind Ramesh * zone capacity, the number of usable blocks up to the zone capacity 5003de881df9SAravind Ramesh * is returned. 0 is returned in all other cases. 5004de881df9SAravind Ramesh */ 5005de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_blks_in_seg( 5006de881df9SAravind Ramesh struct f2fs_sb_info *sbi, unsigned int segno) 5007de881df9SAravind Ramesh { 5008de881df9SAravind Ramesh block_t seg_start, sec_start_blkaddr, sec_cap_blkaddr; 5009de881df9SAravind Ramesh unsigned int zone_idx, dev_idx, secno; 5010de881df9SAravind Ramesh 5011de881df9SAravind Ramesh secno = GET_SEC_FROM_SEG(sbi, segno); 5012de881df9SAravind Ramesh seg_start = START_BLOCK(sbi, segno); 5013de881df9SAravind Ramesh dev_idx = f2fs_target_device_index(sbi, seg_start); 5014de881df9SAravind Ramesh zone_idx = get_zone_idx(sbi, secno, dev_idx); 5015de881df9SAravind Ramesh 5016de881df9SAravind Ramesh /* 5017de881df9SAravind Ramesh * Conventional zone's capacity is always equal to zone size, 5018de881df9SAravind Ramesh * so, blocks per segment is unchanged. 5019de881df9SAravind Ramesh */ 5020de881df9SAravind Ramesh if (is_conv_zone(sbi, zone_idx, dev_idx)) 5021de881df9SAravind Ramesh return sbi->blocks_per_seg; 5022de881df9SAravind Ramesh 5023de881df9SAravind Ramesh if (!FDEV(dev_idx).zone_capacity_blocks) 5024de881df9SAravind Ramesh return sbi->blocks_per_seg; 5025de881df9SAravind Ramesh 5026de881df9SAravind Ramesh sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno)); 5027de881df9SAravind Ramesh sec_cap_blkaddr = sec_start_blkaddr + 5028de881df9SAravind Ramesh FDEV(dev_idx).zone_capacity_blocks[zone_idx]; 5029de881df9SAravind Ramesh 5030de881df9SAravind Ramesh /* 5031de881df9SAravind Ramesh * If segment starts before zone capacity and spans beyond 5032de881df9SAravind Ramesh * zone capacity, then usable blocks are from seg start to 5033de881df9SAravind Ramesh * zone capacity. If the segment starts after the zone capacity, 5034de881df9SAravind Ramesh * then there are no usable blocks. 5035de881df9SAravind Ramesh */ 5036de881df9SAravind Ramesh if (seg_start >= sec_cap_blkaddr) 5037de881df9SAravind Ramesh return 0; 5038de881df9SAravind Ramesh if (seg_start + sbi->blocks_per_seg > sec_cap_blkaddr) 5039de881df9SAravind Ramesh return sec_cap_blkaddr - seg_start; 5040de881df9SAravind Ramesh 5041de881df9SAravind Ramesh return sbi->blocks_per_seg; 5042de881df9SAravind Ramesh } 5043c426d991SShin'ichiro Kawasaki #else 5044c426d991SShin'ichiro Kawasaki int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) 5045c426d991SShin'ichiro Kawasaki { 5046c426d991SShin'ichiro Kawasaki return 0; 5047c426d991SShin'ichiro Kawasaki } 5048d508c94eSShin'ichiro Kawasaki 5049d508c94eSShin'ichiro Kawasaki int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) 5050d508c94eSShin'ichiro Kawasaki { 5051d508c94eSShin'ichiro Kawasaki return 0; 5052d508c94eSShin'ichiro Kawasaki } 5053de881df9SAravind Ramesh 5054de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_blks_in_seg(struct f2fs_sb_info *sbi, 5055de881df9SAravind Ramesh unsigned int segno) 5056de881df9SAravind Ramesh { 5057de881df9SAravind Ramesh return 0; 5058de881df9SAravind Ramesh } 5059de881df9SAravind Ramesh 5060de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_segs_in_sec(struct f2fs_sb_info *sbi, 5061de881df9SAravind Ramesh unsigned int segno) 5062de881df9SAravind Ramesh { 5063de881df9SAravind Ramesh return 0; 5064de881df9SAravind Ramesh } 5065c426d991SShin'ichiro Kawasaki #endif 5066de881df9SAravind Ramesh unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi, 5067de881df9SAravind Ramesh unsigned int segno) 5068de881df9SAravind Ramesh { 5069de881df9SAravind Ramesh if (f2fs_sb_has_blkzoned(sbi)) 5070de881df9SAravind Ramesh return f2fs_usable_zone_blks_in_seg(sbi, segno); 5071de881df9SAravind Ramesh 5072de881df9SAravind Ramesh return sbi->blocks_per_seg; 5073de881df9SAravind Ramesh } 5074de881df9SAravind Ramesh 5075de881df9SAravind Ramesh unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi, 5076de881df9SAravind Ramesh unsigned int segno) 5077de881df9SAravind Ramesh { 5078de881df9SAravind Ramesh if (f2fs_sb_has_blkzoned(sbi)) 5079de881df9SAravind Ramesh return f2fs_usable_zone_segs_in_sec(sbi, segno); 5080de881df9SAravind Ramesh 5081de881df9SAravind Ramesh return sbi->segs_per_sec; 5082de881df9SAravind Ramesh } 5083c426d991SShin'ichiro Kawasaki 50840a8165d7SJaegeuk Kim /* 5085351df4b2SJaegeuk Kim * Update min, max modified time for cost-benefit GC algorithm 5086351df4b2SJaegeuk Kim */ 5087351df4b2SJaegeuk Kim static void init_min_max_mtime(struct f2fs_sb_info *sbi) 5088351df4b2SJaegeuk Kim { 5089351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 5090351df4b2SJaegeuk Kim unsigned int segno; 5091351df4b2SJaegeuk Kim 50923d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 5093351df4b2SJaegeuk Kim 50945ad25442SChao Yu sit_i->min_mtime = ULLONG_MAX; 5095351df4b2SJaegeuk Kim 50967cd8558bSJaegeuk Kim for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { 5097351df4b2SJaegeuk Kim unsigned int i; 5098351df4b2SJaegeuk Kim unsigned long long mtime = 0; 5099351df4b2SJaegeuk Kim 5100351df4b2SJaegeuk Kim for (i = 0; i < sbi->segs_per_sec; i++) 5101351df4b2SJaegeuk Kim mtime += get_seg_entry(sbi, segno + i)->mtime; 5102351df4b2SJaegeuk Kim 5103351df4b2SJaegeuk Kim mtime = div_u64(mtime, sbi->segs_per_sec); 5104351df4b2SJaegeuk Kim 5105351df4b2SJaegeuk Kim if (sit_i->min_mtime > mtime) 5106351df4b2SJaegeuk Kim sit_i->min_mtime = mtime; 5107351df4b2SJaegeuk Kim } 5108a1f72ac2SChao Yu sit_i->max_mtime = get_mtime(sbi, false); 5109093749e2SChao Yu sit_i->dirty_max_mtime = 0; 51103d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 5111351df4b2SJaegeuk Kim } 5112351df4b2SJaegeuk Kim 51134d57b86dSChao Yu int f2fs_build_segment_manager(struct f2fs_sb_info *sbi) 5114351df4b2SJaegeuk Kim { 5115351df4b2SJaegeuk Kim struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); 5116351df4b2SJaegeuk Kim struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 51171042d60fSNamjae Jeon struct f2fs_sm_info *sm_info; 5118351df4b2SJaegeuk Kim int err; 5119351df4b2SJaegeuk Kim 5120acbf054dSChao Yu sm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_sm_info), GFP_KERNEL); 5121351df4b2SJaegeuk Kim if (!sm_info) 5122351df4b2SJaegeuk Kim return -ENOMEM; 5123351df4b2SJaegeuk Kim 5124351df4b2SJaegeuk Kim /* init sm info */ 5125351df4b2SJaegeuk Kim sbi->sm_info = sm_info; 5126351df4b2SJaegeuk Kim sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); 5127351df4b2SJaegeuk Kim sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); 5128351df4b2SJaegeuk Kim sm_info->segment_count = le32_to_cpu(raw_super->segment_count); 5129351df4b2SJaegeuk Kim sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); 5130351df4b2SJaegeuk Kim sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); 5131351df4b2SJaegeuk Kim sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main); 5132351df4b2SJaegeuk Kim sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); 513358c41035SJaegeuk Kim sm_info->rec_prefree_segments = sm_info->main_segments * 513458c41035SJaegeuk Kim DEF_RECLAIM_PREFREE_SEGMENTS / 100; 513544a83499SJaegeuk Kim if (sm_info->rec_prefree_segments > DEF_MAX_RECLAIM_PREFREE_SEGMENTS) 513644a83499SJaegeuk Kim sm_info->rec_prefree_segments = DEF_MAX_RECLAIM_PREFREE_SEGMENTS; 513744a83499SJaegeuk Kim 5138b0332a0fSChao Yu if (!f2fs_lfs_mode(sbi)) 51399b5f136fSJaegeuk Kim sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; 5140216fbd64SJaegeuk Kim sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; 5141c1ce1b02SJaegeuk Kim sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; 5142853137ceSJaegeuk Kim sm_info->min_seq_blocks = sbi->blocks_per_seg * sbi->segs_per_sec; 5143ef095d19SJaegeuk Kim sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; 5144a2a12b67SChao Yu sm_info->min_ssr_sections = reserved_sections(sbi); 5145351df4b2SJaegeuk Kim 5146184a5cd2SChao Yu INIT_LIST_HEAD(&sm_info->sit_entry_set); 5147184a5cd2SChao Yu 51482b60311dSChao Yu init_rwsem(&sm_info->curseg_lock); 51492b60311dSChao Yu 5150d4fdf8baSYunlei He if (!f2fs_readonly(sbi->sb)) { 51514d57b86dSChao Yu err = f2fs_create_flush_cmd_control(sbi); 51522163d198SGu Zheng if (err) 5153a688b9d9SGu Zheng return err; 5154a688b9d9SGu Zheng } 51556b4afdd7SJaegeuk Kim 51560b54fb84SJaegeuk Kim err = create_discard_cmd_control(sbi); 51570b54fb84SJaegeuk Kim if (err) 51580b54fb84SJaegeuk Kim return err; 51590b54fb84SJaegeuk Kim 5160351df4b2SJaegeuk Kim err = build_sit_info(sbi); 5161351df4b2SJaegeuk Kim if (err) 5162351df4b2SJaegeuk Kim return err; 5163351df4b2SJaegeuk Kim err = build_free_segmap(sbi); 5164351df4b2SJaegeuk Kim if (err) 5165351df4b2SJaegeuk Kim return err; 5166351df4b2SJaegeuk Kim err = build_curseg(sbi); 5167351df4b2SJaegeuk Kim if (err) 5168351df4b2SJaegeuk Kim return err; 5169351df4b2SJaegeuk Kim 5170351df4b2SJaegeuk Kim /* reinit free segmap based on SIT */ 5171c39a1b34SJaegeuk Kim err = build_sit_entries(sbi); 5172c39a1b34SJaegeuk Kim if (err) 5173c39a1b34SJaegeuk Kim return err; 5174351df4b2SJaegeuk Kim 5175351df4b2SJaegeuk Kim init_free_segmap(sbi); 5176351df4b2SJaegeuk Kim err = build_dirty_segmap(sbi); 5177351df4b2SJaegeuk Kim if (err) 5178351df4b2SJaegeuk Kim return err; 5179351df4b2SJaegeuk Kim 5180c854f4d6SChao Yu err = sanity_check_curseg(sbi); 5181c854f4d6SChao Yu if (err) 5182c854f4d6SChao Yu return err; 5183c854f4d6SChao Yu 5184351df4b2SJaegeuk Kim init_min_max_mtime(sbi); 5185351df4b2SJaegeuk Kim return 0; 5186351df4b2SJaegeuk Kim } 5187351df4b2SJaegeuk Kim 5188351df4b2SJaegeuk Kim static void discard_dirty_segmap(struct f2fs_sb_info *sbi, 5189351df4b2SJaegeuk Kim enum dirty_type dirty_type) 5190351df4b2SJaegeuk Kim { 5191351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 5192351df4b2SJaegeuk Kim 5193351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 519439307a8eSJaegeuk Kim kvfree(dirty_i->dirty_segmap[dirty_type]); 5195351df4b2SJaegeuk Kim dirty_i->nr_dirty[dirty_type] = 0; 5196351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 5197351df4b2SJaegeuk Kim } 5198351df4b2SJaegeuk Kim 51995ec4e49fSJaegeuk Kim static void destroy_victim_secmap(struct f2fs_sb_info *sbi) 5200351df4b2SJaegeuk Kim { 5201351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 5202*5f029c04SYi Zhuang 520339307a8eSJaegeuk Kim kvfree(dirty_i->victim_secmap); 5204351df4b2SJaegeuk Kim } 5205351df4b2SJaegeuk Kim 5206351df4b2SJaegeuk Kim static void destroy_dirty_segmap(struct f2fs_sb_info *sbi) 5207351df4b2SJaegeuk Kim { 5208351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 5209351df4b2SJaegeuk Kim int i; 5210351df4b2SJaegeuk Kim 5211351df4b2SJaegeuk Kim if (!dirty_i) 5212351df4b2SJaegeuk Kim return; 5213351df4b2SJaegeuk Kim 5214351df4b2SJaegeuk Kim /* discard pre-free/dirty segments list */ 5215351df4b2SJaegeuk Kim for (i = 0; i < NR_DIRTY_TYPE; i++) 5216351df4b2SJaegeuk Kim discard_dirty_segmap(sbi, i); 5217351df4b2SJaegeuk Kim 5218da52f8adSJack Qiu if (__is_large_section(sbi)) { 5219da52f8adSJack Qiu mutex_lock(&dirty_i->seglist_lock); 5220da52f8adSJack Qiu kvfree(dirty_i->dirty_secmap); 5221da52f8adSJack Qiu mutex_unlock(&dirty_i->seglist_lock); 5222da52f8adSJack Qiu } 5223da52f8adSJack Qiu 52245ec4e49fSJaegeuk Kim destroy_victim_secmap(sbi); 5225351df4b2SJaegeuk Kim SM_I(sbi)->dirty_info = NULL; 5226c8eb7024SChao Yu kfree(dirty_i); 5227351df4b2SJaegeuk Kim } 5228351df4b2SJaegeuk Kim 5229351df4b2SJaegeuk Kim static void destroy_curseg(struct f2fs_sb_info *sbi) 5230351df4b2SJaegeuk Kim { 5231351df4b2SJaegeuk Kim struct curseg_info *array = SM_I(sbi)->curseg_array; 5232351df4b2SJaegeuk Kim int i; 5233351df4b2SJaegeuk Kim 5234351df4b2SJaegeuk Kim if (!array) 5235351df4b2SJaegeuk Kim return; 5236351df4b2SJaegeuk Kim SM_I(sbi)->curseg_array = NULL; 5237b7ad7512SChao Yu for (i = 0; i < NR_CURSEG_TYPE; i++) { 5238c8eb7024SChao Yu kfree(array[i].sum_blk); 5239c8eb7024SChao Yu kfree(array[i].journal); 5240b7ad7512SChao Yu } 5241c8eb7024SChao Yu kfree(array); 5242351df4b2SJaegeuk Kim } 5243351df4b2SJaegeuk Kim 5244351df4b2SJaegeuk Kim static void destroy_free_segmap(struct f2fs_sb_info *sbi) 5245351df4b2SJaegeuk Kim { 5246351df4b2SJaegeuk Kim struct free_segmap_info *free_i = SM_I(sbi)->free_info; 5247*5f029c04SYi Zhuang 5248351df4b2SJaegeuk Kim if (!free_i) 5249351df4b2SJaegeuk Kim return; 5250351df4b2SJaegeuk Kim SM_I(sbi)->free_info = NULL; 525139307a8eSJaegeuk Kim kvfree(free_i->free_segmap); 525239307a8eSJaegeuk Kim kvfree(free_i->free_secmap); 5253c8eb7024SChao Yu kfree(free_i); 5254351df4b2SJaegeuk Kim } 5255351df4b2SJaegeuk Kim 5256351df4b2SJaegeuk Kim static void destroy_sit_info(struct f2fs_sb_info *sbi) 5257351df4b2SJaegeuk Kim { 5258351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 5259351df4b2SJaegeuk Kim 5260351df4b2SJaegeuk Kim if (!sit_i) 5261351df4b2SJaegeuk Kim return; 5262351df4b2SJaegeuk Kim 52632fde3dd1SChao Yu if (sit_i->sentries) 52642fde3dd1SChao Yu kvfree(sit_i->bitmap); 5265c8eb7024SChao Yu kfree(sit_i->tmp_map); 526660a3b782SJaegeuk Kim 526739307a8eSJaegeuk Kim kvfree(sit_i->sentries); 526839307a8eSJaegeuk Kim kvfree(sit_i->sec_entries); 526939307a8eSJaegeuk Kim kvfree(sit_i->dirty_sentries_bitmap); 5270351df4b2SJaegeuk Kim 5271351df4b2SJaegeuk Kim SM_I(sbi)->sit_info = NULL; 52725222595dSJaegeuk Kim kvfree(sit_i->sit_bitmap); 5273ae27d62eSChao Yu #ifdef CONFIG_F2FS_CHECK_FS 52745222595dSJaegeuk Kim kvfree(sit_i->sit_bitmap_mir); 5275bbf9f7d9SSahitya Tummala kvfree(sit_i->invalid_segmap); 5276ae27d62eSChao Yu #endif 5277c8eb7024SChao Yu kfree(sit_i); 5278351df4b2SJaegeuk Kim } 5279351df4b2SJaegeuk Kim 52804d57b86dSChao Yu void f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi) 5281351df4b2SJaegeuk Kim { 5282351df4b2SJaegeuk Kim struct f2fs_sm_info *sm_info = SM_I(sbi); 5283a688b9d9SGu Zheng 52843b03f724SChao Yu if (!sm_info) 52853b03f724SChao Yu return; 52864d57b86dSChao Yu f2fs_destroy_flush_cmd_control(sbi, true); 5287f099405fSChao Yu destroy_discard_cmd_control(sbi); 5288351df4b2SJaegeuk Kim destroy_dirty_segmap(sbi); 5289351df4b2SJaegeuk Kim destroy_curseg(sbi); 5290351df4b2SJaegeuk Kim destroy_free_segmap(sbi); 5291351df4b2SJaegeuk Kim destroy_sit_info(sbi); 5292351df4b2SJaegeuk Kim sbi->sm_info = NULL; 5293c8eb7024SChao Yu kfree(sm_info); 5294351df4b2SJaegeuk Kim } 52957fd9e544SJaegeuk Kim 52964d57b86dSChao Yu int __init f2fs_create_segment_manager_caches(void) 52977fd9e544SJaegeuk Kim { 529898510003SChao Yu discard_entry_slab = f2fs_kmem_cache_create("f2fs_discard_entry", 5299e8512d2eSGu Zheng sizeof(struct discard_entry)); 53007fd9e544SJaegeuk Kim if (!discard_entry_slab) 5301184a5cd2SChao Yu goto fail; 5302184a5cd2SChao Yu 530398510003SChao Yu discard_cmd_slab = f2fs_kmem_cache_create("f2fs_discard_cmd", 5304b01a9201SJaegeuk Kim sizeof(struct discard_cmd)); 5305b01a9201SJaegeuk Kim if (!discard_cmd_slab) 53066ab2a308SChao Yu goto destroy_discard_entry; 5307275b66b0SChao Yu 530898510003SChao Yu sit_entry_set_slab = f2fs_kmem_cache_create("f2fs_sit_entry_set", 5309c9ee0085SChangman Lee sizeof(struct sit_entry_set)); 5310184a5cd2SChao Yu if (!sit_entry_set_slab) 5311b01a9201SJaegeuk Kim goto destroy_discard_cmd; 531288b88a66SJaegeuk Kim 531398510003SChao Yu inmem_entry_slab = f2fs_kmem_cache_create("f2fs_inmem_page_entry", 531488b88a66SJaegeuk Kim sizeof(struct inmem_pages)); 531588b88a66SJaegeuk Kim if (!inmem_entry_slab) 531688b88a66SJaegeuk Kim goto destroy_sit_entry_set; 53177fd9e544SJaegeuk Kim return 0; 5318184a5cd2SChao Yu 531988b88a66SJaegeuk Kim destroy_sit_entry_set: 532088b88a66SJaegeuk Kim kmem_cache_destroy(sit_entry_set_slab); 5321b01a9201SJaegeuk Kim destroy_discard_cmd: 5322b01a9201SJaegeuk Kim kmem_cache_destroy(discard_cmd_slab); 53236ab2a308SChao Yu destroy_discard_entry: 5324184a5cd2SChao Yu kmem_cache_destroy(discard_entry_slab); 5325184a5cd2SChao Yu fail: 5326184a5cd2SChao Yu return -ENOMEM; 53277fd9e544SJaegeuk Kim } 53287fd9e544SJaegeuk Kim 53294d57b86dSChao Yu void f2fs_destroy_segment_manager_caches(void) 53307fd9e544SJaegeuk Kim { 5331184a5cd2SChao Yu kmem_cache_destroy(sit_entry_set_slab); 5332b01a9201SJaegeuk Kim kmem_cache_destroy(discard_cmd_slab); 53337fd9e544SJaegeuk Kim kmem_cache_destroy(discard_entry_slab); 533488b88a66SJaegeuk Kim kmem_cache_destroy(inmem_entry_slab); 53357fd9e544SJaegeuk Kim } 5336