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> 124034247aSNeilBrown #include <linux/sched/mm.h> 13690e4a3eSGeert Uytterhoeven #include <linux/prefetch.h> 146b4afdd7SJaegeuk Kim #include <linux/kthread.h> 1574de593aSChao Yu #include <linux/swap.h> 1660b99b48SJaegeuk Kim #include <linux/timer.h> 171d7be270SJaegeuk Kim #include <linux/freezer.h> 181eb1ef4aSJaegeuk Kim #include <linux/sched/signal.h> 196691d940SDaeho Jeong #include <linux/random.h> 20351df4b2SJaegeuk Kim 21351df4b2SJaegeuk Kim #include "f2fs.h" 22351df4b2SJaegeuk Kim #include "segment.h" 23351df4b2SJaegeuk Kim #include "node.h" 245f656541SJaegeuk Kim #include "gc.h" 2552118743SDaeho Jeong #include "iostat.h" 266ec178daSNamjae Jeon #include <trace/events/f2fs.h> 27351df4b2SJaegeuk Kim 289a7f143aSChangman Lee #define __reverse_ffz(x) __reverse_ffs(~(x)) 299a7f143aSChangman Lee 307fd9e544SJaegeuk Kim static struct kmem_cache *discard_entry_slab; 31b01a9201SJaegeuk Kim static struct kmem_cache *discard_cmd_slab; 32184a5cd2SChao Yu static struct kmem_cache *sit_entry_set_slab; 3388b88a66SJaegeuk Kim static struct kmem_cache *inmem_entry_slab; 347fd9e544SJaegeuk Kim 35f96999c3SJaegeuk Kim static unsigned long __reverse_ulong(unsigned char *str) 36f96999c3SJaegeuk Kim { 37f96999c3SJaegeuk Kim unsigned long tmp = 0; 38f96999c3SJaegeuk Kim int shift = 24, idx = 0; 39f96999c3SJaegeuk Kim 40f96999c3SJaegeuk Kim #if BITS_PER_LONG == 64 41f96999c3SJaegeuk Kim shift = 56; 42f96999c3SJaegeuk Kim #endif 43f96999c3SJaegeuk Kim while (shift >= 0) { 44f96999c3SJaegeuk Kim tmp |= (unsigned long)str[idx++] << shift; 45f96999c3SJaegeuk Kim shift -= BITS_PER_BYTE; 46f96999c3SJaegeuk Kim } 47f96999c3SJaegeuk Kim return tmp; 48f96999c3SJaegeuk Kim } 49f96999c3SJaegeuk Kim 509a7f143aSChangman Lee /* 519a7f143aSChangman Lee * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since 529a7f143aSChangman Lee * MSB and LSB are reversed in a byte by f2fs_set_bit. 539a7f143aSChangman Lee */ 549a7f143aSChangman Lee static inline unsigned long __reverse_ffs(unsigned long word) 559a7f143aSChangman Lee { 569a7f143aSChangman Lee int num = 0; 579a7f143aSChangman Lee 589a7f143aSChangman Lee #if BITS_PER_LONG == 64 59f96999c3SJaegeuk Kim if ((word & 0xffffffff00000000UL) == 0) 609a7f143aSChangman Lee num += 32; 61f96999c3SJaegeuk Kim else 629a7f143aSChangman Lee word >>= 32; 639a7f143aSChangman Lee #endif 64f96999c3SJaegeuk Kim if ((word & 0xffff0000) == 0) 659a7f143aSChangman Lee num += 16; 66f96999c3SJaegeuk Kim else 679a7f143aSChangman Lee word >>= 16; 68f96999c3SJaegeuk Kim 69f96999c3SJaegeuk Kim if ((word & 0xff00) == 0) 709a7f143aSChangman Lee num += 8; 71f96999c3SJaegeuk Kim else 729a7f143aSChangman Lee word >>= 8; 73f96999c3SJaegeuk Kim 749a7f143aSChangman Lee if ((word & 0xf0) == 0) 759a7f143aSChangman Lee num += 4; 769a7f143aSChangman Lee else 779a7f143aSChangman Lee word >>= 4; 78f96999c3SJaegeuk Kim 799a7f143aSChangman Lee if ((word & 0xc) == 0) 809a7f143aSChangman Lee num += 2; 819a7f143aSChangman Lee else 829a7f143aSChangman Lee word >>= 2; 83f96999c3SJaegeuk Kim 849a7f143aSChangman Lee if ((word & 0x2) == 0) 859a7f143aSChangman Lee num += 1; 869a7f143aSChangman Lee return num; 879a7f143aSChangman Lee } 889a7f143aSChangman Lee 899a7f143aSChangman Lee /* 90e1c42045Sarter97 * __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c because 919a7f143aSChangman Lee * f2fs_set_bit makes MSB and LSB reversed in a byte. 92692223d1SFan Li * @size must be integral times of unsigned long. 939a7f143aSChangman Lee * Example: 94f96999c3SJaegeuk Kim * MSB <--> LSB 95f96999c3SJaegeuk Kim * f2fs_set_bit(0, bitmap) => 1000 0000 96f96999c3SJaegeuk Kim * f2fs_set_bit(7, bitmap) => 0000 0001 979a7f143aSChangman Lee */ 989a7f143aSChangman Lee static unsigned long __find_rev_next_bit(const unsigned long *addr, 999a7f143aSChangman Lee unsigned long size, unsigned long offset) 1009a7f143aSChangman Lee { 1019a7f143aSChangman Lee const unsigned long *p = addr + BIT_WORD(offset); 102692223d1SFan Li unsigned long result = size; 1039a7f143aSChangman Lee unsigned long tmp; 1049a7f143aSChangman Lee 1059a7f143aSChangman Lee if (offset >= size) 1069a7f143aSChangman Lee return size; 1079a7f143aSChangman Lee 108692223d1SFan Li size -= (offset & ~(BITS_PER_LONG - 1)); 1099a7f143aSChangman Lee offset %= BITS_PER_LONG; 110692223d1SFan Li 111692223d1SFan Li while (1) { 112692223d1SFan Li if (*p == 0) 113692223d1SFan Li goto pass; 1149a7f143aSChangman Lee 115f96999c3SJaegeuk Kim tmp = __reverse_ulong((unsigned char *)p); 116692223d1SFan Li 117f96999c3SJaegeuk Kim tmp &= ~0UL >> offset; 1189a7f143aSChangman Lee if (size < BITS_PER_LONG) 119692223d1SFan Li tmp &= (~0UL << (BITS_PER_LONG - size)); 1209a7f143aSChangman Lee if (tmp) 121692223d1SFan Li goto found; 122692223d1SFan Li pass: 123692223d1SFan Li if (size <= BITS_PER_LONG) 124692223d1SFan Li break; 1259a7f143aSChangman Lee size -= BITS_PER_LONG; 126692223d1SFan Li offset = 0; 127f96999c3SJaegeuk Kim p++; 1289a7f143aSChangman Lee } 1299a7f143aSChangman Lee return result; 130692223d1SFan Li found: 131692223d1SFan Li return result - size + __reverse_ffs(tmp); 1329a7f143aSChangman Lee } 1339a7f143aSChangman Lee 1349a7f143aSChangman Lee static unsigned long __find_rev_next_zero_bit(const unsigned long *addr, 1359a7f143aSChangman Lee unsigned long size, unsigned long offset) 1369a7f143aSChangman Lee { 1379a7f143aSChangman Lee const unsigned long *p = addr + BIT_WORD(offset); 13880609448SJaegeuk Kim unsigned long result = size; 1399a7f143aSChangman Lee unsigned long tmp; 1409a7f143aSChangman Lee 1419a7f143aSChangman Lee if (offset >= size) 1429a7f143aSChangman Lee return size; 1439a7f143aSChangman Lee 14480609448SJaegeuk Kim size -= (offset & ~(BITS_PER_LONG - 1)); 1459a7f143aSChangman Lee offset %= BITS_PER_LONG; 14680609448SJaegeuk Kim 14780609448SJaegeuk Kim while (1) { 14880609448SJaegeuk Kim if (*p == ~0UL) 14980609448SJaegeuk Kim goto pass; 1509a7f143aSChangman Lee 151f96999c3SJaegeuk Kim tmp = __reverse_ulong((unsigned char *)p); 152f96999c3SJaegeuk Kim 15380609448SJaegeuk Kim if (offset) 15480609448SJaegeuk Kim tmp |= ~0UL << (BITS_PER_LONG - offset); 1559a7f143aSChangman Lee if (size < BITS_PER_LONG) 15680609448SJaegeuk Kim tmp |= ~0UL >> size; 157f96999c3SJaegeuk Kim if (tmp != ~0UL) 15880609448SJaegeuk Kim goto found; 15980609448SJaegeuk Kim pass: 16080609448SJaegeuk Kim if (size <= BITS_PER_LONG) 16180609448SJaegeuk Kim break; 1629a7f143aSChangman Lee size -= BITS_PER_LONG; 16380609448SJaegeuk Kim offset = 0; 164f96999c3SJaegeuk Kim p++; 1659a7f143aSChangman Lee } 1669a7f143aSChangman Lee return result; 16780609448SJaegeuk Kim found: 16880609448SJaegeuk Kim return result - size + __reverse_ffz(tmp); 1699a7f143aSChangman Lee } 1709a7f143aSChangman Lee 1714d57b86dSChao Yu bool f2fs_need_SSR(struct f2fs_sb_info *sbi) 172b3a97a2aSJaegeuk Kim { 173b3a97a2aSJaegeuk Kim int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); 174b3a97a2aSJaegeuk Kim int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); 175b3a97a2aSJaegeuk Kim int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); 176b3a97a2aSJaegeuk Kim 177b0332a0fSChao Yu if (f2fs_lfs_mode(sbi)) 178b3a97a2aSJaegeuk Kim return false; 1790e5e8111SDaeho Jeong if (sbi->gc_mode == GC_URGENT_HIGH) 180b3a97a2aSJaegeuk Kim return true; 1814354994fSDaniel Rosenberg if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 1824354994fSDaniel Rosenberg return true; 183b3a97a2aSJaegeuk Kim 184b3a97a2aSJaegeuk Kim return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + 185a2a12b67SChao Yu SM_I(sbi)->min_ssr_sections + reserved_sections(sbi)); 186b3a97a2aSJaegeuk Kim } 187b3a97a2aSJaegeuk Kim 1884d57b86dSChao Yu void f2fs_register_inmem_page(struct inode *inode, struct page *page) 18988b88a66SJaegeuk Kim { 19088b88a66SJaegeuk Kim struct inmem_pages *new; 1919be32d72SJaegeuk Kim 192b763f3beSChao Yu set_page_private_atomic(page); 193decd36b6SChao Yu 19432410577SChao Yu new = f2fs_kmem_cache_alloc(inmem_entry_slab, 19532410577SChao Yu GFP_NOFS, true, NULL); 19688b88a66SJaegeuk Kim 19788b88a66SJaegeuk Kim /* add atomic page indices to the list */ 19888b88a66SJaegeuk Kim new->page = page; 19988b88a66SJaegeuk Kim INIT_LIST_HEAD(&new->list); 200decd36b6SChao Yu 20188b88a66SJaegeuk Kim /* increase reference count with clean state */ 20288b88a66SJaegeuk Kim get_page(page); 203743b620cSJaegeuk Kim mutex_lock(&F2FS_I(inode)->inmem_lock); 204743b620cSJaegeuk Kim list_add_tail(&new->list, &F2FS_I(inode)->inmem_pages); 2058dcf2ff7SJaegeuk Kim inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); 206743b620cSJaegeuk Kim mutex_unlock(&F2FS_I(inode)->inmem_lock); 2078ce67cb0SJaegeuk Kim 2088ce67cb0SJaegeuk Kim trace_f2fs_register_inmem_page(page, INMEM); 20988b88a66SJaegeuk Kim } 21088b88a66SJaegeuk Kim 21128bc106bSChao Yu static int __revoke_inmem_pages(struct inode *inode, 21248432984SChao Yu struct list_head *head, bool drop, bool recover, 21348432984SChao Yu bool trylock) 21429b96b54SChao Yu { 21528bc106bSChao Yu struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 21629b96b54SChao Yu struct inmem_pages *cur, *tmp; 21728bc106bSChao Yu int err = 0; 21829b96b54SChao Yu 21929b96b54SChao Yu list_for_each_entry_safe(cur, tmp, head, list) { 22028bc106bSChao Yu struct page *page = cur->page; 22129b96b54SChao Yu 22228bc106bSChao Yu if (drop) 22328bc106bSChao Yu trace_f2fs_commit_inmem_page(page, INMEM_DROP); 22428bc106bSChao Yu 22548432984SChao Yu if (trylock) { 22648432984SChao Yu /* 22748432984SChao Yu * to avoid deadlock in between page lock and 22848432984SChao Yu * inmem_lock. 22948432984SChao Yu */ 23048432984SChao Yu if (!trylock_page(page)) 23148432984SChao Yu continue; 23248432984SChao Yu } else { 23328bc106bSChao Yu lock_page(page); 23448432984SChao Yu } 23528bc106bSChao Yu 236bae0ee7aSChao Yu f2fs_wait_on_page_writeback(page, DATA, true, true); 237e5e5732dSChao Yu 23828bc106bSChao Yu if (recover) { 23928bc106bSChao Yu struct dnode_of_data dn; 24028bc106bSChao Yu struct node_info ni; 24128bc106bSChao Yu 24228bc106bSChao Yu trace_f2fs_commit_inmem_page(page, INMEM_REVOKE); 2437f2b4e8eSChao Yu retry: 24428bc106bSChao Yu set_new_dnode(&dn, inode, NULL, NULL, 0); 2454d57b86dSChao Yu err = f2fs_get_dnode_of_data(&dn, page->index, 2464d57b86dSChao Yu LOOKUP_NODE); 2477f2b4e8eSChao Yu if (err) { 2487f2b4e8eSChao Yu if (err == -ENOMEM) { 2494034247aSNeilBrown memalloc_retry_wait(GFP_NOFS); 2507f2b4e8eSChao Yu goto retry; 2517f2b4e8eSChao Yu } 25228bc106bSChao Yu err = -EAGAIN; 25328bc106bSChao Yu goto next; 25428bc106bSChao Yu } 2557735730dSChao Yu 256a9419b63SJaegeuk Kim err = f2fs_get_node_info(sbi, dn.nid, &ni, false); 2577735730dSChao Yu if (err) { 2587735730dSChao Yu f2fs_put_dnode(&dn); 2597735730dSChao Yu return err; 2607735730dSChao Yu } 2617735730dSChao Yu 262f1d2564aSDaeho Jeong if (cur->old_addr == NEW_ADDR) { 2634d57b86dSChao Yu f2fs_invalidate_blocks(sbi, dn.data_blkaddr); 264f1d2564aSDaeho Jeong f2fs_update_data_blkaddr(&dn, NEW_ADDR); 265f1d2564aSDaeho Jeong } else 26628bc106bSChao Yu f2fs_replace_block(sbi, &dn, dn.data_blkaddr, 26728bc106bSChao Yu cur->old_addr, ni.version, true, true); 26828bc106bSChao Yu f2fs_put_dnode(&dn); 26928bc106bSChao Yu } 27028bc106bSChao Yu next: 27163c52d78SJaegeuk Kim /* we don't need to invalidate this in the sccessful status */ 2722baf0781SChao Yu if (drop || recover) { 27328bc106bSChao Yu ClearPageUptodate(page); 274b763f3beSChao Yu clear_page_private_gcing(page); 2752baf0781SChao Yu } 276b763f3beSChao Yu detach_page_private(page); 277b763f3beSChao Yu set_page_private(page, 0); 27828bc106bSChao Yu f2fs_put_page(page, 1); 27929b96b54SChao Yu 28029b96b54SChao Yu list_del(&cur->list); 28129b96b54SChao Yu kmem_cache_free(inmem_entry_slab, cur); 28229b96b54SChao Yu dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); 28329b96b54SChao Yu } 28428bc106bSChao Yu return err; 28529b96b54SChao Yu } 28629b96b54SChao Yu 2874d57b86dSChao Yu void f2fs_drop_inmem_pages_all(struct f2fs_sb_info *sbi, bool gc_failure) 28857864ae5SJaegeuk Kim { 28957864ae5SJaegeuk Kim struct list_head *head = &sbi->inode_list[ATOMIC_FILE]; 29057864ae5SJaegeuk Kim struct inode *inode; 29157864ae5SJaegeuk Kim struct f2fs_inode_info *fi; 292677017d1SSahitya Tummala unsigned int count = sbi->atomic_files; 293677017d1SSahitya Tummala unsigned int looped = 0; 29457864ae5SJaegeuk Kim next: 29557864ae5SJaegeuk Kim spin_lock(&sbi->inode_lock[ATOMIC_FILE]); 29657864ae5SJaegeuk Kim if (list_empty(head)) { 29757864ae5SJaegeuk Kim spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 29857864ae5SJaegeuk Kim return; 29957864ae5SJaegeuk Kim } 30057864ae5SJaegeuk Kim fi = list_first_entry(head, struct f2fs_inode_info, inmem_ilist); 30157864ae5SJaegeuk Kim inode = igrab(&fi->vfs_inode); 302677017d1SSahitya Tummala if (inode) 303677017d1SSahitya Tummala list_move_tail(&fi->inmem_ilist, head); 30457864ae5SJaegeuk Kim spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 30557864ae5SJaegeuk Kim 30657864ae5SJaegeuk Kim if (inode) { 3072ef79ecbSChao Yu if (gc_failure) { 308677017d1SSahitya Tummala if (!fi->i_gc_failures[GC_FAILURE_ATOMIC]) 3092ef79ecbSChao Yu goto skip; 3102ef79ecbSChao Yu } 3112ef79ecbSChao Yu set_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST); 3124d57b86dSChao Yu f2fs_drop_inmem_pages(inode); 313677017d1SSahitya Tummala skip: 31457864ae5SJaegeuk Kim iput(inode); 31557864ae5SJaegeuk Kim } 3165df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, DEFAULT_IO_TIMEOUT); 31757864ae5SJaegeuk Kim cond_resched(); 318677017d1SSahitya Tummala if (gc_failure) { 319677017d1SSahitya Tummala if (++looped >= count) 320677017d1SSahitya Tummala return; 321677017d1SSahitya Tummala } 32257864ae5SJaegeuk Kim goto next; 32357864ae5SJaegeuk Kim } 32457864ae5SJaegeuk Kim 3254d57b86dSChao Yu void f2fs_drop_inmem_pages(struct inode *inode) 32629b96b54SChao Yu { 32757864ae5SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 32829b96b54SChao Yu struct f2fs_inode_info *fi = F2FS_I(inode); 32929b96b54SChao Yu 330be1ee45dSYi Zhuang do { 33129b96b54SChao Yu mutex_lock(&fi->inmem_lock); 332be1ee45dSYi Zhuang if (list_empty(&fi->inmem_pages)) { 3332ef79ecbSChao Yu fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0; 334743b620cSJaegeuk Kim 335743b620cSJaegeuk Kim spin_lock(&sbi->inode_lock[ATOMIC_FILE]); 336743b620cSJaegeuk Kim if (!list_empty(&fi->inmem_ilist)) 337743b620cSJaegeuk Kim list_del_init(&fi->inmem_ilist); 338677017d1SSahitya Tummala if (f2fs_is_atomic_file(inode)) { 339677017d1SSahitya Tummala clear_inode_flag(inode, FI_ATOMIC_FILE); 340677017d1SSahitya Tummala sbi->atomic_files--; 341677017d1SSahitya Tummala } 342743b620cSJaegeuk Kim spin_unlock(&sbi->inode_lock[ATOMIC_FILE]); 343be1ee45dSYi Zhuang 344be1ee45dSYi Zhuang mutex_unlock(&fi->inmem_lock); 345be1ee45dSYi Zhuang break; 346be1ee45dSYi Zhuang } 347be1ee45dSYi Zhuang __revoke_inmem_pages(inode, &fi->inmem_pages, 348be1ee45dSYi Zhuang true, false, true); 349be1ee45dSYi Zhuang mutex_unlock(&fi->inmem_lock); 350be1ee45dSYi Zhuang } while (1); 35129b96b54SChao Yu } 35229b96b54SChao Yu 3534d57b86dSChao Yu void f2fs_drop_inmem_page(struct inode *inode, struct page *page) 3548c242db9SJaegeuk Kim { 3558c242db9SJaegeuk Kim struct f2fs_inode_info *fi = F2FS_I(inode); 3568c242db9SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 3578c242db9SJaegeuk Kim struct list_head *head = &fi->inmem_pages; 3588c242db9SJaegeuk Kim struct inmem_pages *cur = NULL; 3598c242db9SJaegeuk Kim 360b763f3beSChao Yu f2fs_bug_on(sbi, !page_private_atomic(page)); 3618c242db9SJaegeuk Kim 3628c242db9SJaegeuk Kim mutex_lock(&fi->inmem_lock); 3638c242db9SJaegeuk Kim list_for_each_entry(cur, head, list) { 3648c242db9SJaegeuk Kim if (cur->page == page) 3658c242db9SJaegeuk Kim break; 3668c242db9SJaegeuk Kim } 3678c242db9SJaegeuk Kim 368d0891e84SSheng Yong f2fs_bug_on(sbi, list_empty(head) || cur->page != page); 3698c242db9SJaegeuk Kim list_del(&cur->list); 3708c242db9SJaegeuk Kim mutex_unlock(&fi->inmem_lock); 3718c242db9SJaegeuk Kim 3728c242db9SJaegeuk Kim dec_page_count(sbi, F2FS_INMEM_PAGES); 3738c242db9SJaegeuk Kim kmem_cache_free(inmem_entry_slab, cur); 3748c242db9SJaegeuk Kim 3758c242db9SJaegeuk Kim ClearPageUptodate(page); 376b763f3beSChao Yu clear_page_private_atomic(page); 3778c242db9SJaegeuk Kim f2fs_put_page(page, 0); 3788c242db9SJaegeuk Kim 379b763f3beSChao Yu detach_page_private(page); 380b763f3beSChao Yu set_page_private(page, 0); 381b763f3beSChao Yu 3828c242db9SJaegeuk Kim trace_f2fs_commit_inmem_page(page, INMEM_INVALIDATE); 3838c242db9SJaegeuk Kim } 3848c242db9SJaegeuk Kim 3854d57b86dSChao Yu static int __f2fs_commit_inmem_pages(struct inode *inode) 38688b88a66SJaegeuk Kim { 38788b88a66SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 38888b88a66SJaegeuk Kim struct f2fs_inode_info *fi = F2FS_I(inode); 38988b88a66SJaegeuk Kim struct inmem_pages *cur, *tmp; 39088b88a66SJaegeuk Kim struct f2fs_io_info fio = { 39105ca3632SJaegeuk Kim .sbi = sbi, 39239d787beSChao Yu .ino = inode->i_ino, 39388b88a66SJaegeuk Kim .type = DATA, 39404d328deSMike Christie .op = REQ_OP_WRITE, 39570fd7614SChristoph Hellwig .op_flags = REQ_SYNC | REQ_PRIO, 396b0af6d49SChao Yu .io_type = FS_DATA_IO, 39788b88a66SJaegeuk Kim }; 398cf52b27aSChao Yu struct list_head revoke_list; 399bab475c5SChao Yu bool submit_bio = false; 400edb27deeSJaegeuk Kim int err = 0; 40188b88a66SJaegeuk Kim 402cf52b27aSChao Yu INIT_LIST_HEAD(&revoke_list); 403cf52b27aSChao Yu 40488b88a66SJaegeuk Kim list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) { 40528bc106bSChao Yu struct page *page = cur->page; 40628bc106bSChao Yu 40728bc106bSChao Yu lock_page(page); 40828bc106bSChao Yu if (page->mapping == inode->i_mapping) { 40928bc106bSChao Yu trace_f2fs_commit_inmem_page(page, INMEM); 41028bc106bSChao Yu 411bae0ee7aSChao Yu f2fs_wait_on_page_writeback(page, DATA, true, true); 4128d64d365SChao Yu 4138d64d365SChao Yu set_page_dirty(page); 414933439c8SChao Yu if (clear_page_dirty_for_io(page)) { 41588b88a66SJaegeuk Kim inode_dec_dirty_pages(inode); 4164d57b86dSChao Yu f2fs_remove_dirty_inode(inode); 417933439c8SChao Yu } 418640cc189SJaegeuk Kim retry: 41928bc106bSChao Yu fio.page = page; 420e959c8f5SHou Pengyang fio.old_blkaddr = NULL_ADDR; 4214d978078SJaegeuk Kim fio.encrypted_page = NULL; 422cc15620bSJaegeuk Kim fio.need_lock = LOCK_DONE; 4234d57b86dSChao Yu err = f2fs_do_write_data_page(&fio); 424edb27deeSJaegeuk Kim if (err) { 425640cc189SJaegeuk Kim if (err == -ENOMEM) { 4264034247aSNeilBrown memalloc_retry_wait(GFP_NOFS); 427640cc189SJaegeuk Kim goto retry; 428640cc189SJaegeuk Kim } 42928bc106bSChao Yu unlock_page(page); 430edb27deeSJaegeuk Kim break; 431edb27deeSJaegeuk Kim } 43228bc106bSChao Yu /* record old blkaddr for revoking */ 43328bc106bSChao Yu cur->old_addr = fio.old_blkaddr; 434bab475c5SChao Yu submit_bio = true; 43588b88a66SJaegeuk Kim } 43628bc106bSChao Yu unlock_page(page); 437cf52b27aSChao Yu list_move_tail(&cur->list, &revoke_list); 43888b88a66SJaegeuk Kim } 43929b96b54SChao Yu 440bab475c5SChao Yu if (submit_bio) 441bab475c5SChao Yu f2fs_submit_merged_write_cond(sbi, inode, NULL, 0, DATA); 44228bc106bSChao Yu 44328bc106bSChao Yu if (err) { 44428bc106bSChao Yu /* 44528bc106bSChao Yu * try to revoke all committed pages, but still we could fail 44628bc106bSChao Yu * due to no memory or other reason, if that happened, EAGAIN 44728bc106bSChao Yu * will be returned, which means in such case, transaction is 44828bc106bSChao Yu * already not integrity, caller should use journal to do the 44928bc106bSChao Yu * recovery or rewrite & commit last transaction. For other 45028bc106bSChao Yu * error number, revoking was done by filesystem itself. 45128bc106bSChao Yu */ 45248432984SChao Yu err = __revoke_inmem_pages(inode, &revoke_list, 45348432984SChao Yu false, true, false); 45428bc106bSChao Yu 45528bc106bSChao Yu /* drop all uncommitted pages */ 45648432984SChao Yu __revoke_inmem_pages(inode, &fi->inmem_pages, 45748432984SChao Yu true, false, false); 458cf52b27aSChao Yu } else { 45948432984SChao Yu __revoke_inmem_pages(inode, &revoke_list, 46048432984SChao Yu false, false, false); 46128bc106bSChao Yu } 462cf52b27aSChao Yu 463cf52b27aSChao Yu return err; 464cf52b27aSChao Yu } 465cf52b27aSChao Yu 4664d57b86dSChao Yu int f2fs_commit_inmem_pages(struct inode *inode) 467cf52b27aSChao Yu { 468cf52b27aSChao Yu struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 469cf52b27aSChao Yu struct f2fs_inode_info *fi = F2FS_I(inode); 470cf52b27aSChao Yu int err; 471cf52b27aSChao Yu 472cf52b27aSChao Yu f2fs_balance_fs(sbi, true); 473cf52b27aSChao Yu 474e4544b63STim Murray f2fs_down_write(&fi->i_gc_rwsem[WRITE]); 4756f8d4455SJaegeuk Kim 4766f8d4455SJaegeuk Kim f2fs_lock_op(sbi); 477cf52b27aSChao Yu set_inode_flag(inode, FI_ATOMIC_COMMIT); 478cf52b27aSChao Yu 479cf52b27aSChao Yu mutex_lock(&fi->inmem_lock); 4804d57b86dSChao Yu err = __f2fs_commit_inmem_pages(inode); 48188b88a66SJaegeuk Kim mutex_unlock(&fi->inmem_lock); 48288b88a66SJaegeuk Kim 4835fe45743SChao Yu clear_inode_flag(inode, FI_ATOMIC_COMMIT); 4845fe45743SChao Yu 48588b88a66SJaegeuk Kim f2fs_unlock_op(sbi); 486e4544b63STim Murray f2fs_up_write(&fi->i_gc_rwsem[WRITE]); 4876f8d4455SJaegeuk Kim 488edb27deeSJaegeuk Kim return err; 48988b88a66SJaegeuk Kim } 49088b88a66SJaegeuk Kim 4910a8165d7SJaegeuk Kim /* 492351df4b2SJaegeuk Kim * This function balances dirty node and dentry pages. 493351df4b2SJaegeuk Kim * In addition, it controls garbage collection. 494351df4b2SJaegeuk Kim */ 4952c4db1a6SJaegeuk Kim void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) 496351df4b2SJaegeuk Kim { 49755523519SChao Yu if (time_to_inject(sbi, FAULT_CHECKPOINT)) { 498c45d6002SChao Yu f2fs_show_injection_info(sbi, FAULT_CHECKPOINT); 4990f348028SChao Yu f2fs_stop_checkpoint(sbi, false); 50055523519SChao Yu } 5010f348028SChao Yu 502e589c2c4SJaegeuk Kim /* balance_fs_bg is able to be pending */ 503a7881893SJaegeuk Kim if (need && excess_cached_nats(sbi)) 5047bcd0cfaSChao Yu f2fs_balance_fs_bg(sbi, false); 505e589c2c4SJaegeuk Kim 50600e09c0bSChao Yu if (!f2fs_is_checkpoint_ready(sbi)) 5074354994fSDaniel Rosenberg return; 5084354994fSDaniel Rosenberg 509351df4b2SJaegeuk Kim /* 510029cd28cSJaegeuk Kim * We should do GC or end up with checkpoint, if there are so many dirty 511029cd28cSJaegeuk Kim * dir/node pages without enough free segments. 512351df4b2SJaegeuk Kim */ 5137f3037a5SJaegeuk Kim if (has_not_enough_free_secs(sbi, 0, 0)) { 5145911d2d1SChao Yu if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && 5155911d2d1SChao Yu sbi->gc_thread->f2fs_gc_task) { 5165911d2d1SChao Yu DEFINE_WAIT(wait); 5175911d2d1SChao Yu 5185911d2d1SChao Yu prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait, 5195911d2d1SChao Yu TASK_UNINTERRUPTIBLE); 5205911d2d1SChao Yu wake_up(&sbi->gc_thread->gc_wait_queue_head); 5215911d2d1SChao Yu io_schedule(); 5225911d2d1SChao Yu finish_wait(&sbi->gc_thread->fggc_wq, &wait); 5235911d2d1SChao Yu } else { 524e4544b63STim Murray f2fs_down_write(&sbi->gc_lock); 5257dede886SChao Yu f2fs_gc(sbi, false, false, false, NULL_SEGNO); 526351df4b2SJaegeuk Kim } 527351df4b2SJaegeuk Kim } 528351df4b2SJaegeuk Kim } 529351df4b2SJaegeuk Kim 530287b1406SChao Yu static inline bool excess_dirty_threshold(struct f2fs_sb_info *sbi) 531287b1406SChao Yu { 532e4544b63STim Murray int factor = f2fs_rwsem_is_locked(&sbi->cp_rwsem) ? 3 : 2; 533287b1406SChao Yu unsigned int dents = get_pages(sbi, F2FS_DIRTY_DENTS); 534287b1406SChao Yu unsigned int qdata = get_pages(sbi, F2FS_DIRTY_QDATA); 535287b1406SChao Yu unsigned int nodes = get_pages(sbi, F2FS_DIRTY_NODES); 536287b1406SChao Yu unsigned int meta = get_pages(sbi, F2FS_DIRTY_META); 537287b1406SChao Yu unsigned int imeta = get_pages(sbi, F2FS_DIRTY_IMETA); 538287b1406SChao Yu unsigned int threshold = sbi->blocks_per_seg * factor * 539287b1406SChao Yu DEFAULT_DIRTY_THRESHOLD; 540287b1406SChao Yu unsigned int global_threshold = threshold * 3 / 2; 541287b1406SChao Yu 542287b1406SChao Yu if (dents >= threshold || qdata >= threshold || 543287b1406SChao Yu nodes >= threshold || meta >= threshold || 544287b1406SChao Yu imeta >= threshold) 545287b1406SChao Yu return true; 546287b1406SChao Yu return dents + qdata + nodes + meta + imeta > global_threshold; 547287b1406SChao Yu } 548287b1406SChao Yu 5497bcd0cfaSChao Yu void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi, bool from_bg) 5504660f9c0SJaegeuk Kim { 55164c74a7aSChao Yu if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) 55264c74a7aSChao Yu return; 55364c74a7aSChao Yu 5541dcc336bSChao Yu /* try to shrink extent cache when there is no enough memory */ 5554d57b86dSChao Yu if (!f2fs_available_free_memory(sbi, EXTENT_CACHE)) 5561dcc336bSChao Yu f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER); 5571dcc336bSChao Yu 5581b38dc8eSJaegeuk Kim /* check the # of cached NAT entries */ 5594d57b86dSChao Yu if (!f2fs_available_free_memory(sbi, NAT_ENTRIES)) 5604d57b86dSChao Yu f2fs_try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK); 5611b38dc8eSJaegeuk Kim 5624d57b86dSChao Yu if (!f2fs_available_free_memory(sbi, FREE_NIDS)) 5634d57b86dSChao Yu f2fs_try_to_free_nids(sbi, MAX_FREE_NIDS); 564ad4edb83SJaegeuk Kim else 5654d57b86dSChao Yu f2fs_build_free_nids(sbi, false, false); 56631696580SChao Yu 567287b1406SChao Yu if (excess_dirty_nats(sbi) || excess_dirty_threshold(sbi) || 568287b1406SChao Yu excess_prefree_segs(sbi) || !f2fs_space_for_roll_forward(sbi)) 569493720a4SChao Yu goto do_sync; 570493720a4SChao Yu 571493720a4SChao Yu /* there is background inflight IO or foreground operation recently */ 572493720a4SChao Yu if (is_inflight_io(sbi, REQ_TIME) || 573e4544b63STim Murray (!f2fs_time_over(sbi, REQ_TIME) && f2fs_rwsem_is_locked(&sbi->cp_rwsem))) 574f455c8a5SJaegeuk Kim return; 575e5e7ea3cSJaegeuk Kim 576493720a4SChao Yu /* exceed periodical checkpoint timeout threshold */ 577493720a4SChao Yu if (f2fs_time_over(sbi, CP_TIME)) 578493720a4SChao Yu goto do_sync; 579493720a4SChao Yu 5804660f9c0SJaegeuk Kim /* checkpoint is the only way to shrink partial cached entries */ 581cd6d697aSChao Yu if (f2fs_available_free_memory(sbi, NAT_ENTRIES) && 582493720a4SChao Yu f2fs_available_free_memory(sbi, INO_ENTRIES)) 583493720a4SChao Yu return; 584493720a4SChao Yu 585493720a4SChao Yu do_sync: 5867bcd0cfaSChao Yu if (test_opt(sbi, DATA_FLUSH) && from_bg) { 587e9f5b8b8SChao Yu struct blk_plug plug; 588e9f5b8b8SChao Yu 589040d2bb3SChao Yu mutex_lock(&sbi->flush_lock); 590040d2bb3SChao Yu 591e9f5b8b8SChao Yu blk_start_plug(&plug); 5924d57b86dSChao Yu f2fs_sync_dirty_inodes(sbi, FILE_INODE); 593e9f5b8b8SChao Yu blk_finish_plug(&plug); 594040d2bb3SChao Yu 595040d2bb3SChao Yu mutex_unlock(&sbi->flush_lock); 596e9f5b8b8SChao Yu } 5974660f9c0SJaegeuk Kim f2fs_sync_fs(sbi->sb, true); 59842190d2aSJaegeuk Kim stat_inc_bg_cp_count(sbi->stat_info); 5994660f9c0SJaegeuk Kim } 6004660f9c0SJaegeuk Kim 60120fda56bSKinglong Mee static int __submit_flush_wait(struct f2fs_sb_info *sbi, 60220fda56bSKinglong Mee struct block_device *bdev) 6033c62be17SJaegeuk Kim { 60425ac8426SChristoph Hellwig int ret = blkdev_issue_flush(bdev); 60520fda56bSKinglong Mee 60620fda56bSKinglong Mee trace_f2fs_issue_flush(bdev, test_opt(sbi, NOBARRIER), 60720fda56bSKinglong Mee test_opt(sbi, FLUSH_MERGE), ret); 6083c62be17SJaegeuk Kim return ret; 6093c62be17SJaegeuk Kim } 6103c62be17SJaegeuk Kim 61139d787beSChao Yu static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino) 6123c62be17SJaegeuk Kim { 61339d787beSChao Yu int ret = 0; 6143c62be17SJaegeuk Kim int i; 6153c62be17SJaegeuk Kim 6160916878dSDamien Le Moal if (!f2fs_is_multi_device(sbi)) 61739d787beSChao Yu return __submit_flush_wait(sbi, sbi->sb->s_bdev); 61820fda56bSKinglong Mee 61939d787beSChao Yu for (i = 0; i < sbi->s_ndevs; i++) { 6204d57b86dSChao Yu if (!f2fs_is_dirty_device(sbi, ino, i, FLUSH_INO)) 62139d787beSChao Yu continue; 62220fda56bSKinglong Mee ret = __submit_flush_wait(sbi, FDEV(i).bdev); 6233c62be17SJaegeuk Kim if (ret) 6243c62be17SJaegeuk Kim break; 6253c62be17SJaegeuk Kim } 6263c62be17SJaegeuk Kim return ret; 6273c62be17SJaegeuk Kim } 6283c62be17SJaegeuk Kim 6292163d198SGu Zheng static int issue_flush_thread(void *data) 6306b4afdd7SJaegeuk Kim { 6316b4afdd7SJaegeuk Kim struct f2fs_sb_info *sbi = data; 632b01a9201SJaegeuk Kim struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 633a688b9d9SGu Zheng wait_queue_head_t *q = &fcc->flush_wait_queue; 6346b4afdd7SJaegeuk Kim repeat: 6356b4afdd7SJaegeuk Kim if (kthread_should_stop()) 6366b4afdd7SJaegeuk Kim return 0; 6376b4afdd7SJaegeuk Kim 638721bd4d5SGu Zheng if (!llist_empty(&fcc->issue_list)) { 6396b4afdd7SJaegeuk Kim struct flush_cmd *cmd, *next; 6406b4afdd7SJaegeuk Kim int ret; 6416b4afdd7SJaegeuk Kim 642721bd4d5SGu Zheng fcc->dispatch_list = llist_del_all(&fcc->issue_list); 643721bd4d5SGu Zheng fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); 644721bd4d5SGu Zheng 64539d787beSChao Yu cmd = llist_entry(fcc->dispatch_list, struct flush_cmd, llnode); 64639d787beSChao Yu 64739d787beSChao Yu ret = submit_flush_wait(sbi, cmd->ino); 6488b8dd65fSChao Yu atomic_inc(&fcc->issued_flush); 6498b8dd65fSChao Yu 650721bd4d5SGu Zheng llist_for_each_entry_safe(cmd, next, 651721bd4d5SGu Zheng fcc->dispatch_list, llnode) { 6526b4afdd7SJaegeuk Kim cmd->ret = ret; 6536b4afdd7SJaegeuk Kim complete(&cmd->wait); 6546b4afdd7SJaegeuk Kim } 655a688b9d9SGu Zheng fcc->dispatch_list = NULL; 6566b4afdd7SJaegeuk Kim } 6576b4afdd7SJaegeuk Kim 658a688b9d9SGu Zheng wait_event_interruptible(*q, 659721bd4d5SGu Zheng kthread_should_stop() || !llist_empty(&fcc->issue_list)); 6606b4afdd7SJaegeuk Kim goto repeat; 6616b4afdd7SJaegeuk Kim } 6626b4afdd7SJaegeuk Kim 66339d787beSChao Yu int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino) 6646b4afdd7SJaegeuk Kim { 665b01a9201SJaegeuk Kim struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 666adf8d90bSChao Yu struct flush_cmd cmd; 6678b8dd65fSChao Yu int ret; 6686b4afdd7SJaegeuk Kim 6690f7b2abdSJaegeuk Kim if (test_opt(sbi, NOBARRIER)) 6700f7b2abdSJaegeuk Kim return 0; 6710f7b2abdSJaegeuk Kim 6728b8dd65fSChao Yu if (!test_opt(sbi, FLUSH_MERGE)) { 67372691af6SJaegeuk Kim atomic_inc(&fcc->queued_flush); 67439d787beSChao Yu ret = submit_flush_wait(sbi, ino); 67572691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 6768b8dd65fSChao Yu atomic_inc(&fcc->issued_flush); 6778b8dd65fSChao Yu return ret; 6788b8dd65fSChao Yu } 6798b8dd65fSChao Yu 6800916878dSDamien Le Moal if (atomic_inc_return(&fcc->queued_flush) == 1 || 6810916878dSDamien Le Moal f2fs_is_multi_device(sbi)) { 68239d787beSChao Yu ret = submit_flush_wait(sbi, ino); 68372691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 6848b8dd65fSChao Yu 6858b8dd65fSChao Yu atomic_inc(&fcc->issued_flush); 686740432f8SJaegeuk Kim return ret; 687740432f8SJaegeuk Kim } 6886b4afdd7SJaegeuk Kim 68939d787beSChao Yu cmd.ino = ino; 690adf8d90bSChao Yu init_completion(&cmd.wait); 6916b4afdd7SJaegeuk Kim 692721bd4d5SGu Zheng llist_add(&cmd.llnode, &fcc->issue_list); 6936b4afdd7SJaegeuk Kim 6943b42c741SChao Yu /* 6953b42c741SChao Yu * update issue_list before we wake up issue_flush thread, this 6963b42c741SChao Yu * smp_mb() pairs with another barrier in ___wait_event(), see 6973b42c741SChao Yu * more details in comments of waitqueue_active(). 6983b42c741SChao Yu */ 6996f890df0SChao Yu smp_mb(); 7006f890df0SChao Yu 7016f890df0SChao Yu if (waitqueue_active(&fcc->flush_wait_queue)) 702a688b9d9SGu Zheng wake_up(&fcc->flush_wait_queue); 7036b4afdd7SJaegeuk Kim 7045eba8c5dSJaegeuk Kim if (fcc->f2fs_issue_flush) { 705adf8d90bSChao Yu wait_for_completion(&cmd.wait); 70672691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 7075eba8c5dSJaegeuk Kim } else { 708d3238691SChao Yu struct llist_node *list; 709d3238691SChao Yu 710d3238691SChao Yu list = llist_del_all(&fcc->issue_list); 711d3238691SChao Yu if (!list) { 712d3238691SChao Yu wait_for_completion(&cmd.wait); 71372691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 714d3238691SChao Yu } else { 715d3238691SChao Yu struct flush_cmd *tmp, *next; 716d3238691SChao Yu 71739d787beSChao Yu ret = submit_flush_wait(sbi, ino); 718d3238691SChao Yu 719d3238691SChao Yu llist_for_each_entry_safe(tmp, next, list, llnode) { 720d3238691SChao Yu if (tmp == &cmd) { 721d3238691SChao Yu cmd.ret = ret; 72272691af6SJaegeuk Kim atomic_dec(&fcc->queued_flush); 723d3238691SChao Yu continue; 724d3238691SChao Yu } 725d3238691SChao Yu tmp->ret = ret; 726d3238691SChao Yu complete(&tmp->wait); 727d3238691SChao Yu } 728d3238691SChao Yu } 7295eba8c5dSJaegeuk Kim } 730adf8d90bSChao Yu 731adf8d90bSChao Yu return cmd.ret; 7326b4afdd7SJaegeuk Kim } 7336b4afdd7SJaegeuk Kim 7344d57b86dSChao Yu int f2fs_create_flush_cmd_control(struct f2fs_sb_info *sbi) 7352163d198SGu Zheng { 7362163d198SGu Zheng dev_t dev = sbi->sb->s_bdev->bd_dev; 7372163d198SGu Zheng struct flush_cmd_control *fcc; 7382163d198SGu Zheng int err = 0; 7392163d198SGu Zheng 740b01a9201SJaegeuk Kim if (SM_I(sbi)->fcc_info) { 741b01a9201SJaegeuk Kim fcc = SM_I(sbi)->fcc_info; 742d871cd04SYunlong Song if (fcc->f2fs_issue_flush) 743d871cd04SYunlong Song return err; 7445eba8c5dSJaegeuk Kim goto init_thread; 7455eba8c5dSJaegeuk Kim } 7465eba8c5dSJaegeuk Kim 747acbf054dSChao Yu fcc = f2fs_kzalloc(sbi, sizeof(struct flush_cmd_control), GFP_KERNEL); 7482163d198SGu Zheng if (!fcc) 7492163d198SGu Zheng return -ENOMEM; 7508b8dd65fSChao Yu atomic_set(&fcc->issued_flush, 0); 75172691af6SJaegeuk Kim atomic_set(&fcc->queued_flush, 0); 7522163d198SGu Zheng init_waitqueue_head(&fcc->flush_wait_queue); 753721bd4d5SGu Zheng init_llist_head(&fcc->issue_list); 754b01a9201SJaegeuk Kim SM_I(sbi)->fcc_info = fcc; 755d4fdf8baSYunlei He if (!test_opt(sbi, FLUSH_MERGE)) 756d4fdf8baSYunlei He return err; 757d4fdf8baSYunlei He 7585eba8c5dSJaegeuk Kim init_thread: 7592163d198SGu Zheng fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, 7602163d198SGu Zheng "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); 7612163d198SGu Zheng if (IS_ERR(fcc->f2fs_issue_flush)) { 7622163d198SGu Zheng err = PTR_ERR(fcc->f2fs_issue_flush); 763c8eb7024SChao Yu kfree(fcc); 764b01a9201SJaegeuk Kim SM_I(sbi)->fcc_info = NULL; 7652163d198SGu Zheng return err; 7662163d198SGu Zheng } 7672163d198SGu Zheng 7682163d198SGu Zheng return err; 7692163d198SGu Zheng } 7702163d198SGu Zheng 7714d57b86dSChao Yu void f2fs_destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) 7722163d198SGu Zheng { 773b01a9201SJaegeuk Kim struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 7742163d198SGu Zheng 7755eba8c5dSJaegeuk Kim if (fcc && fcc->f2fs_issue_flush) { 7765eba8c5dSJaegeuk Kim struct task_struct *flush_thread = fcc->f2fs_issue_flush; 7775eba8c5dSJaegeuk Kim 7785eba8c5dSJaegeuk Kim fcc->f2fs_issue_flush = NULL; 7795eba8c5dSJaegeuk Kim kthread_stop(flush_thread); 7805eba8c5dSJaegeuk Kim } 7815eba8c5dSJaegeuk Kim if (free) { 782c8eb7024SChao Yu kfree(fcc); 783b01a9201SJaegeuk Kim SM_I(sbi)->fcc_info = NULL; 7842163d198SGu Zheng } 7855eba8c5dSJaegeuk Kim } 7862163d198SGu Zheng 7871228b482SChao Yu int f2fs_flush_device_cache(struct f2fs_sb_info *sbi) 7881228b482SChao Yu { 7891228b482SChao Yu int ret = 0, i; 7901228b482SChao Yu 7910916878dSDamien Le Moal if (!f2fs_is_multi_device(sbi)) 7921228b482SChao Yu return 0; 7931228b482SChao Yu 7946ed29fe1SChao Yu if (test_opt(sbi, NOBARRIER)) 7956ed29fe1SChao Yu return 0; 7966ed29fe1SChao Yu 7971228b482SChao Yu for (i = 1; i < sbi->s_ndevs; i++) { 79891803392SChao Yu int count = DEFAULT_RETRY_IO_COUNT; 79991803392SChao Yu 8001228b482SChao Yu if (!f2fs_test_bit(i, (char *)&sbi->dirty_device)) 8011228b482SChao Yu continue; 80291803392SChao Yu 80391803392SChao Yu do { 8041228b482SChao Yu ret = __submit_flush_wait(sbi, FDEV(i).bdev); 8051228b482SChao Yu if (ret) 80691803392SChao Yu congestion_wait(BLK_RW_ASYNC, 80791803392SChao Yu DEFAULT_IO_TIMEOUT); 80891803392SChao Yu } while (ret && --count); 80991803392SChao Yu 81091803392SChao Yu if (ret) { 81191803392SChao Yu f2fs_stop_checkpoint(sbi, false); 8121228b482SChao Yu break; 81391803392SChao Yu } 8141228b482SChao Yu 8151228b482SChao Yu spin_lock(&sbi->dev_lock); 8161228b482SChao Yu f2fs_clear_bit(i, (char *)&sbi->dirty_device); 8171228b482SChao Yu spin_unlock(&sbi->dev_lock); 8181228b482SChao Yu } 8191228b482SChao Yu 8201228b482SChao Yu return ret; 8211228b482SChao Yu } 8221228b482SChao Yu 823351df4b2SJaegeuk Kim static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 824351df4b2SJaegeuk Kim enum dirty_type dirty_type) 825351df4b2SJaegeuk Kim { 826351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 827351df4b2SJaegeuk Kim 828351df4b2SJaegeuk Kim /* need not be added */ 829351df4b2SJaegeuk Kim if (IS_CURSEG(sbi, segno)) 830351df4b2SJaegeuk Kim return; 831351df4b2SJaegeuk Kim 832351df4b2SJaegeuk Kim if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) 833351df4b2SJaegeuk Kim dirty_i->nr_dirty[dirty_type]++; 834351df4b2SJaegeuk Kim 835351df4b2SJaegeuk Kim if (dirty_type == DIRTY) { 836351df4b2SJaegeuk Kim struct seg_entry *sentry = get_seg_entry(sbi, segno); 8374625d6aaSChangman Lee enum dirty_type t = sentry->type; 838b2f2c390SJaegeuk Kim 839ec325b52SJaegeuk Kim if (unlikely(t >= DIRTY)) { 840ec325b52SJaegeuk Kim f2fs_bug_on(sbi, 1); 841ec325b52SJaegeuk Kim return; 842ec325b52SJaegeuk Kim } 8434625d6aaSChangman Lee if (!test_and_set_bit(segno, dirty_i->dirty_segmap[t])) 8444625d6aaSChangman Lee dirty_i->nr_dirty[t]++; 845da52f8adSJack Qiu 846da52f8adSJack Qiu if (__is_large_section(sbi)) { 847da52f8adSJack Qiu unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 848123aaf77SShin'ichiro Kawasaki block_t valid_blocks = 849da52f8adSJack Qiu get_valid_blocks(sbi, segno, true); 850da52f8adSJack Qiu 851da52f8adSJack Qiu f2fs_bug_on(sbi, unlikely(!valid_blocks || 852da52f8adSJack Qiu valid_blocks == BLKS_PER_SEC(sbi))); 853da52f8adSJack Qiu 854da52f8adSJack Qiu if (!IS_CURSEC(sbi, secno)) 855da52f8adSJack Qiu set_bit(secno, dirty_i->dirty_secmap); 856da52f8adSJack Qiu } 857351df4b2SJaegeuk Kim } 858351df4b2SJaegeuk Kim } 859351df4b2SJaegeuk Kim 860351df4b2SJaegeuk Kim static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 861351df4b2SJaegeuk Kim enum dirty_type dirty_type) 862351df4b2SJaegeuk Kim { 863351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 864123aaf77SShin'ichiro Kawasaki block_t valid_blocks; 865351df4b2SJaegeuk Kim 866351df4b2SJaegeuk Kim if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type])) 867351df4b2SJaegeuk Kim dirty_i->nr_dirty[dirty_type]--; 868351df4b2SJaegeuk Kim 869351df4b2SJaegeuk Kim if (dirty_type == DIRTY) { 8704625d6aaSChangman Lee struct seg_entry *sentry = get_seg_entry(sbi, segno); 8714625d6aaSChangman Lee enum dirty_type t = sentry->type; 872b2f2c390SJaegeuk Kim 8734625d6aaSChangman Lee if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) 874b2f2c390SJaegeuk Kim dirty_i->nr_dirty[t]--; 875b2f2c390SJaegeuk Kim 876da52f8adSJack Qiu valid_blocks = get_valid_blocks(sbi, segno, true); 877da52f8adSJack Qiu if (valid_blocks == 0) { 8784ddb1a4dSJaegeuk Kim clear_bit(GET_SEC_FROM_SEG(sbi, segno), 8795ec4e49fSJaegeuk Kim dirty_i->victim_secmap); 880bbf9f7d9SSahitya Tummala #ifdef CONFIG_F2FS_CHECK_FS 881bbf9f7d9SSahitya Tummala clear_bit(segno, SIT_I(sbi)->invalid_segmap); 882bbf9f7d9SSahitya Tummala #endif 883bbf9f7d9SSahitya Tummala } 884da52f8adSJack Qiu if (__is_large_section(sbi)) { 885da52f8adSJack Qiu unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 886da52f8adSJack Qiu 887da52f8adSJack Qiu if (!valid_blocks || 888da52f8adSJack Qiu valid_blocks == BLKS_PER_SEC(sbi)) { 889da52f8adSJack Qiu clear_bit(secno, dirty_i->dirty_secmap); 890da52f8adSJack Qiu return; 891da52f8adSJack Qiu } 892da52f8adSJack Qiu 893da52f8adSJack Qiu if (!IS_CURSEC(sbi, secno)) 894da52f8adSJack Qiu set_bit(secno, dirty_i->dirty_secmap); 895da52f8adSJack Qiu } 896351df4b2SJaegeuk Kim } 897351df4b2SJaegeuk Kim } 898351df4b2SJaegeuk Kim 8990a8165d7SJaegeuk Kim /* 900351df4b2SJaegeuk Kim * Should not occur error such as -ENOMEM. 901351df4b2SJaegeuk Kim * Adding dirty entry into seglist is not critical operation. 902351df4b2SJaegeuk Kim * If a given segment is one of current working segments, it won't be added. 903351df4b2SJaegeuk Kim */ 9048d8451afSHaicheng Li static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) 905351df4b2SJaegeuk Kim { 906351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9074354994fSDaniel Rosenberg unsigned short valid_blocks, ckpt_valid_blocks; 908de881df9SAravind Ramesh unsigned int usable_blocks; 909351df4b2SJaegeuk Kim 910351df4b2SJaegeuk Kim if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno)) 911351df4b2SJaegeuk Kim return; 912351df4b2SJaegeuk Kim 913de881df9SAravind Ramesh usable_blocks = f2fs_usable_blks_in_seg(sbi, segno); 914351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 915351df4b2SJaegeuk Kim 916302bd348SJaegeuk Kim valid_blocks = get_valid_blocks(sbi, segno, false); 91761461fc9SChao Yu ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno, false); 918351df4b2SJaegeuk Kim 9194354994fSDaniel Rosenberg if (valid_blocks == 0 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) || 920de881df9SAravind Ramesh ckpt_valid_blocks == usable_blocks)) { 921351df4b2SJaegeuk Kim __locate_dirty_segment(sbi, segno, PRE); 922351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, segno, DIRTY); 923de881df9SAravind Ramesh } else if (valid_blocks < usable_blocks) { 924351df4b2SJaegeuk Kim __locate_dirty_segment(sbi, segno, DIRTY); 925351df4b2SJaegeuk Kim } else { 926351df4b2SJaegeuk Kim /* Recovery routine with SSR needs this */ 927351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, segno, DIRTY); 928351df4b2SJaegeuk Kim } 929351df4b2SJaegeuk Kim 930351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 931351df4b2SJaegeuk Kim } 932351df4b2SJaegeuk Kim 9334354994fSDaniel Rosenberg /* This moves currently empty dirty blocks to prefree. Must hold seglist_lock */ 9344354994fSDaniel Rosenberg void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi) 9354354994fSDaniel Rosenberg { 9364354994fSDaniel Rosenberg struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9374354994fSDaniel Rosenberg unsigned int segno; 9384354994fSDaniel Rosenberg 9394354994fSDaniel Rosenberg mutex_lock(&dirty_i->seglist_lock); 9404354994fSDaniel Rosenberg for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 9414354994fSDaniel Rosenberg if (get_valid_blocks(sbi, segno, false)) 9424354994fSDaniel Rosenberg continue; 9434354994fSDaniel Rosenberg if (IS_CURSEG(sbi, segno)) 9444354994fSDaniel Rosenberg continue; 9454354994fSDaniel Rosenberg __locate_dirty_segment(sbi, segno, PRE); 9464354994fSDaniel Rosenberg __remove_dirty_segment(sbi, segno, DIRTY); 9474354994fSDaniel Rosenberg } 9484354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 9494354994fSDaniel Rosenberg } 9504354994fSDaniel Rosenberg 9514d3aed70SDaniel Rosenberg block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi) 9524354994fSDaniel Rosenberg { 953ae4ad7eaSDaniel Rosenberg int ovp_hole_segs = 954ae4ad7eaSDaniel Rosenberg (overprovision_segments(sbi) - reserved_segments(sbi)); 955ae4ad7eaSDaniel Rosenberg block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg; 9564d3aed70SDaniel Rosenberg struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9574354994fSDaniel Rosenberg block_t holes[2] = {0, 0}; /* DATA and NODE */ 9584d3aed70SDaniel Rosenberg block_t unusable; 9594354994fSDaniel Rosenberg struct seg_entry *se; 9604354994fSDaniel Rosenberg unsigned int segno; 9614354994fSDaniel Rosenberg 9624354994fSDaniel Rosenberg mutex_lock(&dirty_i->seglist_lock); 9634354994fSDaniel Rosenberg for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 9644354994fSDaniel Rosenberg se = get_seg_entry(sbi, segno); 9654354994fSDaniel Rosenberg if (IS_NODESEG(se->type)) 966de881df9SAravind Ramesh holes[NODE] += f2fs_usable_blks_in_seg(sbi, segno) - 967de881df9SAravind Ramesh se->valid_blocks; 9684354994fSDaniel Rosenberg else 969de881df9SAravind Ramesh holes[DATA] += f2fs_usable_blks_in_seg(sbi, segno) - 970de881df9SAravind Ramesh se->valid_blocks; 9714354994fSDaniel Rosenberg } 9724354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 9734354994fSDaniel Rosenberg 9744d3aed70SDaniel Rosenberg unusable = holes[DATA] > holes[NODE] ? holes[DATA] : holes[NODE]; 9754d3aed70SDaniel Rosenberg if (unusable > ovp_holes) 9764d3aed70SDaniel Rosenberg return unusable - ovp_holes; 9774d3aed70SDaniel Rosenberg return 0; 9784d3aed70SDaniel Rosenberg } 9794d3aed70SDaniel Rosenberg 9804d3aed70SDaniel Rosenberg int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable) 9814d3aed70SDaniel Rosenberg { 9824d3aed70SDaniel Rosenberg int ovp_hole_segs = 9834d3aed70SDaniel Rosenberg (overprovision_segments(sbi) - reserved_segments(sbi)); 9844d3aed70SDaniel Rosenberg if (unusable > F2FS_OPTION(sbi).unusable_cap) 9854354994fSDaniel Rosenberg return -EAGAIN; 986db610a64SJaegeuk Kim if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) && 987ae4ad7eaSDaniel Rosenberg dirty_segments(sbi) > ovp_hole_segs) 988db610a64SJaegeuk Kim return -EAGAIN; 9894354994fSDaniel Rosenberg return 0; 9904354994fSDaniel Rosenberg } 9914354994fSDaniel Rosenberg 9924354994fSDaniel Rosenberg /* This is only used by SBI_CP_DISABLED */ 9934354994fSDaniel Rosenberg static unsigned int get_free_segment(struct f2fs_sb_info *sbi) 9944354994fSDaniel Rosenberg { 9954354994fSDaniel Rosenberg struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 9964354994fSDaniel Rosenberg unsigned int segno = 0; 9974354994fSDaniel Rosenberg 9984354994fSDaniel Rosenberg mutex_lock(&dirty_i->seglist_lock); 9994354994fSDaniel Rosenberg for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 10004354994fSDaniel Rosenberg if (get_valid_blocks(sbi, segno, false)) 10014354994fSDaniel Rosenberg continue; 100261461fc9SChao Yu if (get_ckpt_valid_blocks(sbi, segno, false)) 10034354994fSDaniel Rosenberg continue; 10044354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 10054354994fSDaniel Rosenberg return segno; 10064354994fSDaniel Rosenberg } 10074354994fSDaniel Rosenberg mutex_unlock(&dirty_i->seglist_lock); 10084354994fSDaniel Rosenberg return NULL_SEGNO; 10094354994fSDaniel Rosenberg } 10104354994fSDaniel Rosenberg 1011004b6862SChao Yu static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, 1012c81abe34SJaegeuk Kim struct block_device *bdev, block_t lstart, 1013c81abe34SJaegeuk Kim block_t start, block_t len) 1014275b66b0SChao Yu { 10150b54fb84SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1016ba48a33eSChao Yu struct list_head *pend_list; 1017b01a9201SJaegeuk Kim struct discard_cmd *dc; 1018275b66b0SChao Yu 1019ba48a33eSChao Yu f2fs_bug_on(sbi, !len); 1020ba48a33eSChao Yu 1021ba48a33eSChao Yu pend_list = &dcc->pend_list[plist_idx(len)]; 1022ba48a33eSChao Yu 102332410577SChao Yu dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS, true, NULL); 1024b01a9201SJaegeuk Kim INIT_LIST_HEAD(&dc->list); 1025c81abe34SJaegeuk Kim dc->bdev = bdev; 1026b01a9201SJaegeuk Kim dc->lstart = lstart; 1027c81abe34SJaegeuk Kim dc->start = start; 1028b01a9201SJaegeuk Kim dc->len = len; 1029ec9895adSChao Yu dc->ref = 0; 103015469963SJaegeuk Kim dc->state = D_PREP; 103172691af6SJaegeuk Kim dc->queued = 0; 1032c81abe34SJaegeuk Kim dc->error = 0; 1033b01a9201SJaegeuk Kim init_completion(&dc->wait); 103422d375ddSChao Yu list_add_tail(&dc->list, pend_list); 103535ec7d57SChao Yu spin_lock_init(&dc->lock); 103635ec7d57SChao Yu dc->bio_ref = 0; 10375f32366aSChao Yu atomic_inc(&dcc->discard_cmd_cnt); 1038d84d1cbdSChao Yu dcc->undiscard_blks += len; 1039004b6862SChao Yu 1040004b6862SChao Yu return dc; 104115469963SJaegeuk Kim } 104215469963SJaegeuk Kim 1043004b6862SChao Yu static struct discard_cmd *__attach_discard_cmd(struct f2fs_sb_info *sbi, 1044004b6862SChao Yu struct block_device *bdev, block_t lstart, 1045004b6862SChao Yu block_t start, block_t len, 10464dada3fdSChao Yu struct rb_node *parent, struct rb_node **p, 10474dada3fdSChao Yu bool leftmost) 1048004b6862SChao Yu { 1049004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1050004b6862SChao Yu struct discard_cmd *dc; 1051004b6862SChao Yu 1052004b6862SChao Yu dc = __create_discard_cmd(sbi, bdev, lstart, start, len); 1053004b6862SChao Yu 1054004b6862SChao Yu rb_link_node(&dc->rb_node, parent, p); 10554dada3fdSChao Yu rb_insert_color_cached(&dc->rb_node, &dcc->root, leftmost); 1056004b6862SChao Yu 1057004b6862SChao Yu return dc; 1058004b6862SChao Yu } 1059004b6862SChao Yu 1060004b6862SChao Yu static void __detach_discard_cmd(struct discard_cmd_control *dcc, 1061004b6862SChao Yu struct discard_cmd *dc) 106215469963SJaegeuk Kim { 1063dcc9165dSJaegeuk Kim if (dc->state == D_DONE) 106472691af6SJaegeuk Kim atomic_sub(dc->queued, &dcc->queued_discard); 1065004b6862SChao Yu 1066004b6862SChao Yu list_del(&dc->list); 10674dada3fdSChao Yu rb_erase_cached(&dc->rb_node, &dcc->root); 1068d84d1cbdSChao Yu dcc->undiscard_blks -= dc->len; 1069004b6862SChao Yu 1070004b6862SChao Yu kmem_cache_free(discard_cmd_slab, dc); 1071004b6862SChao Yu 1072004b6862SChao Yu atomic_dec(&dcc->discard_cmd_cnt); 1073004b6862SChao Yu } 1074004b6862SChao Yu 1075004b6862SChao Yu static void __remove_discard_cmd(struct f2fs_sb_info *sbi, 1076004b6862SChao Yu struct discard_cmd *dc) 1077004b6862SChao Yu { 1078004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 107935ec7d57SChao Yu unsigned long flags; 1080dcc9165dSJaegeuk Kim 10812ec6f2efSChao Yu trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len); 10822ec6f2efSChao Yu 108335ec7d57SChao Yu spin_lock_irqsave(&dc->lock, flags); 108435ec7d57SChao Yu if (dc->bio_ref) { 108535ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 108635ec7d57SChao Yu return; 108735ec7d57SChao Yu } 108835ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 108935ec7d57SChao Yu 1090d9703d90SChao Yu f2fs_bug_on(sbi, dc->ref); 1091d9703d90SChao Yu 1092c81abe34SJaegeuk Kim if (dc->error == -EOPNOTSUPP) 1093c81abe34SJaegeuk Kim dc->error = 0; 109415469963SJaegeuk Kim 1095c81abe34SJaegeuk Kim if (dc->error) 109622d7ea13SChao Yu printk_ratelimited( 1097c45d6002SChao Yu "%sF2FS-fs (%s): Issue discard(%u, %u, %u) failed, ret: %d", 1098c45d6002SChao Yu KERN_INFO, sbi->sb->s_id, 1099c45d6002SChao Yu dc->lstart, dc->start, dc->len, dc->error); 1100004b6862SChao Yu __detach_discard_cmd(dcc, dc); 1101275b66b0SChao Yu } 1102275b66b0SChao Yu 1103c81abe34SJaegeuk Kim static void f2fs_submit_discard_endio(struct bio *bio) 1104c81abe34SJaegeuk Kim { 1105c81abe34SJaegeuk Kim struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private; 110635ec7d57SChao Yu unsigned long flags; 1107c81abe34SJaegeuk Kim 110835ec7d57SChao Yu spin_lock_irqsave(&dc->lock, flags); 11093fa6a8c5SSahitya Tummala if (!dc->error) 11103fa6a8c5SSahitya Tummala dc->error = blk_status_to_errno(bio->bi_status); 111135ec7d57SChao Yu dc->bio_ref--; 111235ec7d57SChao Yu if (!dc->bio_ref && dc->state == D_SUBMIT) { 1113c81abe34SJaegeuk Kim dc->state = D_DONE; 1114e31b9821SChao Yu complete_all(&dc->wait); 111535ec7d57SChao Yu } 111635ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 1117c81abe34SJaegeuk Kim bio_put(bio); 1118c81abe34SJaegeuk Kim } 1119c81abe34SJaegeuk Kim 112094b1e10eSWei Yongjun static void __check_sit_bitmap(struct f2fs_sb_info *sbi, 11216915ea9dSChao Yu block_t start, block_t end) 11226915ea9dSChao Yu { 11236915ea9dSChao Yu #ifdef CONFIG_F2FS_CHECK_FS 11246915ea9dSChao Yu struct seg_entry *sentry; 11256915ea9dSChao Yu unsigned int segno; 11266915ea9dSChao Yu block_t blk = start; 11276915ea9dSChao Yu unsigned long offset, size, max_blocks = sbi->blocks_per_seg; 11286915ea9dSChao Yu unsigned long *map; 11296915ea9dSChao Yu 11306915ea9dSChao Yu while (blk < end) { 11316915ea9dSChao Yu segno = GET_SEGNO(sbi, blk); 11326915ea9dSChao Yu sentry = get_seg_entry(sbi, segno); 11336915ea9dSChao Yu offset = GET_BLKOFF_FROM_SEG0(sbi, blk); 11346915ea9dSChao Yu 1135008396e1SYunlong Song if (end < START_BLOCK(sbi, segno + 1)) 1136008396e1SYunlong Song size = GET_BLKOFF_FROM_SEG0(sbi, end); 1137008396e1SYunlong Song else 1138008396e1SYunlong Song size = max_blocks; 11396915ea9dSChao Yu map = (unsigned long *)(sentry->cur_valid_map); 11406915ea9dSChao Yu offset = __find_rev_next_bit(map, size, offset); 11416915ea9dSChao Yu f2fs_bug_on(sbi, offset != size); 1142008396e1SYunlong Song blk = START_BLOCK(sbi, segno + 1); 11436915ea9dSChao Yu } 11446915ea9dSChao Yu #endif 11456915ea9dSChao Yu } 11466915ea9dSChao Yu 11478bb4f253SJaegeuk Kim static void __init_discard_policy(struct f2fs_sb_info *sbi, 11488bb4f253SJaegeuk Kim struct discard_policy *dpolicy, 11498bb4f253SJaegeuk Kim int discard_type, unsigned int granularity) 11508bb4f253SJaegeuk Kim { 1151c35b8d5eSSahitya Tummala struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1152c35b8d5eSSahitya Tummala 11538bb4f253SJaegeuk Kim /* common policy */ 11548bb4f253SJaegeuk Kim dpolicy->type = discard_type; 11558bb4f253SJaegeuk Kim dpolicy->sync = true; 115620ee4382SChao Yu dpolicy->ordered = false; 11578bb4f253SJaegeuk Kim dpolicy->granularity = granularity; 11588bb4f253SJaegeuk Kim 1159*d2d8e896SKonstantin Vyshetsky dpolicy->max_requests = dcc->max_discard_request; 11608bb4f253SJaegeuk Kim dpolicy->io_aware_gran = MAX_PLIST_NUM; 11616ce48b0cSChao Yu dpolicy->timeout = false; 11628bb4f253SJaegeuk Kim 11638bb4f253SJaegeuk Kim if (discard_type == DPOLICY_BG) { 1164*d2d8e896SKonstantin Vyshetsky dpolicy->min_interval = dcc->min_discard_issue_time; 1165*d2d8e896SKonstantin Vyshetsky dpolicy->mid_interval = dcc->mid_discard_issue_time; 1166*d2d8e896SKonstantin Vyshetsky dpolicy->max_interval = dcc->max_discard_issue_time; 11678bb4f253SJaegeuk Kim dpolicy->io_aware = true; 1168cba60849SChao Yu dpolicy->sync = false; 116920ee4382SChao Yu dpolicy->ordered = true; 11708bb4f253SJaegeuk Kim if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) { 11718bb4f253SJaegeuk Kim dpolicy->granularity = 1; 1172c35b8d5eSSahitya Tummala if (atomic_read(&dcc->discard_cmd_cnt)) 1173c35b8d5eSSahitya Tummala dpolicy->max_interval = 1174*d2d8e896SKonstantin Vyshetsky dcc->min_discard_issue_time; 11758bb4f253SJaegeuk Kim } 11768bb4f253SJaegeuk Kim } else if (discard_type == DPOLICY_FORCE) { 1177*d2d8e896SKonstantin Vyshetsky dpolicy->min_interval = dcc->min_discard_issue_time; 1178*d2d8e896SKonstantin Vyshetsky dpolicy->mid_interval = dcc->mid_discard_issue_time; 1179*d2d8e896SKonstantin Vyshetsky dpolicy->max_interval = dcc->max_discard_issue_time; 11808bb4f253SJaegeuk Kim dpolicy->io_aware = false; 11818bb4f253SJaegeuk Kim } else if (discard_type == DPOLICY_FSTRIM) { 11828bb4f253SJaegeuk Kim dpolicy->io_aware = false; 11838bb4f253SJaegeuk Kim } else if (discard_type == DPOLICY_UMOUNT) { 11848bb4f253SJaegeuk Kim dpolicy->io_aware = false; 1185b8623253SJaegeuk Kim /* we need to issue all to keep CP_TRIMMED_FLAG */ 1186b8623253SJaegeuk Kim dpolicy->granularity = 1; 11876ce48b0cSChao Yu dpolicy->timeout = true; 11888bb4f253SJaegeuk Kim } 11898bb4f253SJaegeuk Kim } 11908bb4f253SJaegeuk Kim 119135ec7d57SChao Yu static void __update_discard_tree_range(struct f2fs_sb_info *sbi, 119235ec7d57SChao Yu struct block_device *bdev, block_t lstart, 119335ec7d57SChao Yu block_t start, block_t len); 1194c81abe34SJaegeuk Kim /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ 11956b9cb124SChao Yu static int __submit_discard_cmd(struct f2fs_sb_info *sbi, 119678997b56SChao Yu struct discard_policy *dpolicy, 119735ec7d57SChao Yu struct discard_cmd *dc, 119835ec7d57SChao Yu unsigned int *issued) 1199c81abe34SJaegeuk Kim { 120035ec7d57SChao Yu struct block_device *bdev = dc->bdev; 120135ec7d57SChao Yu struct request_queue *q = bdev_get_queue(bdev); 120235ec7d57SChao Yu unsigned int max_discard_blocks = 120335ec7d57SChao Yu SECTOR_TO_BLOCK(q->limits.max_discard_sectors); 1204c81abe34SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 120578997b56SChao Yu struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? 120678997b56SChao Yu &(dcc->fstrim_list) : &(dcc->wait_list); 120778997b56SChao Yu int flag = dpolicy->sync ? REQ_SYNC : 0; 120835ec7d57SChao Yu block_t lstart, start, len, total_len; 120935ec7d57SChao Yu int err = 0; 1210c81abe34SJaegeuk Kim 1211c81abe34SJaegeuk Kim if (dc->state != D_PREP) 12126b9cb124SChao Yu return 0; 1213c81abe34SJaegeuk Kim 1214d6184774SYunlei He if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) 12156b9cb124SChao Yu return 0; 1216d6184774SYunlei He 121735ec7d57SChao Yu trace_f2fs_issue_discard(bdev, dc->start, dc->len); 12180243a5f9SChao Yu 121935ec7d57SChao Yu lstart = dc->lstart; 122035ec7d57SChao Yu start = dc->start; 122135ec7d57SChao Yu len = dc->len; 122235ec7d57SChao Yu total_len = len; 122335ec7d57SChao Yu 122435ec7d57SChao Yu dc->len = 0; 122535ec7d57SChao Yu 122635ec7d57SChao Yu while (total_len && *issued < dpolicy->max_requests && !err) { 122735ec7d57SChao Yu struct bio *bio = NULL; 122835ec7d57SChao Yu unsigned long flags; 122935ec7d57SChao Yu bool last = true; 123035ec7d57SChao Yu 123135ec7d57SChao Yu if (len > max_discard_blocks) { 123235ec7d57SChao Yu len = max_discard_blocks; 123335ec7d57SChao Yu last = false; 123435ec7d57SChao Yu } 123535ec7d57SChao Yu 123635ec7d57SChao Yu (*issued)++; 123735ec7d57SChao Yu if (*issued == dpolicy->max_requests) 123835ec7d57SChao Yu last = true; 123935ec7d57SChao Yu 124035ec7d57SChao Yu dc->len += len; 124135ec7d57SChao Yu 1242b83dcfe6SChao Yu if (time_to_inject(sbi, FAULT_DISCARD)) { 1243c45d6002SChao Yu f2fs_show_injection_info(sbi, FAULT_DISCARD); 1244b83dcfe6SChao Yu err = -EIO; 1245b83dcfe6SChao Yu goto submit; 1246b83dcfe6SChao Yu } 124735ec7d57SChao Yu err = __blkdev_issue_discard(bdev, 124835ec7d57SChao Yu SECTOR_FROM_BLOCK(start), 124935ec7d57SChao Yu SECTOR_FROM_BLOCK(len), 1250c81abe34SJaegeuk Kim GFP_NOFS, 0, &bio); 1251b83dcfe6SChao Yu submit: 12526b9cb124SChao Yu if (err) { 12536b9cb124SChao Yu spin_lock_irqsave(&dc->lock, flags); 12546b9cb124SChao Yu if (dc->state == D_PARTIAL) 12556b9cb124SChao Yu dc->state = D_SUBMIT; 12566b9cb124SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 12576b9cb124SChao Yu 12586b9cb124SChao Yu break; 12596b9cb124SChao Yu } 12606b9cb124SChao Yu 12616b9cb124SChao Yu f2fs_bug_on(sbi, !bio); 12626b9cb124SChao Yu 126335ec7d57SChao Yu /* 126435ec7d57SChao Yu * should keep before submission to avoid D_DONE 126535ec7d57SChao Yu * right away 126635ec7d57SChao Yu */ 126735ec7d57SChao Yu spin_lock_irqsave(&dc->lock, flags); 126835ec7d57SChao Yu if (last) 1269c81abe34SJaegeuk Kim dc->state = D_SUBMIT; 127035ec7d57SChao Yu else 127135ec7d57SChao Yu dc->state = D_PARTIAL; 127235ec7d57SChao Yu dc->bio_ref++; 127335ec7d57SChao Yu spin_unlock_irqrestore(&dc->lock, flags); 127435ec7d57SChao Yu 127572691af6SJaegeuk Kim atomic_inc(&dcc->queued_discard); 127672691af6SJaegeuk Kim dc->queued++; 127735ec7d57SChao Yu list_move_tail(&dc->list, wait_list); 127835ec7d57SChao Yu 127935ec7d57SChao Yu /* sanity check on discard range */ 12809249ddedSQiuyang Sun __check_sit_bitmap(sbi, lstart, lstart + len); 128135ec7d57SChao Yu 1282c81abe34SJaegeuk Kim bio->bi_private = dc; 1283c81abe34SJaegeuk Kim bio->bi_end_io = f2fs_submit_discard_endio; 1284ecc9aa00SChao Yu bio->bi_opf |= flag; 1285c81abe34SJaegeuk Kim submit_bio(bio); 128635ec7d57SChao Yu 128735ec7d57SChao Yu atomic_inc(&dcc->issued_discard); 1288b0af6d49SChao Yu 1289b0af6d49SChao Yu f2fs_update_iostat(sbi, FS_DISCARD, 1); 129035ec7d57SChao Yu 129135ec7d57SChao Yu lstart += len; 129235ec7d57SChao Yu start += len; 129335ec7d57SChao Yu total_len -= len; 129435ec7d57SChao Yu len = total_len; 129535ec7d57SChao Yu } 129635ec7d57SChao Yu 1297df423399SSahitya Tummala if (!err && len) { 1298df423399SSahitya Tummala dcc->undiscard_blks -= len; 129935ec7d57SChao Yu __update_discard_tree_range(sbi, bdev, lstart, start, len); 1300df423399SSahitya Tummala } 13016b9cb124SChao Yu return err; 1302c81abe34SJaegeuk Kim } 1303c81abe34SJaegeuk Kim 130447d0d7d7SChao Yu static void __insert_discard_tree(struct f2fs_sb_info *sbi, 1305004b6862SChao Yu struct block_device *bdev, block_t lstart, 1306004b6862SChao Yu block_t start, block_t len, 1307004b6862SChao Yu struct rb_node **insert_p, 1308004b6862SChao Yu struct rb_node *insert_parent) 1309004b6862SChao Yu { 1310004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1311dca6951fSColin Ian King struct rb_node **p; 1312004b6862SChao Yu struct rb_node *parent = NULL; 13134dada3fdSChao Yu bool leftmost = true; 1314004b6862SChao Yu 1315004b6862SChao Yu if (insert_p && insert_parent) { 1316004b6862SChao Yu parent = insert_parent; 1317004b6862SChao Yu p = insert_p; 1318004b6862SChao Yu goto do_insert; 1319004b6862SChao Yu } 1320004b6862SChao Yu 13214dada3fdSChao Yu p = f2fs_lookup_rb_tree_for_insert(sbi, &dcc->root, &parent, 13224dada3fdSChao Yu lstart, &leftmost); 1323004b6862SChao Yu do_insert: 132447d0d7d7SChao Yu __attach_discard_cmd(sbi, bdev, lstart, start, len, parent, 13254dada3fdSChao Yu p, leftmost); 1326004b6862SChao Yu } 1327004b6862SChao Yu 1328ba48a33eSChao Yu static void __relocate_discard_cmd(struct discard_cmd_control *dcc, 1329ba48a33eSChao Yu struct discard_cmd *dc) 1330ba48a33eSChao Yu { 1331ba48a33eSChao Yu list_move_tail(&dc->list, &dcc->pend_list[plist_idx(dc->len)]); 1332ba48a33eSChao Yu } 1333ba48a33eSChao Yu 1334004b6862SChao Yu static void __punch_discard_cmd(struct f2fs_sb_info *sbi, 1335004b6862SChao Yu struct discard_cmd *dc, block_t blkaddr) 1336004b6862SChao Yu { 1337ba48a33eSChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1338004b6862SChao Yu struct discard_info di = dc->di; 1339004b6862SChao Yu bool modified = false; 1340004b6862SChao Yu 1341004b6862SChao Yu if (dc->state == D_DONE || dc->len == 1) { 1342004b6862SChao Yu __remove_discard_cmd(sbi, dc); 1343004b6862SChao Yu return; 1344004b6862SChao Yu } 1345004b6862SChao Yu 1346d84d1cbdSChao Yu dcc->undiscard_blks -= di.len; 1347d84d1cbdSChao Yu 1348004b6862SChao Yu if (blkaddr > di.lstart) { 1349004b6862SChao Yu dc->len = blkaddr - dc->lstart; 1350d84d1cbdSChao Yu dcc->undiscard_blks += dc->len; 1351ba48a33eSChao Yu __relocate_discard_cmd(dcc, dc); 1352004b6862SChao Yu modified = true; 1353004b6862SChao Yu } 1354004b6862SChao Yu 1355004b6862SChao Yu if (blkaddr < di.lstart + di.len - 1) { 1356004b6862SChao Yu if (modified) { 1357004b6862SChao Yu __insert_discard_tree(sbi, dc->bdev, blkaddr + 1, 1358004b6862SChao Yu di.start + blkaddr + 1 - di.lstart, 1359004b6862SChao Yu di.lstart + di.len - 1 - blkaddr, 1360004b6862SChao Yu NULL, NULL); 1361004b6862SChao Yu } else { 1362004b6862SChao Yu dc->lstart++; 1363004b6862SChao Yu dc->len--; 1364004b6862SChao Yu dc->start++; 1365d84d1cbdSChao Yu dcc->undiscard_blks += dc->len; 1366ba48a33eSChao Yu __relocate_discard_cmd(dcc, dc); 1367004b6862SChao Yu } 1368004b6862SChao Yu } 1369004b6862SChao Yu } 1370004b6862SChao Yu 1371004b6862SChao Yu static void __update_discard_tree_range(struct f2fs_sb_info *sbi, 1372004b6862SChao Yu struct block_device *bdev, block_t lstart, 1373004b6862SChao Yu block_t start, block_t len) 1374004b6862SChao Yu { 1375004b6862SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1376004b6862SChao Yu struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 1377004b6862SChao Yu struct discard_cmd *dc; 1378004b6862SChao Yu struct discard_info di = {0}; 1379004b6862SChao Yu struct rb_node **insert_p = NULL, *insert_parent = NULL; 138035ec7d57SChao Yu struct request_queue *q = bdev_get_queue(bdev); 138135ec7d57SChao Yu unsigned int max_discard_blocks = 138235ec7d57SChao Yu SECTOR_TO_BLOCK(q->limits.max_discard_sectors); 1383004b6862SChao Yu block_t end = lstart + len; 1384004b6862SChao Yu 13854d57b86dSChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, 1386004b6862SChao Yu NULL, lstart, 1387004b6862SChao Yu (struct rb_entry **)&prev_dc, 1388004b6862SChao Yu (struct rb_entry **)&next_dc, 13894dada3fdSChao Yu &insert_p, &insert_parent, true, NULL); 1390004b6862SChao Yu if (dc) 1391004b6862SChao Yu prev_dc = dc; 1392004b6862SChao Yu 1393004b6862SChao Yu if (!prev_dc) { 1394004b6862SChao Yu di.lstart = lstart; 1395004b6862SChao Yu di.len = next_dc ? next_dc->lstart - lstart : len; 1396004b6862SChao Yu di.len = min(di.len, len); 1397004b6862SChao Yu di.start = start; 1398004b6862SChao Yu } 1399004b6862SChao Yu 1400004b6862SChao Yu while (1) { 1401004b6862SChao Yu struct rb_node *node; 1402004b6862SChao Yu bool merged = false; 1403004b6862SChao Yu struct discard_cmd *tdc = NULL; 1404004b6862SChao Yu 1405004b6862SChao Yu if (prev_dc) { 1406004b6862SChao Yu di.lstart = prev_dc->lstart + prev_dc->len; 1407004b6862SChao Yu if (di.lstart < lstart) 1408004b6862SChao Yu di.lstart = lstart; 1409004b6862SChao Yu if (di.lstart >= end) 1410004b6862SChao Yu break; 1411004b6862SChao Yu 1412004b6862SChao Yu if (!next_dc || next_dc->lstart > end) 1413004b6862SChao Yu di.len = end - di.lstart; 1414004b6862SChao Yu else 1415004b6862SChao Yu di.len = next_dc->lstart - di.lstart; 1416004b6862SChao Yu di.start = start + di.lstart - lstart; 1417004b6862SChao Yu } 1418004b6862SChao Yu 1419004b6862SChao Yu if (!di.len) 1420004b6862SChao Yu goto next; 1421004b6862SChao Yu 1422004b6862SChao Yu if (prev_dc && prev_dc->state == D_PREP && 1423004b6862SChao Yu prev_dc->bdev == bdev && 142435ec7d57SChao Yu __is_discard_back_mergeable(&di, &prev_dc->di, 142535ec7d57SChao Yu max_discard_blocks)) { 1426004b6862SChao Yu prev_dc->di.len += di.len; 1427d84d1cbdSChao Yu dcc->undiscard_blks += di.len; 1428ba48a33eSChao Yu __relocate_discard_cmd(dcc, prev_dc); 1429004b6862SChao Yu di = prev_dc->di; 1430004b6862SChao Yu tdc = prev_dc; 1431004b6862SChao Yu merged = true; 1432004b6862SChao Yu } 1433004b6862SChao Yu 1434004b6862SChao Yu if (next_dc && next_dc->state == D_PREP && 1435004b6862SChao Yu next_dc->bdev == bdev && 143635ec7d57SChao Yu __is_discard_front_mergeable(&di, &next_dc->di, 143735ec7d57SChao Yu max_discard_blocks)) { 1438004b6862SChao Yu next_dc->di.lstart = di.lstart; 1439004b6862SChao Yu next_dc->di.len += di.len; 1440004b6862SChao Yu next_dc->di.start = di.start; 1441d84d1cbdSChao Yu dcc->undiscard_blks += di.len; 1442ba48a33eSChao Yu __relocate_discard_cmd(dcc, next_dc); 1443004b6862SChao Yu if (tdc) 1444004b6862SChao Yu __remove_discard_cmd(sbi, tdc); 1445004b6862SChao Yu merged = true; 1446004b6862SChao Yu } 1447004b6862SChao Yu 1448df0f6b44SChao Yu if (!merged) { 1449004b6862SChao Yu __insert_discard_tree(sbi, bdev, di.lstart, di.start, 1450004b6862SChao Yu di.len, NULL, NULL); 1451df0f6b44SChao Yu } 1452004b6862SChao Yu next: 1453004b6862SChao Yu prev_dc = next_dc; 1454004b6862SChao Yu if (!prev_dc) 1455004b6862SChao Yu break; 1456004b6862SChao Yu 1457004b6862SChao Yu node = rb_next(&prev_dc->rb_node); 1458004b6862SChao Yu next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); 1459004b6862SChao Yu } 1460004b6862SChao Yu } 1461004b6862SChao Yu 1462c81abe34SJaegeuk Kim static int __queue_discard_cmd(struct f2fs_sb_info *sbi, 1463c81abe34SJaegeuk Kim struct block_device *bdev, block_t blkstart, block_t blklen) 1464c81abe34SJaegeuk Kim { 1465c81abe34SJaegeuk Kim block_t lblkstart = blkstart; 1466c81abe34SJaegeuk Kim 14677f3d7719SDamien Le Moal if (!f2fs_bdev_support_discard(bdev)) 14687f3d7719SDamien Le Moal return 0; 14697f3d7719SDamien Le Moal 14700243a5f9SChao Yu trace_f2fs_queue_discard(bdev, blkstart, blklen); 1471c81abe34SJaegeuk Kim 14720916878dSDamien Le Moal if (f2fs_is_multi_device(sbi)) { 1473c81abe34SJaegeuk Kim int devi = f2fs_target_device_index(sbi, blkstart); 1474c81abe34SJaegeuk Kim 1475c81abe34SJaegeuk Kim blkstart -= FDEV(devi).start_blk; 1476c81abe34SJaegeuk Kim } 147735ec7d57SChao Yu mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock); 1478004b6862SChao Yu __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen); 147935ec7d57SChao Yu mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock); 1480c81abe34SJaegeuk Kim return 0; 1481c81abe34SJaegeuk Kim } 1482c81abe34SJaegeuk Kim 148320ee4382SChao Yu static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, 148420ee4382SChao Yu struct discard_policy *dpolicy) 148520ee4382SChao Yu { 148620ee4382SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 148720ee4382SChao Yu struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 148820ee4382SChao Yu struct rb_node **insert_p = NULL, *insert_parent = NULL; 148920ee4382SChao Yu struct discard_cmd *dc; 149020ee4382SChao Yu struct blk_plug plug; 149120ee4382SChao Yu unsigned int pos = dcc->next_pos; 149220ee4382SChao Yu unsigned int issued = 0; 149320ee4382SChao Yu bool io_interrupted = false; 149420ee4382SChao Yu 149520ee4382SChao Yu mutex_lock(&dcc->cmd_lock); 149620ee4382SChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, 149720ee4382SChao Yu NULL, pos, 149820ee4382SChao Yu (struct rb_entry **)&prev_dc, 149920ee4382SChao Yu (struct rb_entry **)&next_dc, 15004dada3fdSChao Yu &insert_p, &insert_parent, true, NULL); 150120ee4382SChao Yu if (!dc) 150220ee4382SChao Yu dc = next_dc; 150320ee4382SChao Yu 150420ee4382SChao Yu blk_start_plug(&plug); 150520ee4382SChao Yu 150620ee4382SChao Yu while (dc) { 150720ee4382SChao Yu struct rb_node *node; 15086b9cb124SChao Yu int err = 0; 150920ee4382SChao Yu 151020ee4382SChao Yu if (dc->state != D_PREP) 151120ee4382SChao Yu goto next; 151220ee4382SChao Yu 1513a7d10cf3SSahitya Tummala if (dpolicy->io_aware && !is_idle(sbi, DISCARD_TIME)) { 151420ee4382SChao Yu io_interrupted = true; 151520ee4382SChao Yu break; 151620ee4382SChao Yu } 151720ee4382SChao Yu 151820ee4382SChao Yu dcc->next_pos = dc->lstart + dc->len; 15196b9cb124SChao Yu err = __submit_discard_cmd(sbi, dpolicy, dc, &issued); 152020ee4382SChao Yu 152135ec7d57SChao Yu if (issued >= dpolicy->max_requests) 152220ee4382SChao Yu break; 152320ee4382SChao Yu next: 152420ee4382SChao Yu node = rb_next(&dc->rb_node); 15256b9cb124SChao Yu if (err) 15266b9cb124SChao Yu __remove_discard_cmd(sbi, dc); 152720ee4382SChao Yu dc = rb_entry_safe(node, struct discard_cmd, rb_node); 152820ee4382SChao Yu } 152920ee4382SChao Yu 153020ee4382SChao Yu blk_finish_plug(&plug); 153120ee4382SChao Yu 153220ee4382SChao Yu if (!dc) 153320ee4382SChao Yu dcc->next_pos = 0; 153420ee4382SChao Yu 153520ee4382SChao Yu mutex_unlock(&dcc->cmd_lock); 153620ee4382SChao Yu 153720ee4382SChao Yu if (!issued && io_interrupted) 153820ee4382SChao Yu issued = -1; 153920ee4382SChao Yu 154020ee4382SChao Yu return issued; 154120ee4382SChao Yu } 1542141af6baSSahitya Tummala static unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi, 1543141af6baSSahitya Tummala struct discard_policy *dpolicy); 154420ee4382SChao Yu 154578997b56SChao Yu static int __issue_discard_cmd(struct f2fs_sb_info *sbi, 154678997b56SChao Yu struct discard_policy *dpolicy) 1547bd5b0738SChao Yu { 1548bd5b0738SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1549bd5b0738SChao Yu struct list_head *pend_list; 1550bd5b0738SChao Yu struct discard_cmd *dc, *tmp; 1551bd5b0738SChao Yu struct blk_plug plug; 1552141af6baSSahitya Tummala int i, issued; 1553e6c6de18SChao Yu bool io_interrupted = false; 1554bd5b0738SChao Yu 15556ce48b0cSChao Yu if (dpolicy->timeout) 15566ce48b0cSChao Yu f2fs_update_time(sbi, UMOUNT_DISCARD_TIMEOUT); 155703f2c02dSJaegeuk Kim 1558141af6baSSahitya Tummala retry: 1559141af6baSSahitya Tummala issued = 0; 156078997b56SChao Yu for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { 15616ce48b0cSChao Yu if (dpolicy->timeout && 15626ce48b0cSChao Yu f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) 156303f2c02dSJaegeuk Kim break; 156403f2c02dSJaegeuk Kim 156578997b56SChao Yu if (i + 1 < dpolicy->granularity) 156678997b56SChao Yu break; 156720ee4382SChao Yu 156820ee4382SChao Yu if (i < DEFAULT_DISCARD_GRANULARITY && dpolicy->ordered) 156920ee4382SChao Yu return __issue_discard_cmd_orderly(sbi, dpolicy); 157020ee4382SChao Yu 1571bd5b0738SChao Yu pend_list = &dcc->pend_list[i]; 157233da62cfSChao Yu 157333da62cfSChao Yu mutex_lock(&dcc->cmd_lock); 157449c60c67SChao Yu if (list_empty(pend_list)) 157549c60c67SChao Yu goto next; 157667fce70bSChao Yu if (unlikely(dcc->rbtree_check)) 157767fce70bSChao Yu f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi, 15782e9b2bb2SChao Yu &dcc->root, false)); 157933da62cfSChao Yu blk_start_plug(&plug); 1580bd5b0738SChao Yu list_for_each_entry_safe(dc, tmp, pend_list, list) { 1581bd5b0738SChao Yu f2fs_bug_on(sbi, dc->state != D_PREP); 1582bd5b0738SChao Yu 15836ce48b0cSChao Yu if (dpolicy->timeout && 15846ce48b0cSChao Yu f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) 15856e0cd4a9SHeng Xiao break; 15866e0cd4a9SHeng Xiao 1587ecc9aa00SChao Yu if (dpolicy->io_aware && i < dpolicy->io_aware_gran && 1588a7d10cf3SSahitya Tummala !is_idle(sbi, DISCARD_TIME)) { 1589e6c6de18SChao Yu io_interrupted = true; 1590522d1711SChao Yu break; 1591e6c6de18SChao Yu } 1592e6c6de18SChao Yu 159335ec7d57SChao Yu __submit_discard_cmd(sbi, dpolicy, dc, &issued); 1594522d1711SChao Yu 159535ec7d57SChao Yu if (issued >= dpolicy->max_requests) 159633da62cfSChao Yu break; 1597bd5b0738SChao Yu } 1598bd5b0738SChao Yu blk_finish_plug(&plug); 159949c60c67SChao Yu next: 1600bd5b0738SChao Yu mutex_unlock(&dcc->cmd_lock); 1601969d1b18SChao Yu 1602522d1711SChao Yu if (issued >= dpolicy->max_requests || io_interrupted) 160333da62cfSChao Yu break; 160433da62cfSChao Yu } 160533da62cfSChao Yu 1606141af6baSSahitya Tummala if (dpolicy->type == DPOLICY_UMOUNT && issued) { 1607141af6baSSahitya Tummala __wait_all_discard_cmd(sbi, dpolicy); 1608141af6baSSahitya Tummala goto retry; 1609141af6baSSahitya Tummala } 1610141af6baSSahitya Tummala 1611e6c6de18SChao Yu if (!issued && io_interrupted) 1612e6c6de18SChao Yu issued = -1; 1613e6c6de18SChao Yu 1614969d1b18SChao Yu return issued; 1615969d1b18SChao Yu } 1616969d1b18SChao Yu 1617cf5c759fSChao Yu static bool __drop_discard_cmd(struct f2fs_sb_info *sbi) 1618969d1b18SChao Yu { 1619969d1b18SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1620969d1b18SChao Yu struct list_head *pend_list; 1621969d1b18SChao Yu struct discard_cmd *dc, *tmp; 1622969d1b18SChao Yu int i; 1623cf5c759fSChao Yu bool dropped = false; 1624969d1b18SChao Yu 1625969d1b18SChao Yu mutex_lock(&dcc->cmd_lock); 1626969d1b18SChao Yu for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { 1627969d1b18SChao Yu pend_list = &dcc->pend_list[i]; 1628969d1b18SChao Yu list_for_each_entry_safe(dc, tmp, pend_list, list) { 1629969d1b18SChao Yu f2fs_bug_on(sbi, dc->state != D_PREP); 1630969d1b18SChao Yu __remove_discard_cmd(sbi, dc); 1631cf5c759fSChao Yu dropped = true; 1632969d1b18SChao Yu } 1633969d1b18SChao Yu } 1634969d1b18SChao Yu mutex_unlock(&dcc->cmd_lock); 1635cf5c759fSChao Yu 1636cf5c759fSChao Yu return dropped; 1637bd5b0738SChao Yu } 1638bd5b0738SChao Yu 16394d57b86dSChao Yu void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi) 16407950e9acSChao Yu { 16417950e9acSChao Yu __drop_discard_cmd(sbi); 16427950e9acSChao Yu } 16437950e9acSChao Yu 16440ea80512SChao Yu static unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, 16452a510c00SChao Yu struct discard_cmd *dc) 16462a510c00SChao Yu { 16472a510c00SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 16480ea80512SChao Yu unsigned int len = 0; 16492a510c00SChao Yu 16502a510c00SChao Yu wait_for_completion_io(&dc->wait); 16512a510c00SChao Yu mutex_lock(&dcc->cmd_lock); 16522a510c00SChao Yu f2fs_bug_on(sbi, dc->state != D_DONE); 16532a510c00SChao Yu dc->ref--; 16540ea80512SChao Yu if (!dc->ref) { 16550ea80512SChao Yu if (!dc->error) 16560ea80512SChao Yu len = dc->len; 16572a510c00SChao Yu __remove_discard_cmd(sbi, dc); 16580ea80512SChao Yu } 16592a510c00SChao Yu mutex_unlock(&dcc->cmd_lock); 16600ea80512SChao Yu 16610ea80512SChao Yu return len; 16622a510c00SChao Yu } 16632a510c00SChao Yu 16640ea80512SChao Yu static unsigned int __wait_discard_cmd_range(struct f2fs_sb_info *sbi, 166578997b56SChao Yu struct discard_policy *dpolicy, 166678997b56SChao Yu block_t start, block_t end) 166763a94fa1SChao Yu { 166863a94fa1SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 166978997b56SChao Yu struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? 167078997b56SChao Yu &(dcc->fstrim_list) : &(dcc->wait_list); 167163a94fa1SChao Yu struct discard_cmd *dc, *tmp; 16726afae633SChao Yu bool need_wait; 16730ea80512SChao Yu unsigned int trimmed = 0; 16746afae633SChao Yu 16756afae633SChao Yu next: 16766afae633SChao Yu need_wait = false; 167763a94fa1SChao Yu 167863a94fa1SChao Yu mutex_lock(&dcc->cmd_lock); 167963a94fa1SChao Yu list_for_each_entry_safe(dc, tmp, wait_list, list) { 16808412663dSChao Yu if (dc->lstart + dc->len <= start || end <= dc->lstart) 16818412663dSChao Yu continue; 168278997b56SChao Yu if (dc->len < dpolicy->granularity) 16838412663dSChao Yu continue; 168478997b56SChao Yu if (dc->state == D_DONE && !dc->ref) { 168563a94fa1SChao Yu wait_for_completion_io(&dc->wait); 16860ea80512SChao Yu if (!dc->error) 16870ea80512SChao Yu trimmed += dc->len; 168863a94fa1SChao Yu __remove_discard_cmd(sbi, dc); 16896afae633SChao Yu } else { 16906afae633SChao Yu dc->ref++; 16916afae633SChao Yu need_wait = true; 16926afae633SChao Yu break; 169363a94fa1SChao Yu } 169463a94fa1SChao Yu } 169563a94fa1SChao Yu mutex_unlock(&dcc->cmd_lock); 16966afae633SChao Yu 16976afae633SChao Yu if (need_wait) { 16980ea80512SChao Yu trimmed += __wait_one_discard_bio(sbi, dc); 16996afae633SChao Yu goto next; 17006afae633SChao Yu } 17010ea80512SChao Yu 17020ea80512SChao Yu return trimmed; 170363a94fa1SChao Yu } 170463a94fa1SChao Yu 170501f9cf6dSChao Yu static unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi, 170678997b56SChao Yu struct discard_policy *dpolicy) 17078412663dSChao Yu { 17089a997188SJaegeuk Kim struct discard_policy dp; 170901f9cf6dSChao Yu unsigned int discard_blks; 17109a997188SJaegeuk Kim 171101f9cf6dSChao Yu if (dpolicy) 171201f9cf6dSChao Yu return __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); 17139a997188SJaegeuk Kim 17149a997188SJaegeuk Kim /* wait all */ 17158bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1); 171601f9cf6dSChao Yu discard_blks = __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); 17178bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1); 171801f9cf6dSChao Yu discard_blks += __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); 171901f9cf6dSChao Yu 172001f9cf6dSChao Yu return discard_blks; 17218412663dSChao Yu } 17228412663dSChao Yu 17234e6a8d9bSJaegeuk Kim /* This should be covered by global mutex, &sit_i->sentry_lock */ 172494b1e10eSWei Yongjun static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) 1725275b66b0SChao Yu { 17260b54fb84SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1727004b6862SChao Yu struct discard_cmd *dc; 1728ec9895adSChao Yu bool need_wait = false; 1729275b66b0SChao Yu 173015469963SJaegeuk Kim mutex_lock(&dcc->cmd_lock); 17314d57b86dSChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree(&dcc->root, 17324d57b86dSChao Yu NULL, blkaddr); 1733004b6862SChao Yu if (dc) { 1734ec9895adSChao Yu if (dc->state == D_PREP) { 17353d6a650fSYunlei He __punch_discard_cmd(sbi, dc, blkaddr); 1736ec9895adSChao Yu } else { 1737ec9895adSChao Yu dc->ref++; 1738ec9895adSChao Yu need_wait = true; 1739275b66b0SChao Yu } 1740ec9895adSChao Yu } 1741d431413fSChao Yu mutex_unlock(&dcc->cmd_lock); 1742ec9895adSChao Yu 17432a510c00SChao Yu if (need_wait) 17442a510c00SChao Yu __wait_one_discard_bio(sbi, dc); 1745d431413fSChao Yu } 1746d431413fSChao Yu 17474d57b86dSChao Yu void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi) 1748cce13252SChao Yu { 1749cce13252SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 1750cce13252SChao Yu 1751cce13252SChao Yu if (dcc && dcc->f2fs_issue_discard) { 1752cce13252SChao Yu struct task_struct *discard_thread = dcc->f2fs_issue_discard; 1753cce13252SChao Yu 1754cce13252SChao Yu dcc->f2fs_issue_discard = NULL; 1755cce13252SChao Yu kthread_stop(discard_thread); 175615469963SJaegeuk Kim } 175715469963SJaegeuk Kim } 175815469963SJaegeuk Kim 17598412663dSChao Yu /* This comes from f2fs_put_super */ 176003f2c02dSJaegeuk Kim bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) 1761275b66b0SChao Yu { 1762969d1b18SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 176378997b56SChao Yu struct discard_policy dpolicy; 1764cf5c759fSChao Yu bool dropped; 1765969d1b18SChao Yu 17668bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT, 17678bb4f253SJaegeuk Kim dcc->discard_granularity); 176878997b56SChao Yu __issue_discard_cmd(sbi, &dpolicy); 1769cf5c759fSChao Yu dropped = __drop_discard_cmd(sbi); 1770cf5c759fSChao Yu 17719a997188SJaegeuk Kim /* just to make sure there is no pending discard commands */ 17729a997188SJaegeuk Kim __wait_all_discard_cmd(sbi, NULL); 17732482c432SChao Yu 17742482c432SChao Yu f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt)); 1775cf5c759fSChao Yu return dropped; 1776969d1b18SChao Yu } 1777969d1b18SChao Yu 177815469963SJaegeuk Kim static int issue_discard_thread(void *data) 177915469963SJaegeuk Kim { 178015469963SJaegeuk Kim struct f2fs_sb_info *sbi = data; 178115469963SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 178215469963SJaegeuk Kim wait_queue_head_t *q = &dcc->discard_wait_queue; 178378997b56SChao Yu struct discard_policy dpolicy; 1784*d2d8e896SKonstantin Vyshetsky unsigned int wait_ms = dcc->min_discard_issue_time; 1785969d1b18SChao Yu int issued; 17861d7be270SJaegeuk Kim 17871d7be270SJaegeuk Kim set_freezable(); 17881d7be270SJaegeuk Kim 17891d7be270SJaegeuk Kim do { 1790c35b8d5eSSahitya Tummala if (sbi->gc_mode == GC_URGENT_HIGH || 1791c35b8d5eSSahitya Tummala !f2fs_available_free_memory(sbi, DISCARD_CACHE)) 1792c35b8d5eSSahitya Tummala __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1); 1793c35b8d5eSSahitya Tummala else 17948bb4f253SJaegeuk Kim __init_discard_policy(sbi, &dpolicy, DPOLICY_BG, 179578997b56SChao Yu dcc->discard_granularity); 179678997b56SChao Yu 1797c35b8d5eSSahitya Tummala if (!atomic_read(&dcc->discard_cmd_cnt)) 1798c35b8d5eSSahitya Tummala wait_ms = dpolicy.max_interval; 1799c35b8d5eSSahitya Tummala 1800969d1b18SChao Yu wait_event_interruptible_timeout(*q, 1801969d1b18SChao Yu kthread_should_stop() || freezing(current) || 1802969d1b18SChao Yu dcc->discard_wake, 1803969d1b18SChao Yu msecs_to_jiffies(wait_ms)); 180435a9a766SSheng Yong 180535a9a766SSheng Yong if (dcc->discard_wake) 180635a9a766SSheng Yong dcc->discard_wake = 0; 180735a9a766SSheng Yong 180876c7bfb3SJaegeuk Kim /* clean up pending candidates before going to sleep */ 180976c7bfb3SJaegeuk Kim if (atomic_read(&dcc->queued_discard)) 181076c7bfb3SJaegeuk Kim __wait_all_discard_cmd(sbi, NULL); 181176c7bfb3SJaegeuk Kim 18121d7be270SJaegeuk Kim if (try_to_freeze()) 18131d7be270SJaegeuk Kim continue; 18143b60d802SChao Yu if (f2fs_readonly(sbi->sb)) 18153b60d802SChao Yu continue; 181615469963SJaegeuk Kim if (kthread_should_stop()) 181715469963SJaegeuk Kim return 0; 1818d6184774SYunlei He if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { 1819d6184774SYunlei He wait_ms = dpolicy.max_interval; 1820d6184774SYunlei He continue; 1821d6184774SYunlei He } 182243f8c47eSChao Yu if (!atomic_read(&dcc->discard_cmd_cnt)) 182343f8c47eSChao Yu continue; 182415469963SJaegeuk Kim 1825dc6febb6SChao Yu sb_start_intwrite(sbi->sb); 1826dc6febb6SChao Yu 182778997b56SChao Yu issued = __issue_discard_cmd(sbi, &dpolicy); 1828f9d1dcedSYunlei He if (issued > 0) { 182978997b56SChao Yu __wait_all_discard_cmd(sbi, &dpolicy); 183078997b56SChao Yu wait_ms = dpolicy.min_interval; 1831f9d1dcedSYunlei He } else if (issued == -1) { 1832a7d10cf3SSahitya Tummala wait_ms = f2fs_time_to_wait(sbi, DISCARD_TIME); 1833a7d10cf3SSahitya Tummala if (!wait_ms) 1834f9d1dcedSYunlei He wait_ms = dpolicy.mid_interval; 1835969d1b18SChao Yu } else { 183678997b56SChao Yu wait_ms = dpolicy.max_interval; 1837969d1b18SChao Yu } 183815469963SJaegeuk Kim 1839dc6febb6SChao Yu sb_end_intwrite(sbi->sb); 1840dc6febb6SChao Yu 18411d7be270SJaegeuk Kim } while (!kthread_should_stop()); 18421d7be270SJaegeuk Kim return 0; 184315469963SJaegeuk Kim } 184415469963SJaegeuk Kim 1845f46e8809SDamien Le Moal #ifdef CONFIG_BLK_DEV_ZONED 18463c62be17SJaegeuk Kim static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, 18473c62be17SJaegeuk Kim struct block_device *bdev, block_t blkstart, block_t blklen) 1848f46e8809SDamien Le Moal { 184992592285SJaegeuk Kim sector_t sector, nr_sects; 185010a875f8SKinglong Mee block_t lblkstart = blkstart; 18513c62be17SJaegeuk Kim int devi = 0; 1852f46e8809SDamien Le Moal 18530916878dSDamien Le Moal if (f2fs_is_multi_device(sbi)) { 18543c62be17SJaegeuk Kim devi = f2fs_target_device_index(sbi, blkstart); 185595175dafSDamien Le Moal if (blkstart < FDEV(devi).start_blk || 185695175dafSDamien Le Moal blkstart > FDEV(devi).end_blk) { 1857dcbb4c10SJoe Perches f2fs_err(sbi, "Invalid block %x", blkstart); 185895175dafSDamien Le Moal return -EIO; 185995175dafSDamien Le Moal } 18603c62be17SJaegeuk Kim blkstart -= FDEV(devi).start_blk; 18613c62be17SJaegeuk Kim } 1862f46e8809SDamien Le Moal 186395175dafSDamien Le Moal /* For sequential zones, reset the zone write pointer */ 186495175dafSDamien Le Moal if (f2fs_blkz_is_seq(sbi, devi, blkstart)) { 186592592285SJaegeuk Kim sector = SECTOR_FROM_BLOCK(blkstart); 186692592285SJaegeuk Kim nr_sects = SECTOR_FROM_BLOCK(blklen); 186792592285SJaegeuk Kim 186892592285SJaegeuk Kim if (sector & (bdev_zone_sectors(bdev) - 1) || 186992592285SJaegeuk Kim nr_sects != bdev_zone_sectors(bdev)) { 1870dcbb4c10SJoe Perches f2fs_err(sbi, "(%d) %s: Unaligned zone reset attempted (block %x + %x)", 187192592285SJaegeuk Kim devi, sbi->s_ndevs ? FDEV(devi).path : "", 187292592285SJaegeuk Kim blkstart, blklen); 187392592285SJaegeuk Kim return -EIO; 187492592285SJaegeuk Kim } 1875d50aaeecSJaegeuk Kim trace_f2fs_issue_reset_zone(bdev, blkstart); 18766c1b1da5SAjay Joshi return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET, 18776c1b1da5SAjay Joshi sector, nr_sects, GFP_NOFS); 1878f46e8809SDamien Le Moal } 187995175dafSDamien Le Moal 188095175dafSDamien Le Moal /* For conventional zones, use regular discard if supported */ 188195175dafSDamien Le Moal return __queue_discard_cmd(sbi, bdev, lblkstart, blklen); 1882f46e8809SDamien Le Moal } 1883f46e8809SDamien Le Moal #endif 1884f46e8809SDamien Le Moal 18853c62be17SJaegeuk Kim static int __issue_discard_async(struct f2fs_sb_info *sbi, 18863c62be17SJaegeuk Kim struct block_device *bdev, block_t blkstart, block_t blklen) 18873c62be17SJaegeuk Kim { 18883c62be17SJaegeuk Kim #ifdef CONFIG_BLK_DEV_ZONED 18897f3d7719SDamien Le Moal if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) 18903c62be17SJaegeuk Kim return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); 18913c62be17SJaegeuk Kim #endif 1892c81abe34SJaegeuk Kim return __queue_discard_cmd(sbi, bdev, blkstart, blklen); 18933c62be17SJaegeuk Kim } 18943c62be17SJaegeuk Kim 18951e87a78dSJaegeuk Kim static int f2fs_issue_discard(struct f2fs_sb_info *sbi, 189637208879SJaegeuk Kim block_t blkstart, block_t blklen) 189737208879SJaegeuk Kim { 18983c62be17SJaegeuk Kim sector_t start = blkstart, len = 0; 18993c62be17SJaegeuk Kim struct block_device *bdev; 1900a66cdd98SJaegeuk Kim struct seg_entry *se; 1901a66cdd98SJaegeuk Kim unsigned int offset; 1902a66cdd98SJaegeuk Kim block_t i; 19033c62be17SJaegeuk Kim int err = 0; 1904a66cdd98SJaegeuk Kim 19053c62be17SJaegeuk Kim bdev = f2fs_target_device(sbi, blkstart, NULL); 19063c62be17SJaegeuk Kim 19073c62be17SJaegeuk Kim for (i = blkstart; i < blkstart + blklen; i++, len++) { 19083c62be17SJaegeuk Kim if (i != start) { 19093c62be17SJaegeuk Kim struct block_device *bdev2 = 19103c62be17SJaegeuk Kim f2fs_target_device(sbi, i, NULL); 19113c62be17SJaegeuk Kim 19123c62be17SJaegeuk Kim if (bdev2 != bdev) { 19133c62be17SJaegeuk Kim err = __issue_discard_async(sbi, bdev, 19143c62be17SJaegeuk Kim start, len); 19153c62be17SJaegeuk Kim if (err) 19163c62be17SJaegeuk Kim return err; 19173c62be17SJaegeuk Kim bdev = bdev2; 19183c62be17SJaegeuk Kim start = i; 19193c62be17SJaegeuk Kim len = 0; 19203c62be17SJaegeuk Kim } 19213c62be17SJaegeuk Kim } 19223c62be17SJaegeuk Kim 1923a66cdd98SJaegeuk Kim se = get_seg_entry(sbi, GET_SEGNO(sbi, i)); 1924a66cdd98SJaegeuk Kim offset = GET_BLKOFF_FROM_SEG0(sbi, i); 1925a66cdd98SJaegeuk Kim 19264f993264SChao Yu if (f2fs_block_unit_discard(sbi) && 19274f993264SChao Yu !f2fs_test_and_set_bit(offset, se->discard_map)) 1928a66cdd98SJaegeuk Kim sbi->discard_blks--; 1929a66cdd98SJaegeuk Kim } 1930f46e8809SDamien Le Moal 19313c62be17SJaegeuk Kim if (len) 19323c62be17SJaegeuk Kim err = __issue_discard_async(sbi, bdev, start, len); 19333c62be17SJaegeuk Kim return err; 19341e87a78dSJaegeuk Kim } 19351e87a78dSJaegeuk Kim 193625290fa5SJaegeuk Kim static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, 193725290fa5SJaegeuk Kim bool check_only) 1938adf4983bSJaegeuk Kim { 1939b2955550SJaegeuk Kim int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 1940b2955550SJaegeuk Kim int max_blocks = sbi->blocks_per_seg; 19414b2fecc8SJaegeuk Kim struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); 1942b2955550SJaegeuk Kim unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 1943b2955550SJaegeuk Kim unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 1944a66cdd98SJaegeuk Kim unsigned long *discard_map = (unsigned long *)se->discard_map; 194560a3b782SJaegeuk Kim unsigned long *dmap = SIT_I(sbi)->tmp_map; 1946b2955550SJaegeuk Kim unsigned int start = 0, end = -1; 1947c473f1a9SChao Yu bool force = (cpc->reason & CP_DISCARD); 1948a7eeb823SChao Yu struct discard_entry *de = NULL; 194946f84c2cSChao Yu struct list_head *head = &SM_I(sbi)->dcc_info->entry_list; 1950b2955550SJaegeuk Kim int i; 1951b2955550SJaegeuk Kim 19524f993264SChao Yu if (se->valid_blocks == max_blocks || !f2fs_hw_support_discard(sbi) || 19534f993264SChao Yu !f2fs_block_unit_discard(sbi)) 195425290fa5SJaegeuk Kim return false; 1955b2955550SJaegeuk Kim 1956a66cdd98SJaegeuk Kim if (!force) { 19577d20c8abSChao Yu if (!f2fs_realtime_discard_enable(sbi) || !se->valid_blocks || 19580b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info->nr_discards >= 19590b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info->max_discards) 196025290fa5SJaegeuk Kim return false; 19614b2fecc8SJaegeuk Kim } 1962b2955550SJaegeuk Kim 1963b2955550SJaegeuk Kim /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ 1964b2955550SJaegeuk Kim for (i = 0; i < entries; i++) 1965a66cdd98SJaegeuk Kim dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : 1966d7bc2484SJaegeuk Kim (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; 1967b2955550SJaegeuk Kim 19680b54fb84SJaegeuk Kim while (force || SM_I(sbi)->dcc_info->nr_discards <= 19690b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info->max_discards) { 1970b2955550SJaegeuk Kim start = __find_rev_next_bit(dmap, max_blocks, end + 1); 1971b2955550SJaegeuk Kim if (start >= max_blocks) 1972b2955550SJaegeuk Kim break; 1973b2955550SJaegeuk Kim 1974b2955550SJaegeuk Kim end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); 1975c7b41e16SYunlei He if (force && start && end != max_blocks 1976c7b41e16SYunlei He && (end - start) < cpc->trim_minlen) 1977c7b41e16SYunlei He continue; 1978c7b41e16SYunlei He 197925290fa5SJaegeuk Kim if (check_only) 198025290fa5SJaegeuk Kim return true; 198125290fa5SJaegeuk Kim 1982a7eeb823SChao Yu if (!de) { 1983a7eeb823SChao Yu de = f2fs_kmem_cache_alloc(discard_entry_slab, 198432410577SChao Yu GFP_F2FS_ZERO, true, NULL); 1985a7eeb823SChao Yu de->start_blkaddr = START_BLOCK(sbi, cpc->trim_start); 1986a7eeb823SChao Yu list_add_tail(&de->list, head); 1987a7eeb823SChao Yu } 1988a7eeb823SChao Yu 1989a7eeb823SChao Yu for (i = start; i < end; i++) 1990a7eeb823SChao Yu __set_bit_le(i, (void *)de->discard_map); 1991a7eeb823SChao Yu 1992a7eeb823SChao Yu SM_I(sbi)->dcc_info->nr_discards += end - start; 1993b2955550SJaegeuk Kim } 199425290fa5SJaegeuk Kim return false; 1995b2955550SJaegeuk Kim } 1996b2955550SJaegeuk Kim 1997af8ff65bSChao Yu static void release_discard_addr(struct discard_entry *entry) 1998af8ff65bSChao Yu { 1999af8ff65bSChao Yu list_del(&entry->list); 2000af8ff65bSChao Yu kmem_cache_free(discard_entry_slab, entry); 2001af8ff65bSChao Yu } 2002af8ff65bSChao Yu 20034d57b86dSChao Yu void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi) 20044b2fecc8SJaegeuk Kim { 200546f84c2cSChao Yu struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list); 20064b2fecc8SJaegeuk Kim struct discard_entry *entry, *this; 20074b2fecc8SJaegeuk Kim 20084b2fecc8SJaegeuk Kim /* drop caches */ 2009af8ff65bSChao Yu list_for_each_entry_safe(entry, this, head, list) 2010af8ff65bSChao Yu release_discard_addr(entry); 20114b2fecc8SJaegeuk Kim } 20124b2fecc8SJaegeuk Kim 20130a8165d7SJaegeuk Kim /* 20144d57b86dSChao Yu * Should call f2fs_clear_prefree_segments after checkpoint is done. 2015351df4b2SJaegeuk Kim */ 2016351df4b2SJaegeuk Kim static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) 2017351df4b2SJaegeuk Kim { 2018351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 2019b65ee148SChao Yu unsigned int segno; 2020351df4b2SJaegeuk Kim 2021351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 20227cd8558bSJaegeuk Kim for_each_set_bit(segno, dirty_i->dirty_segmap[PRE], MAIN_SEGS(sbi)) 2023d0b9e42aSChao Yu __set_test_and_free(sbi, segno, false); 2024351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 2025351df4b2SJaegeuk Kim } 2026351df4b2SJaegeuk Kim 20274d57b86dSChao Yu void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, 20284d57b86dSChao Yu struct cp_control *cpc) 2029351df4b2SJaegeuk Kim { 2030969d1b18SChao Yu struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 2031969d1b18SChao Yu struct list_head *head = &dcc->entry_list; 20322d7b822aSChao Yu struct discard_entry *entry, *this; 2033351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 203429e59c14SChangman Lee unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; 203529e59c14SChangman Lee unsigned int start = 0, end = -1; 203636abef4eSJaegeuk Kim unsigned int secno, start_segno; 2037c473f1a9SChao Yu bool force = (cpc->reason & CP_DISCARD); 20384f993264SChao Yu bool section_alignment = F2FS_OPTION(sbi).discard_unit == 20394f993264SChao Yu DISCARD_UNIT_SECTION; 20404f993264SChao Yu 20414f993264SChao Yu if (f2fs_lfs_mode(sbi) && __is_large_section(sbi)) 20424f993264SChao Yu section_alignment = true; 2043351df4b2SJaegeuk Kim 2044351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 204529e59c14SChangman Lee 2046351df4b2SJaegeuk Kim while (1) { 204729e59c14SChangman Lee int i; 2048ad6672bbSYunlong Song 20494f993264SChao Yu if (section_alignment && end != -1) 2050ad6672bbSYunlong Song end--; 20517cd8558bSJaegeuk Kim start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1); 20527cd8558bSJaegeuk Kim if (start >= MAIN_SEGS(sbi)) 2053351df4b2SJaegeuk Kim break; 20547cd8558bSJaegeuk Kim end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi), 20557cd8558bSJaegeuk Kim start + 1); 2056351df4b2SJaegeuk Kim 20574f993264SChao Yu if (section_alignment) { 2058ad6672bbSYunlong Song start = rounddown(start, sbi->segs_per_sec); 2059ad6672bbSYunlong Song end = roundup(end, sbi->segs_per_sec); 2060ad6672bbSYunlong Song } 2061351df4b2SJaegeuk Kim 2062ad6672bbSYunlong Song for (i = start; i < end; i++) { 2063ad6672bbSYunlong Song if (test_and_clear_bit(i, prefree_map)) 2064ad6672bbSYunlong Song dirty_i->nr_dirty[PRE]--; 2065ad6672bbSYunlong Song } 206629e59c14SChangman Lee 20677d20c8abSChao Yu if (!f2fs_realtime_discard_enable(sbi)) 2068650d3c4eSYunlei He continue; 2069650d3c4eSYunlei He 2070650d3c4eSYunlei He if (force && start >= cpc->trim_start && 2071650d3c4eSYunlei He (end - 1) <= cpc->trim_end) 207229e59c14SChangman Lee continue; 207329e59c14SChangman Lee 2074b0332a0fSChao Yu if (!f2fs_lfs_mode(sbi) || !__is_large_section(sbi)) { 207537208879SJaegeuk Kim f2fs_issue_discard(sbi, START_BLOCK(sbi, start), 207637208879SJaegeuk Kim (end - start) << sbi->log_blocks_per_seg); 207736abef4eSJaegeuk Kim continue; 207836abef4eSJaegeuk Kim } 207936abef4eSJaegeuk Kim next: 20804ddb1a4dSJaegeuk Kim secno = GET_SEC_FROM_SEG(sbi, start); 20814ddb1a4dSJaegeuk Kim start_segno = GET_SEG_FROM_SEC(sbi, secno); 208236abef4eSJaegeuk Kim if (!IS_CURSEC(sbi, secno) && 2083302bd348SJaegeuk Kim !get_valid_blocks(sbi, start, true)) 208436abef4eSJaegeuk Kim f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), 208536abef4eSJaegeuk Kim sbi->segs_per_sec << sbi->log_blocks_per_seg); 208636abef4eSJaegeuk Kim 208736abef4eSJaegeuk Kim start = start_segno + sbi->segs_per_sec; 208836abef4eSJaegeuk Kim if (start < end) 208936abef4eSJaegeuk Kim goto next; 20908b107f5bSJaegeuk Kim else 20918b107f5bSJaegeuk Kim end = start - 1; 2092351df4b2SJaegeuk Kim } 2093351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 2094b2955550SJaegeuk Kim 20954f993264SChao Yu if (!f2fs_block_unit_discard(sbi)) 20964f993264SChao Yu goto wakeup; 20974f993264SChao Yu 2098b2955550SJaegeuk Kim /* send small discards */ 20992d7b822aSChao Yu list_for_each_entry_safe(entry, this, head, list) { 2100a7eeb823SChao Yu unsigned int cur_pos = 0, next_pos, len, total_len = 0; 2101a7eeb823SChao Yu bool is_valid = test_bit_le(0, entry->discard_map); 2102a7eeb823SChao Yu 2103a7eeb823SChao Yu find_next: 2104a7eeb823SChao Yu if (is_valid) { 2105a7eeb823SChao Yu next_pos = find_next_zero_bit_le(entry->discard_map, 2106a7eeb823SChao Yu sbi->blocks_per_seg, cur_pos); 2107a7eeb823SChao Yu len = next_pos - cur_pos; 2108a7eeb823SChao Yu 21097beb01f7SChao Yu if (f2fs_sb_has_blkzoned(sbi) || 2110acfd2810SDamien Le Moal (force && len < cpc->trim_minlen)) 2111836b5a63SJaegeuk Kim goto skip; 2112a7eeb823SChao Yu 2113a7eeb823SChao Yu f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, 2114a7eeb823SChao Yu len); 2115a7eeb823SChao Yu total_len += len; 2116a7eeb823SChao Yu } else { 2117a7eeb823SChao Yu next_pos = find_next_bit_le(entry->discard_map, 2118a7eeb823SChao Yu sbi->blocks_per_seg, cur_pos); 2119a7eeb823SChao Yu } 2120836b5a63SJaegeuk Kim skip: 2121a7eeb823SChao Yu cur_pos = next_pos; 2122a7eeb823SChao Yu is_valid = !is_valid; 2123a7eeb823SChao Yu 2124a7eeb823SChao Yu if (cur_pos < sbi->blocks_per_seg) 2125a7eeb823SChao Yu goto find_next; 2126a7eeb823SChao Yu 2127af8ff65bSChao Yu release_discard_addr(entry); 2128969d1b18SChao Yu dcc->nr_discards -= total_len; 2129b2955550SJaegeuk Kim } 213034e159daSChao Yu 21314f993264SChao Yu wakeup: 213201983c71SJaegeuk Kim wake_up_discard_thread(sbi, false); 2133351df4b2SJaegeuk Kim } 2134351df4b2SJaegeuk Kim 21354d674904SFengnan Chang int f2fs_start_discard_thread(struct f2fs_sb_info *sbi) 21360b54fb84SJaegeuk Kim { 213715469963SJaegeuk Kim dev_t dev = sbi->sb->s_bdev->bd_dev; 21384d674904SFengnan Chang struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 21394d674904SFengnan Chang int err = 0; 21404d674904SFengnan Chang 21414d674904SFengnan Chang if (!f2fs_realtime_discard_enable(sbi)) 21424d674904SFengnan Chang return 0; 21434d674904SFengnan Chang 21444d674904SFengnan Chang dcc->f2fs_issue_discard = kthread_run(issue_discard_thread, sbi, 21454d674904SFengnan Chang "f2fs_discard-%u:%u", MAJOR(dev), MINOR(dev)); 21464d674904SFengnan Chang if (IS_ERR(dcc->f2fs_issue_discard)) 21474d674904SFengnan Chang err = PTR_ERR(dcc->f2fs_issue_discard); 21484d674904SFengnan Chang 21494d674904SFengnan Chang return err; 21504d674904SFengnan Chang } 21514d674904SFengnan Chang 21524d674904SFengnan Chang static int create_discard_cmd_control(struct f2fs_sb_info *sbi) 21534d674904SFengnan Chang { 21540b54fb84SJaegeuk Kim struct discard_cmd_control *dcc; 2155ba48a33eSChao Yu int err = 0, i; 21560b54fb84SJaegeuk Kim 21570b54fb84SJaegeuk Kim if (SM_I(sbi)->dcc_info) { 21580b54fb84SJaegeuk Kim dcc = SM_I(sbi)->dcc_info; 21590b54fb84SJaegeuk Kim goto init_thread; 21600b54fb84SJaegeuk Kim } 21610b54fb84SJaegeuk Kim 2162acbf054dSChao Yu dcc = f2fs_kzalloc(sbi, sizeof(struct discard_cmd_control), GFP_KERNEL); 21630b54fb84SJaegeuk Kim if (!dcc) 21640b54fb84SJaegeuk Kim return -ENOMEM; 21650b54fb84SJaegeuk Kim 2166969d1b18SChao Yu dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY; 21674f993264SChao Yu if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SEGMENT) 21684f993264SChao Yu dcc->discard_granularity = sbi->blocks_per_seg; 21694f993264SChao Yu else if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SECTION) 21704f993264SChao Yu dcc->discard_granularity = BLKS_PER_SEC(sbi); 21714f993264SChao Yu 217246f84c2cSChao Yu INIT_LIST_HEAD(&dcc->entry_list); 217378997b56SChao Yu for (i = 0; i < MAX_PLIST_NUM; i++) 2174ba48a33eSChao Yu INIT_LIST_HEAD(&dcc->pend_list[i]); 217546f84c2cSChao Yu INIT_LIST_HEAD(&dcc->wait_list); 21768412663dSChao Yu INIT_LIST_HEAD(&dcc->fstrim_list); 217715469963SJaegeuk Kim mutex_init(&dcc->cmd_lock); 21788b8dd65fSChao Yu atomic_set(&dcc->issued_discard, 0); 217972691af6SJaegeuk Kim atomic_set(&dcc->queued_discard, 0); 21805f32366aSChao Yu atomic_set(&dcc->discard_cmd_cnt, 0); 21810b54fb84SJaegeuk Kim dcc->nr_discards = 0; 2182d618ebafSChao Yu dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg; 2183*d2d8e896SKonstantin Vyshetsky dcc->max_discard_request = DEF_MAX_DISCARD_REQUEST; 2184*d2d8e896SKonstantin Vyshetsky dcc->min_discard_issue_time = DEF_MIN_DISCARD_ISSUE_TIME; 2185*d2d8e896SKonstantin Vyshetsky dcc->mid_discard_issue_time = DEF_MID_DISCARD_ISSUE_TIME; 2186*d2d8e896SKonstantin Vyshetsky dcc->max_discard_issue_time = DEF_MAX_DISCARD_ISSUE_TIME; 2187d84d1cbdSChao Yu dcc->undiscard_blks = 0; 218820ee4382SChao Yu dcc->next_pos = 0; 21894dada3fdSChao Yu dcc->root = RB_ROOT_CACHED; 219067fce70bSChao Yu dcc->rbtree_check = false; 21910b54fb84SJaegeuk Kim 219215469963SJaegeuk Kim init_waitqueue_head(&dcc->discard_wait_queue); 21930b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info = dcc; 21940b54fb84SJaegeuk Kim init_thread: 21954d674904SFengnan Chang err = f2fs_start_discard_thread(sbi); 21964d674904SFengnan Chang if (err) { 2197c8eb7024SChao Yu kfree(dcc); 219815469963SJaegeuk Kim SM_I(sbi)->dcc_info = NULL; 219915469963SJaegeuk Kim } 220015469963SJaegeuk Kim 22010b54fb84SJaegeuk Kim return err; 22020b54fb84SJaegeuk Kim } 22030b54fb84SJaegeuk Kim 2204f099405fSChao Yu static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi) 22050b54fb84SJaegeuk Kim { 22060b54fb84SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 22070b54fb84SJaegeuk Kim 2208f099405fSChao Yu if (!dcc) 2209f099405fSChao Yu return; 2210f099405fSChao Yu 22114d57b86dSChao Yu f2fs_stop_discard_thread(sbi); 2212f099405fSChao Yu 221304f9287aSChao Yu /* 221404f9287aSChao Yu * Recovery can cache discard commands, so in error path of 221504f9287aSChao Yu * fill_super(), it needs to give a chance to handle them. 221604f9287aSChao Yu */ 221704f9287aSChao Yu if (unlikely(atomic_read(&dcc->discard_cmd_cnt))) 221804f9287aSChao Yu f2fs_issue_discard_timeout(sbi); 221904f9287aSChao Yu 2220c8eb7024SChao Yu kfree(dcc); 22210b54fb84SJaegeuk Kim SM_I(sbi)->dcc_info = NULL; 22220b54fb84SJaegeuk Kim } 22230b54fb84SJaegeuk Kim 2224184a5cd2SChao Yu static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) 2225351df4b2SJaegeuk Kim { 2226351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 2227184a5cd2SChao Yu 2228184a5cd2SChao Yu if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap)) { 2229351df4b2SJaegeuk Kim sit_i->dirty_sentries++; 2230184a5cd2SChao Yu return false; 2231184a5cd2SChao Yu } 2232184a5cd2SChao Yu 2233184a5cd2SChao Yu return true; 2234351df4b2SJaegeuk Kim } 2235351df4b2SJaegeuk Kim 2236351df4b2SJaegeuk Kim static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type, 2237351df4b2SJaegeuk Kim unsigned int segno, int modified) 2238351df4b2SJaegeuk Kim { 2239351df4b2SJaegeuk Kim struct seg_entry *se = get_seg_entry(sbi, segno); 22405f029c04SYi Zhuang 2241351df4b2SJaegeuk Kim se->type = type; 2242351df4b2SJaegeuk Kim if (modified) 2243351df4b2SJaegeuk Kim __mark_sit_entry_dirty(sbi, segno); 2244351df4b2SJaegeuk Kim } 2245351df4b2SJaegeuk Kim 2246c5d02785SChao Yu static inline unsigned long long get_segment_mtime(struct f2fs_sb_info *sbi, 2247c5d02785SChao Yu block_t blkaddr) 22486f3a01aeSChao Yu { 22496f3a01aeSChao Yu unsigned int segno = GET_SEGNO(sbi, blkaddr); 2250c5d02785SChao Yu 2251c5d02785SChao Yu if (segno == NULL_SEGNO) 2252c5d02785SChao Yu return 0; 2253c5d02785SChao Yu return get_seg_entry(sbi, segno)->mtime; 2254c5d02785SChao Yu } 2255c5d02785SChao Yu 2256c5d02785SChao Yu static void update_segment_mtime(struct f2fs_sb_info *sbi, block_t blkaddr, 2257c5d02785SChao Yu unsigned long long old_mtime) 2258c5d02785SChao Yu { 2259c5d02785SChao Yu struct seg_entry *se; 2260c5d02785SChao Yu unsigned int segno = GET_SEGNO(sbi, blkaddr); 2261c5d02785SChao Yu unsigned long long ctime = get_mtime(sbi, false); 2262c5d02785SChao Yu unsigned long long mtime = old_mtime ? old_mtime : ctime; 2263c5d02785SChao Yu 2264c5d02785SChao Yu if (segno == NULL_SEGNO) 2265c5d02785SChao Yu return; 2266c5d02785SChao Yu 2267c5d02785SChao Yu se = get_seg_entry(sbi, segno); 22686f3a01aeSChao Yu 22696f3a01aeSChao Yu if (!se->mtime) 22706f3a01aeSChao Yu se->mtime = mtime; 22716f3a01aeSChao Yu else 22726f3a01aeSChao Yu se->mtime = div_u64(se->mtime * se->valid_blocks + mtime, 22736f3a01aeSChao Yu se->valid_blocks + 1); 22746f3a01aeSChao Yu 2275c5d02785SChao Yu if (ctime > SIT_I(sbi)->max_mtime) 2276c5d02785SChao Yu SIT_I(sbi)->max_mtime = ctime; 22776f3a01aeSChao Yu } 22786f3a01aeSChao Yu 2279351df4b2SJaegeuk Kim static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) 2280351df4b2SJaegeuk Kim { 2281351df4b2SJaegeuk Kim struct seg_entry *se; 2282351df4b2SJaegeuk Kim unsigned int segno, offset; 2283351df4b2SJaegeuk Kim long int new_vblocks; 22846415fedcSYunlong Song bool exist; 22856415fedcSYunlong Song #ifdef CONFIG_F2FS_CHECK_FS 22866415fedcSYunlong Song bool mir_exist; 22876415fedcSYunlong Song #endif 2288351df4b2SJaegeuk Kim 2289351df4b2SJaegeuk Kim segno = GET_SEGNO(sbi, blkaddr); 2290351df4b2SJaegeuk Kim 2291351df4b2SJaegeuk Kim se = get_seg_entry(sbi, segno); 2292351df4b2SJaegeuk Kim new_vblocks = se->valid_blocks + del; 2293491c0854SJaegeuk Kim offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 2294351df4b2SJaegeuk Kim 22959feffe14SZhihao Cheng f2fs_bug_on(sbi, (new_vblocks < 0 || 2296de881df9SAravind Ramesh (new_vblocks > f2fs_usable_blks_in_seg(sbi, segno)))); 2297351df4b2SJaegeuk Kim 2298351df4b2SJaegeuk Kim se->valid_blocks = new_vblocks; 2299351df4b2SJaegeuk Kim 2300351df4b2SJaegeuk Kim /* Update valid block bitmap */ 2301351df4b2SJaegeuk Kim if (del > 0) { 23026415fedcSYunlong Song exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); 2303355e7891SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 23046415fedcSYunlong Song mir_exist = f2fs_test_and_set_bit(offset, 23056415fedcSYunlong Song se->cur_valid_map_mir); 23066415fedcSYunlong Song if (unlikely(exist != mir_exist)) { 2307dcbb4c10SJoe Perches f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d", 23086415fedcSYunlong Song blkaddr, exist); 230905796763SJaegeuk Kim f2fs_bug_on(sbi, 1); 2310355e7891SChao Yu } 23116415fedcSYunlong Song #endif 23126415fedcSYunlong Song if (unlikely(exist)) { 2313dcbb4c10SJoe Perches f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", 2314dcbb4c10SJoe Perches blkaddr); 23156415fedcSYunlong Song f2fs_bug_on(sbi, 1); 231635ee82caSYunlong Song se->valid_blocks--; 231735ee82caSYunlong Song del = 0; 23186415fedcSYunlong Song } 23196415fedcSYunlong Song 23204f993264SChao Yu if (f2fs_block_unit_discard(sbi) && 23214f993264SChao Yu !f2fs_test_and_set_bit(offset, se->discard_map)) 2322a66cdd98SJaegeuk Kim sbi->discard_blks--; 2323720037f9SJaegeuk Kim 2324899fee36SChao Yu /* 2325899fee36SChao Yu * SSR should never reuse block which is checkpointed 2326899fee36SChao Yu * or newly invalidated. 2327899fee36SChao Yu */ 2328899fee36SChao Yu if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) { 2329720037f9SJaegeuk Kim if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) 2330720037f9SJaegeuk Kim se->ckpt_valid_blocks++; 2331720037f9SJaegeuk Kim } 2332351df4b2SJaegeuk Kim } else { 23336415fedcSYunlong Song exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map); 2334355e7891SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 23356415fedcSYunlong Song mir_exist = f2fs_test_and_clear_bit(offset, 23366415fedcSYunlong Song se->cur_valid_map_mir); 23376415fedcSYunlong Song if (unlikely(exist != mir_exist)) { 2338dcbb4c10SJoe Perches f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", 23396415fedcSYunlong Song blkaddr, exist); 234005796763SJaegeuk Kim f2fs_bug_on(sbi, 1); 2341355e7891SChao Yu } 23426415fedcSYunlong Song #endif 23436415fedcSYunlong Song if (unlikely(!exist)) { 2344dcbb4c10SJoe Perches f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", 2345dcbb4c10SJoe Perches blkaddr); 23466415fedcSYunlong Song f2fs_bug_on(sbi, 1); 234735ee82caSYunlong Song se->valid_blocks++; 234835ee82caSYunlong Song del = 0; 23494354994fSDaniel Rosenberg } else if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { 23504354994fSDaniel Rosenberg /* 23514354994fSDaniel Rosenberg * If checkpoints are off, we must not reuse data that 23524354994fSDaniel Rosenberg * was used in the previous checkpoint. If it was used 23534354994fSDaniel Rosenberg * before, we must track that to know how much space we 23544354994fSDaniel Rosenberg * really have. 23554354994fSDaniel Rosenberg */ 2356c9c8ed50SChao Yu if (f2fs_test_bit(offset, se->ckpt_valid_map)) { 2357c9c8ed50SChao Yu spin_lock(&sbi->stat_lock); 23584354994fSDaniel Rosenberg sbi->unusable_block_count++; 2359c9c8ed50SChao Yu spin_unlock(&sbi->stat_lock); 2360c9c8ed50SChao Yu } 23616415fedcSYunlong Song } 23626415fedcSYunlong Song 23634f993264SChao Yu if (f2fs_block_unit_discard(sbi) && 23644f993264SChao Yu f2fs_test_and_clear_bit(offset, se->discard_map)) 2365a66cdd98SJaegeuk Kim sbi->discard_blks++; 2366351df4b2SJaegeuk Kim } 2367351df4b2SJaegeuk Kim if (!f2fs_test_bit(offset, se->ckpt_valid_map)) 2368351df4b2SJaegeuk Kim se->ckpt_valid_blocks += del; 2369351df4b2SJaegeuk Kim 2370351df4b2SJaegeuk Kim __mark_sit_entry_dirty(sbi, segno); 2371351df4b2SJaegeuk Kim 2372351df4b2SJaegeuk Kim /* update total number of valid blocks to be written in ckpt area */ 2373351df4b2SJaegeuk Kim SIT_I(sbi)->written_valid_blocks += del; 2374351df4b2SJaegeuk Kim 23752c70c5e3SChao Yu if (__is_large_section(sbi)) 2376351df4b2SJaegeuk Kim get_sec_entry(sbi, segno)->valid_blocks += del; 2377351df4b2SJaegeuk Kim } 2378351df4b2SJaegeuk Kim 23794d57b86dSChao Yu void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) 2380351df4b2SJaegeuk Kim { 2381351df4b2SJaegeuk Kim unsigned int segno = GET_SEGNO(sbi, addr); 2382351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 2383351df4b2SJaegeuk Kim 23849850cf4aSJaegeuk Kim f2fs_bug_on(sbi, addr == NULL_ADDR); 23854c8ff709SChao Yu if (addr == NEW_ADDR || addr == COMPRESS_ADDR) 2386351df4b2SJaegeuk Kim return; 2387351df4b2SJaegeuk Kim 23886aa58d8aSChao Yu invalidate_mapping_pages(META_MAPPING(sbi), addr, addr); 23896ce19affSChao Yu f2fs_invalidate_compress_page(sbi, addr); 23906aa58d8aSChao Yu 2391351df4b2SJaegeuk Kim /* add it into sit main buffer */ 23923d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 2393351df4b2SJaegeuk Kim 2394c5d02785SChao Yu update_segment_mtime(sbi, addr, 0); 2395351df4b2SJaegeuk Kim update_sit_entry(sbi, addr, -1); 2396351df4b2SJaegeuk Kim 2397351df4b2SJaegeuk Kim /* add it into dirty seglist */ 2398351df4b2SJaegeuk Kim locate_dirty_segment(sbi, segno); 2399351df4b2SJaegeuk Kim 24003d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 2401351df4b2SJaegeuk Kim } 2402351df4b2SJaegeuk Kim 24034d57b86dSChao Yu bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) 24046e2c64adSJaegeuk Kim { 24056e2c64adSJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 24066e2c64adSJaegeuk Kim unsigned int segno, offset; 24076e2c64adSJaegeuk Kim struct seg_entry *se; 24086e2c64adSJaegeuk Kim bool is_cp = false; 24096e2c64adSJaegeuk Kim 241093770ab7SChao Yu if (!__is_valid_data_blkaddr(blkaddr)) 24116e2c64adSJaegeuk Kim return true; 24126e2c64adSJaegeuk Kim 24133d26fa6bSChao Yu down_read(&sit_i->sentry_lock); 24146e2c64adSJaegeuk Kim 24156e2c64adSJaegeuk Kim segno = GET_SEGNO(sbi, blkaddr); 24166e2c64adSJaegeuk Kim se = get_seg_entry(sbi, segno); 24176e2c64adSJaegeuk Kim offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 24186e2c64adSJaegeuk Kim 24196e2c64adSJaegeuk Kim if (f2fs_test_bit(offset, se->ckpt_valid_map)) 24206e2c64adSJaegeuk Kim is_cp = true; 24216e2c64adSJaegeuk Kim 24223d26fa6bSChao Yu up_read(&sit_i->sentry_lock); 24236e2c64adSJaegeuk Kim 24246e2c64adSJaegeuk Kim return is_cp; 24256e2c64adSJaegeuk Kim } 24266e2c64adSJaegeuk Kim 24270a8165d7SJaegeuk Kim /* 2428351df4b2SJaegeuk Kim * This function should be resided under the curseg_mutex lock 2429351df4b2SJaegeuk Kim */ 2430351df4b2SJaegeuk Kim static void __add_sum_entry(struct f2fs_sb_info *sbi, int type, 2431e79efe3bSHaicheng Li struct f2fs_summary *sum) 2432351df4b2SJaegeuk Kim { 2433351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2434351df4b2SJaegeuk Kim void *addr = curseg->sum_blk; 24355f029c04SYi Zhuang 2436e79efe3bSHaicheng Li addr += curseg->next_blkoff * sizeof(struct f2fs_summary); 2437351df4b2SJaegeuk Kim memcpy(addr, sum, sizeof(struct f2fs_summary)); 2438351df4b2SJaegeuk Kim } 2439351df4b2SJaegeuk Kim 24400a8165d7SJaegeuk Kim /* 2441351df4b2SJaegeuk Kim * Calculate the number of current summary pages for writing 2442351df4b2SJaegeuk Kim */ 24434d57b86dSChao Yu int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) 2444351df4b2SJaegeuk Kim { 2445351df4b2SJaegeuk Kim int valid_sum_count = 0; 24469a47938bSFan Li int i, sum_in_page; 2447351df4b2SJaegeuk Kim 2448351df4b2SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 2449351df4b2SJaegeuk Kim if (sbi->ckpt->alloc_type[i] == SSR) 2450351df4b2SJaegeuk Kim valid_sum_count += sbi->blocks_per_seg; 24513fa06d7bSChao Yu else { 24523fa06d7bSChao Yu if (for_ra) 24533fa06d7bSChao Yu valid_sum_count += le16_to_cpu( 24543fa06d7bSChao Yu F2FS_CKPT(sbi)->cur_data_blkoff[i]); 2455351df4b2SJaegeuk Kim else 2456351df4b2SJaegeuk Kim valid_sum_count += curseg_blkoff(sbi, i); 2457351df4b2SJaegeuk Kim } 24583fa06d7bSChao Yu } 2459351df4b2SJaegeuk Kim 246009cbfeafSKirill A. Shutemov sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE - 24619a47938bSFan Li SUM_FOOTER_SIZE) / SUMMARY_SIZE; 24629a47938bSFan Li if (valid_sum_count <= sum_in_page) 2463351df4b2SJaegeuk Kim return 1; 24649a47938bSFan Li else if ((valid_sum_count - sum_in_page) <= 246509cbfeafSKirill A. Shutemov (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) 2466351df4b2SJaegeuk Kim return 2; 2467351df4b2SJaegeuk Kim return 3; 2468351df4b2SJaegeuk Kim } 2469351df4b2SJaegeuk Kim 24700a8165d7SJaegeuk Kim /* 2471351df4b2SJaegeuk Kim * Caller should put this summary page 2472351df4b2SJaegeuk Kim */ 24734d57b86dSChao Yu struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) 2474351df4b2SJaegeuk Kim { 247586f33603SJaegeuk Kim if (unlikely(f2fs_cp_error(sbi))) 247686f33603SJaegeuk Kim return ERR_PTR(-EIO); 247786f33603SJaegeuk Kim return f2fs_get_meta_page_retry(sbi, GET_SUM_BLOCK(sbi, segno)); 2478351df4b2SJaegeuk Kim } 2479351df4b2SJaegeuk Kim 24804d57b86dSChao Yu void f2fs_update_meta_page(struct f2fs_sb_info *sbi, 24814d57b86dSChao Yu void *src, block_t blk_addr) 2482381722d2SChao Yu { 24834d57b86dSChao Yu struct page *page = f2fs_grab_meta_page(sbi, blk_addr); 2484381722d2SChao Yu 24850537b811SChao Yu memcpy(page_address(page), src, PAGE_SIZE); 2486381722d2SChao Yu set_page_dirty(page); 2487381722d2SChao Yu f2fs_put_page(page, 1); 2488381722d2SChao Yu } 2489381722d2SChao Yu 2490351df4b2SJaegeuk Kim static void write_sum_page(struct f2fs_sb_info *sbi, 2491351df4b2SJaegeuk Kim struct f2fs_summary_block *sum_blk, block_t blk_addr) 2492351df4b2SJaegeuk Kim { 24934d57b86dSChao Yu f2fs_update_meta_page(sbi, (void *)sum_blk, blk_addr); 2494351df4b2SJaegeuk Kim } 2495351df4b2SJaegeuk Kim 2496b7ad7512SChao Yu static void write_current_sum_page(struct f2fs_sb_info *sbi, 2497b7ad7512SChao Yu int type, block_t blk_addr) 2498b7ad7512SChao Yu { 2499b7ad7512SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 25004d57b86dSChao Yu struct page *page = f2fs_grab_meta_page(sbi, blk_addr); 2501b7ad7512SChao Yu struct f2fs_summary_block *src = curseg->sum_blk; 2502b7ad7512SChao Yu struct f2fs_summary_block *dst; 2503b7ad7512SChao Yu 2504b7ad7512SChao Yu dst = (struct f2fs_summary_block *)page_address(page); 250581114baaSChao Yu memset(dst, 0, PAGE_SIZE); 2506b7ad7512SChao Yu 2507b7ad7512SChao Yu mutex_lock(&curseg->curseg_mutex); 2508b7ad7512SChao Yu 2509b7ad7512SChao Yu down_read(&curseg->journal_rwsem); 2510b7ad7512SChao Yu memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE); 2511b7ad7512SChao Yu up_read(&curseg->journal_rwsem); 2512b7ad7512SChao Yu 2513b7ad7512SChao Yu memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE); 2514b7ad7512SChao Yu memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE); 2515b7ad7512SChao Yu 2516b7ad7512SChao Yu mutex_unlock(&curseg->curseg_mutex); 2517b7ad7512SChao Yu 2518b7ad7512SChao Yu set_page_dirty(page); 2519b7ad7512SChao Yu f2fs_put_page(page, 1); 2520b7ad7512SChao Yu } 2521b7ad7512SChao Yu 2522093749e2SChao Yu static int is_next_segment_free(struct f2fs_sb_info *sbi, 2523093749e2SChao Yu struct curseg_info *curseg, int type) 2524a7881893SJaegeuk Kim { 2525a7881893SJaegeuk Kim unsigned int segno = curseg->segno + 1; 2526a7881893SJaegeuk Kim struct free_segmap_info *free_i = FREE_I(sbi); 2527a7881893SJaegeuk Kim 2528a7881893SJaegeuk Kim if (segno < MAIN_SEGS(sbi) && segno % sbi->segs_per_sec) 2529a7881893SJaegeuk Kim return !test_bit(segno, free_i->free_segmap); 2530a7881893SJaegeuk Kim return 0; 2531a7881893SJaegeuk Kim } 2532a7881893SJaegeuk Kim 25330a8165d7SJaegeuk Kim /* 2534351df4b2SJaegeuk Kim * Find a new segment from the free segments bitmap to right order 2535351df4b2SJaegeuk Kim * This function should be returned with success, otherwise BUG 2536351df4b2SJaegeuk Kim */ 2537351df4b2SJaegeuk Kim static void get_new_segment(struct f2fs_sb_info *sbi, 2538351df4b2SJaegeuk Kim unsigned int *newseg, bool new_sec, int dir) 2539351df4b2SJaegeuk Kim { 2540351df4b2SJaegeuk Kim struct free_segmap_info *free_i = FREE_I(sbi); 2541351df4b2SJaegeuk Kim unsigned int segno, secno, zoneno; 25427cd8558bSJaegeuk Kim unsigned int total_zones = MAIN_SECS(sbi) / sbi->secs_per_zone; 25434ddb1a4dSJaegeuk Kim unsigned int hint = GET_SEC_FROM_SEG(sbi, *newseg); 25444ddb1a4dSJaegeuk Kim unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg); 2545351df4b2SJaegeuk Kim unsigned int left_start = hint; 2546351df4b2SJaegeuk Kim bool init = true; 2547351df4b2SJaegeuk Kim int go_left = 0; 2548351df4b2SJaegeuk Kim int i; 2549351df4b2SJaegeuk Kim 25501a118ccfSChao Yu spin_lock(&free_i->segmap_lock); 2551351df4b2SJaegeuk Kim 2552351df4b2SJaegeuk Kim if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { 2553351df4b2SJaegeuk Kim segno = find_next_zero_bit(free_i->free_segmap, 25544ddb1a4dSJaegeuk Kim GET_SEG_FROM_SEC(sbi, hint + 1), *newseg + 1); 25554ddb1a4dSJaegeuk Kim if (segno < GET_SEG_FROM_SEC(sbi, hint + 1)) 2556351df4b2SJaegeuk Kim goto got_it; 2557351df4b2SJaegeuk Kim } 2558351df4b2SJaegeuk Kim find_other_zone: 25597cd8558bSJaegeuk Kim secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint); 25607cd8558bSJaegeuk Kim if (secno >= MAIN_SECS(sbi)) { 2561351df4b2SJaegeuk Kim if (dir == ALLOC_RIGHT) { 2562b5c7e7ecSYury Norov secno = find_first_zero_bit(free_i->free_secmap, 2563b5c7e7ecSYury Norov MAIN_SECS(sbi)); 25647cd8558bSJaegeuk Kim f2fs_bug_on(sbi, secno >= MAIN_SECS(sbi)); 2565351df4b2SJaegeuk Kim } else { 2566351df4b2SJaegeuk Kim go_left = 1; 2567351df4b2SJaegeuk Kim left_start = hint - 1; 2568351df4b2SJaegeuk Kim } 2569351df4b2SJaegeuk Kim } 2570351df4b2SJaegeuk Kim if (go_left == 0) 2571351df4b2SJaegeuk Kim goto skip_left; 2572351df4b2SJaegeuk Kim 2573351df4b2SJaegeuk Kim while (test_bit(left_start, free_i->free_secmap)) { 2574351df4b2SJaegeuk Kim if (left_start > 0) { 2575351df4b2SJaegeuk Kim left_start--; 2576351df4b2SJaegeuk Kim continue; 2577351df4b2SJaegeuk Kim } 2578b5c7e7ecSYury Norov left_start = find_first_zero_bit(free_i->free_secmap, 2579b5c7e7ecSYury Norov MAIN_SECS(sbi)); 25807cd8558bSJaegeuk Kim f2fs_bug_on(sbi, left_start >= MAIN_SECS(sbi)); 2581351df4b2SJaegeuk Kim break; 2582351df4b2SJaegeuk Kim } 2583351df4b2SJaegeuk Kim secno = left_start; 2584351df4b2SJaegeuk Kim skip_left: 25854ddb1a4dSJaegeuk Kim segno = GET_SEG_FROM_SEC(sbi, secno); 25864ddb1a4dSJaegeuk Kim zoneno = GET_ZONE_FROM_SEC(sbi, secno); 2587351df4b2SJaegeuk Kim 2588351df4b2SJaegeuk Kim /* give up on finding another zone */ 2589351df4b2SJaegeuk Kim if (!init) 2590351df4b2SJaegeuk Kim goto got_it; 2591351df4b2SJaegeuk Kim if (sbi->secs_per_zone == 1) 2592351df4b2SJaegeuk Kim goto got_it; 2593351df4b2SJaegeuk Kim if (zoneno == old_zoneno) 2594351df4b2SJaegeuk Kim goto got_it; 2595351df4b2SJaegeuk Kim if (dir == ALLOC_LEFT) { 2596351df4b2SJaegeuk Kim if (!go_left && zoneno + 1 >= total_zones) 2597351df4b2SJaegeuk Kim goto got_it; 2598351df4b2SJaegeuk Kim if (go_left && zoneno == 0) 2599351df4b2SJaegeuk Kim goto got_it; 2600351df4b2SJaegeuk Kim } 2601351df4b2SJaegeuk Kim for (i = 0; i < NR_CURSEG_TYPE; i++) 2602351df4b2SJaegeuk Kim if (CURSEG_I(sbi, i)->zone == zoneno) 2603351df4b2SJaegeuk Kim break; 2604351df4b2SJaegeuk Kim 2605351df4b2SJaegeuk Kim if (i < NR_CURSEG_TYPE) { 2606351df4b2SJaegeuk Kim /* zone is in user, try another */ 2607351df4b2SJaegeuk Kim if (go_left) 2608351df4b2SJaegeuk Kim hint = zoneno * sbi->secs_per_zone - 1; 2609351df4b2SJaegeuk Kim else if (zoneno + 1 >= total_zones) 2610351df4b2SJaegeuk Kim hint = 0; 2611351df4b2SJaegeuk Kim else 2612351df4b2SJaegeuk Kim hint = (zoneno + 1) * sbi->secs_per_zone; 2613351df4b2SJaegeuk Kim init = false; 2614351df4b2SJaegeuk Kim goto find_other_zone; 2615351df4b2SJaegeuk Kim } 2616351df4b2SJaegeuk Kim got_it: 2617351df4b2SJaegeuk Kim /* set it as dirty segment in free segmap */ 26189850cf4aSJaegeuk Kim f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap)); 2619351df4b2SJaegeuk Kim __set_inuse(sbi, segno); 2620351df4b2SJaegeuk Kim *newseg = segno; 26211a118ccfSChao Yu spin_unlock(&free_i->segmap_lock); 2622351df4b2SJaegeuk Kim } 2623351df4b2SJaegeuk Kim 2624351df4b2SJaegeuk Kim static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) 2625351df4b2SJaegeuk Kim { 2626351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2627351df4b2SJaegeuk Kim struct summary_footer *sum_footer; 2628093749e2SChao Yu unsigned short seg_type = curseg->seg_type; 2629351df4b2SJaegeuk Kim 2630d0b9e42aSChao Yu curseg->inited = true; 2631351df4b2SJaegeuk Kim curseg->segno = curseg->next_segno; 26324ddb1a4dSJaegeuk Kim curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno); 2633351df4b2SJaegeuk Kim curseg->next_blkoff = 0; 2634351df4b2SJaegeuk Kim curseg->next_segno = NULL_SEGNO; 2635351df4b2SJaegeuk Kim 2636351df4b2SJaegeuk Kim sum_footer = &(curseg->sum_blk->footer); 2637351df4b2SJaegeuk Kim memset(sum_footer, 0, sizeof(struct summary_footer)); 2638093749e2SChao Yu 2639093749e2SChao Yu sanity_check_seg_type(sbi, seg_type); 2640093749e2SChao Yu 2641093749e2SChao Yu if (IS_DATASEG(seg_type)) 2642351df4b2SJaegeuk Kim SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); 2643093749e2SChao Yu if (IS_NODESEG(seg_type)) 2644351df4b2SJaegeuk Kim SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); 2645093749e2SChao Yu __set_sit_entry_type(sbi, seg_type, curseg->segno, modified); 2646351df4b2SJaegeuk Kim } 2647351df4b2SJaegeuk Kim 26487a20b8a6SJaegeuk Kim static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) 26497a20b8a6SJaegeuk Kim { 2650d0b9e42aSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2651093749e2SChao Yu unsigned short seg_type = curseg->seg_type; 2652093749e2SChao Yu 2653093749e2SChao Yu sanity_check_seg_type(sbi, seg_type); 26546691d940SDaeho Jeong if (f2fs_need_rand_seg(sbi)) 26556691d940SDaeho Jeong return prandom_u32() % (MAIN_SECS(sbi) * sbi->segs_per_sec); 2656d0b9e42aSChao Yu 2657a7881893SJaegeuk Kim /* if segs_per_sec is large than 1, we need to keep original policy. */ 26582c70c5e3SChao Yu if (__is_large_section(sbi)) 2659d0b9e42aSChao Yu return curseg->segno; 2660d0b9e42aSChao Yu 2661d0b9e42aSChao Yu /* inmem log may not locate on any segment after mount */ 2662d0b9e42aSChao Yu if (!curseg->inited) 2663d0b9e42aSChao Yu return 0; 2664a7881893SJaegeuk Kim 26654354994fSDaniel Rosenberg if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 26664354994fSDaniel Rosenberg return 0; 26674354994fSDaniel Rosenberg 2668b94929d9SYunlong Song if (test_opt(sbi, NOHEAP) && 2669093749e2SChao Yu (seg_type == CURSEG_HOT_DATA || IS_NODESEG(seg_type))) 26707a20b8a6SJaegeuk Kim return 0; 26717a20b8a6SJaegeuk Kim 2672e066b83cSJaegeuk Kim if (SIT_I(sbi)->last_victim[ALLOC_NEXT]) 2673e066b83cSJaegeuk Kim return SIT_I(sbi)->last_victim[ALLOC_NEXT]; 267407939627SJaegeuk Kim 267507939627SJaegeuk Kim /* find segments from 0 to reuse freed segments */ 267663189b78SChao Yu if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE) 267707939627SJaegeuk Kim return 0; 267807939627SJaegeuk Kim 2679d0b9e42aSChao Yu return curseg->segno; 26807a20b8a6SJaegeuk Kim } 26817a20b8a6SJaegeuk Kim 26820a8165d7SJaegeuk Kim /* 2683351df4b2SJaegeuk Kim * Allocate a current working segment. 2684351df4b2SJaegeuk Kim * This function always allocates a free segment in LFS manner. 2685351df4b2SJaegeuk Kim */ 2686351df4b2SJaegeuk Kim static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) 2687351df4b2SJaegeuk Kim { 2688351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2689d0b9e42aSChao Yu unsigned short seg_type = curseg->seg_type; 2690351df4b2SJaegeuk Kim unsigned int segno = curseg->segno; 2691351df4b2SJaegeuk Kim int dir = ALLOC_LEFT; 2692351df4b2SJaegeuk Kim 2693d0b9e42aSChao Yu if (curseg->inited) 2694351df4b2SJaegeuk Kim write_sum_page(sbi, curseg->sum_blk, 269581fb5e87SHaicheng Li GET_SUM_BLOCK(sbi, segno)); 2696d0b9e42aSChao Yu if (seg_type == CURSEG_WARM_DATA || seg_type == CURSEG_COLD_DATA) 2697351df4b2SJaegeuk Kim dir = ALLOC_RIGHT; 2698351df4b2SJaegeuk Kim 2699351df4b2SJaegeuk Kim if (test_opt(sbi, NOHEAP)) 2700351df4b2SJaegeuk Kim dir = ALLOC_RIGHT; 2701351df4b2SJaegeuk Kim 27027a20b8a6SJaegeuk Kim segno = __get_next_segno(sbi, type); 2703351df4b2SJaegeuk Kim get_new_segment(sbi, &segno, new_sec, dir); 2704351df4b2SJaegeuk Kim curseg->next_segno = segno; 2705351df4b2SJaegeuk Kim reset_curseg(sbi, type, 1); 2706351df4b2SJaegeuk Kim curseg->alloc_type = LFS; 27076691d940SDaeho Jeong if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK) 27086691d940SDaeho Jeong curseg->fragment_remained_chunk = 27096691d940SDaeho Jeong prandom_u32() % sbi->max_fragment_chunk + 1; 2710351df4b2SJaegeuk Kim } 2711351df4b2SJaegeuk Kim 2712453e2ff8SChao Yu static int __next_free_blkoff(struct f2fs_sb_info *sbi, 2713453e2ff8SChao Yu int segno, block_t start) 2714351df4b2SJaegeuk Kim { 2715453e2ff8SChao Yu struct seg_entry *se = get_seg_entry(sbi, segno); 2716e81c93cfSChangman Lee int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 271760a3b782SJaegeuk Kim unsigned long *target_map = SIT_I(sbi)->tmp_map; 2718e81c93cfSChangman Lee unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 2719e81c93cfSChangman Lee unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 2720453e2ff8SChao Yu int i; 2721e81c93cfSChangman Lee 2722e81c93cfSChangman Lee for (i = 0; i < entries; i++) 2723e81c93cfSChangman Lee target_map[i] = ckpt_map[i] | cur_map[i]; 2724e81c93cfSChangman Lee 2725453e2ff8SChao Yu return __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start); 2726351df4b2SJaegeuk Kim } 2727351df4b2SJaegeuk Kim 27280a8165d7SJaegeuk Kim /* 2729351df4b2SJaegeuk Kim * If a segment is written by LFS manner, next block offset is just obtained 2730351df4b2SJaegeuk Kim * by increasing the current block offset. However, if a segment is written by 2731351df4b2SJaegeuk Kim * SSR manner, next block offset obtained by calling __next_free_blkoff 2732351df4b2SJaegeuk Kim */ 2733351df4b2SJaegeuk Kim static void __refresh_next_blkoff(struct f2fs_sb_info *sbi, 2734351df4b2SJaegeuk Kim struct curseg_info *seg) 2735351df4b2SJaegeuk Kim { 27366691d940SDaeho Jeong if (seg->alloc_type == SSR) { 2737453e2ff8SChao Yu seg->next_blkoff = 2738453e2ff8SChao Yu __next_free_blkoff(sbi, seg->segno, 2739453e2ff8SChao Yu seg->next_blkoff + 1); 27406691d940SDaeho Jeong } else { 2741351df4b2SJaegeuk Kim seg->next_blkoff++; 27426691d940SDaeho Jeong if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK) { 27436691d940SDaeho Jeong /* To allocate block chunks in different sizes, use random number */ 27446691d940SDaeho Jeong if (--seg->fragment_remained_chunk <= 0) { 27456691d940SDaeho Jeong seg->fragment_remained_chunk = 27466691d940SDaeho Jeong prandom_u32() % sbi->max_fragment_chunk + 1; 27476691d940SDaeho Jeong seg->next_blkoff += 27486691d940SDaeho Jeong prandom_u32() % sbi->max_fragment_hole + 1; 27496691d940SDaeho Jeong } 27506691d940SDaeho Jeong } 27516691d940SDaeho Jeong } 2752351df4b2SJaegeuk Kim } 2753351df4b2SJaegeuk Kim 275461461fc9SChao Yu bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno) 275561461fc9SChao Yu { 2756453e2ff8SChao Yu return __next_free_blkoff(sbi, segno, 0) < sbi->blocks_per_seg; 275761461fc9SChao Yu } 275861461fc9SChao Yu 27590a8165d7SJaegeuk Kim /* 2760351df4b2SJaegeuk Kim * This function always allocates a used segment(from dirty seglist) by SSR 2761351df4b2SJaegeuk Kim * manner, so it should recover the existing segment information of valid blocks 2762351df4b2SJaegeuk Kim */ 2763093749e2SChao Yu static void change_curseg(struct f2fs_sb_info *sbi, int type, bool flush) 2764351df4b2SJaegeuk Kim { 2765351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 2766351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2767351df4b2SJaegeuk Kim unsigned int new_segno = curseg->next_segno; 2768351df4b2SJaegeuk Kim struct f2fs_summary_block *sum_node; 2769351df4b2SJaegeuk Kim struct page *sum_page; 2770351df4b2SJaegeuk Kim 2771093749e2SChao Yu if (flush) 2772351df4b2SJaegeuk Kim write_sum_page(sbi, curseg->sum_blk, 2773351df4b2SJaegeuk Kim GET_SUM_BLOCK(sbi, curseg->segno)); 2774093749e2SChao Yu 2775351df4b2SJaegeuk Kim __set_test_and_inuse(sbi, new_segno); 2776351df4b2SJaegeuk Kim 2777351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 2778351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, new_segno, PRE); 2779351df4b2SJaegeuk Kim __remove_dirty_segment(sbi, new_segno, DIRTY); 2780351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 2781351df4b2SJaegeuk Kim 2782351df4b2SJaegeuk Kim reset_curseg(sbi, type, 1); 2783351df4b2SJaegeuk Kim curseg->alloc_type = SSR; 2784453e2ff8SChao Yu curseg->next_blkoff = __next_free_blkoff(sbi, curseg->segno, 0); 2785351df4b2SJaegeuk Kim 27864d57b86dSChao Yu sum_page = f2fs_get_sum_page(sbi, new_segno); 278786f33603SJaegeuk Kim if (IS_ERR(sum_page)) { 278886f33603SJaegeuk Kim /* GC won't be able to use stale summary pages by cp_error */ 278986f33603SJaegeuk Kim memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE); 279086f33603SJaegeuk Kim return; 279186f33603SJaegeuk Kim } 2792351df4b2SJaegeuk Kim sum_node = (struct f2fs_summary_block *)page_address(sum_page); 2793351df4b2SJaegeuk Kim memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); 2794351df4b2SJaegeuk Kim f2fs_put_page(sum_page, 1); 2795351df4b2SJaegeuk Kim } 2796351df4b2SJaegeuk Kim 2797093749e2SChao Yu static int get_ssr_segment(struct f2fs_sb_info *sbi, int type, 2798093749e2SChao Yu int alloc_mode, unsigned long long age); 2799093749e2SChao Yu 2800093749e2SChao Yu static void get_atssr_segment(struct f2fs_sb_info *sbi, int type, 2801093749e2SChao Yu int target_type, int alloc_mode, 2802093749e2SChao Yu unsigned long long age) 2803093749e2SChao Yu { 2804093749e2SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2805093749e2SChao Yu 2806093749e2SChao Yu curseg->seg_type = target_type; 2807093749e2SChao Yu 2808093749e2SChao Yu if (get_ssr_segment(sbi, type, alloc_mode, age)) { 2809093749e2SChao Yu struct seg_entry *se = get_seg_entry(sbi, curseg->next_segno); 2810093749e2SChao Yu 2811093749e2SChao Yu curseg->seg_type = se->type; 2812093749e2SChao Yu change_curseg(sbi, type, true); 2813093749e2SChao Yu } else { 2814093749e2SChao Yu /* allocate cold segment by default */ 2815093749e2SChao Yu curseg->seg_type = CURSEG_COLD_DATA; 2816093749e2SChao Yu new_curseg(sbi, type, true); 2817093749e2SChao Yu } 2818093749e2SChao Yu stat_inc_seg_type(sbi, curseg); 2819093749e2SChao Yu } 2820093749e2SChao Yu 2821093749e2SChao Yu static void __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) 2822093749e2SChao Yu { 2823093749e2SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_ALL_DATA_ATGC); 2824093749e2SChao Yu 2825093749e2SChao Yu if (!sbi->am.atgc_enabled) 2826093749e2SChao Yu return; 2827093749e2SChao Yu 2828e4544b63STim Murray f2fs_down_read(&SM_I(sbi)->curseg_lock); 2829093749e2SChao Yu 2830093749e2SChao Yu mutex_lock(&curseg->curseg_mutex); 2831093749e2SChao Yu down_write(&SIT_I(sbi)->sentry_lock); 2832093749e2SChao Yu 2833093749e2SChao Yu get_atssr_segment(sbi, CURSEG_ALL_DATA_ATGC, CURSEG_COLD_DATA, SSR, 0); 2834093749e2SChao Yu 2835093749e2SChao Yu up_write(&SIT_I(sbi)->sentry_lock); 2836093749e2SChao Yu mutex_unlock(&curseg->curseg_mutex); 2837093749e2SChao Yu 2838e4544b63STim Murray f2fs_up_read(&SM_I(sbi)->curseg_lock); 2839093749e2SChao Yu 2840093749e2SChao Yu } 2841093749e2SChao Yu void f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi) 2842093749e2SChao Yu { 2843093749e2SChao Yu __f2fs_init_atgc_curseg(sbi); 2844093749e2SChao Yu } 2845093749e2SChao Yu 2846093749e2SChao Yu static void __f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi, int type) 2847d0b9e42aSChao Yu { 2848d0b9e42aSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2849d0b9e42aSChao Yu 2850d0b9e42aSChao Yu mutex_lock(&curseg->curseg_mutex); 2851d0b9e42aSChao Yu if (!curseg->inited) 2852d0b9e42aSChao Yu goto out; 2853d0b9e42aSChao Yu 2854d0b9e42aSChao Yu if (get_valid_blocks(sbi, curseg->segno, false)) { 2855d0b9e42aSChao Yu write_sum_page(sbi, curseg->sum_blk, 2856d0b9e42aSChao Yu GET_SUM_BLOCK(sbi, curseg->segno)); 2857d0b9e42aSChao Yu } else { 2858d0b9e42aSChao Yu mutex_lock(&DIRTY_I(sbi)->seglist_lock); 2859d0b9e42aSChao Yu __set_test_and_free(sbi, curseg->segno, true); 2860d0b9e42aSChao Yu mutex_unlock(&DIRTY_I(sbi)->seglist_lock); 2861d0b9e42aSChao Yu } 2862d0b9e42aSChao Yu out: 2863d0b9e42aSChao Yu mutex_unlock(&curseg->curseg_mutex); 2864d0b9e42aSChao Yu } 2865d0b9e42aSChao Yu 2866093749e2SChao Yu void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi) 2867093749e2SChao Yu { 2868093749e2SChao Yu __f2fs_save_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED); 2869093749e2SChao Yu 2870093749e2SChao Yu if (sbi->am.atgc_enabled) 2871093749e2SChao Yu __f2fs_save_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC); 2872093749e2SChao Yu } 2873093749e2SChao Yu 2874093749e2SChao Yu static void __f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi, int type) 2875d0b9e42aSChao Yu { 2876d0b9e42aSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 2877d0b9e42aSChao Yu 2878d0b9e42aSChao Yu mutex_lock(&curseg->curseg_mutex); 2879d0b9e42aSChao Yu if (!curseg->inited) 2880d0b9e42aSChao Yu goto out; 2881d0b9e42aSChao Yu if (get_valid_blocks(sbi, curseg->segno, false)) 2882d0b9e42aSChao Yu goto out; 2883d0b9e42aSChao Yu 2884d0b9e42aSChao Yu mutex_lock(&DIRTY_I(sbi)->seglist_lock); 2885d0b9e42aSChao Yu __set_test_and_inuse(sbi, curseg->segno); 2886d0b9e42aSChao Yu mutex_unlock(&DIRTY_I(sbi)->seglist_lock); 2887d0b9e42aSChao Yu out: 2888d0b9e42aSChao Yu mutex_unlock(&curseg->curseg_mutex); 2889d0b9e42aSChao Yu } 2890d0b9e42aSChao Yu 2891093749e2SChao Yu void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi) 2892093749e2SChao Yu { 2893093749e2SChao Yu __f2fs_restore_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED); 2894093749e2SChao Yu 2895093749e2SChao Yu if (sbi->am.atgc_enabled) 2896093749e2SChao Yu __f2fs_restore_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC); 2897093749e2SChao Yu } 2898093749e2SChao Yu 2899093749e2SChao Yu static int get_ssr_segment(struct f2fs_sb_info *sbi, int type, 2900093749e2SChao Yu int alloc_mode, unsigned long long age) 290143727527SJaegeuk Kim { 290243727527SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 290343727527SJaegeuk Kim const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; 2904e066b83cSJaegeuk Kim unsigned segno = NULL_SEGNO; 2905093749e2SChao Yu unsigned short seg_type = curseg->seg_type; 2906d27c3d89SChao Yu int i, cnt; 2907d27c3d89SChao Yu bool reversed = false; 2908c192f7a4SJaegeuk Kim 2909093749e2SChao Yu sanity_check_seg_type(sbi, seg_type); 2910093749e2SChao Yu 29114d57b86dSChao Yu /* f2fs_need_SSR() already forces to do this */ 2912093749e2SChao Yu if (!v_ops->get_victim(sbi, &segno, BG_GC, seg_type, alloc_mode, age)) { 2913e066b83cSJaegeuk Kim curseg->next_segno = segno; 2914c192f7a4SJaegeuk Kim return 1; 2915e066b83cSJaegeuk Kim } 291643727527SJaegeuk Kim 291770d625cbSJaegeuk Kim /* For node segments, let's do SSR more intensively */ 2918093749e2SChao Yu if (IS_NODESEG(seg_type)) { 2919093749e2SChao Yu if (seg_type >= CURSEG_WARM_NODE) { 2920d27c3d89SChao Yu reversed = true; 2921d27c3d89SChao Yu i = CURSEG_COLD_NODE; 2922d27c3d89SChao Yu } else { 292370d625cbSJaegeuk Kim i = CURSEG_HOT_NODE; 2924d27c3d89SChao Yu } 2925d27c3d89SChao Yu cnt = NR_CURSEG_NODE_TYPE; 2926d27c3d89SChao Yu } else { 2927093749e2SChao Yu if (seg_type >= CURSEG_WARM_DATA) { 2928d27c3d89SChao Yu reversed = true; 2929d27c3d89SChao Yu i = CURSEG_COLD_DATA; 293070d625cbSJaegeuk Kim } else { 293170d625cbSJaegeuk Kim i = CURSEG_HOT_DATA; 2932d27c3d89SChao Yu } 2933d27c3d89SChao Yu cnt = NR_CURSEG_DATA_TYPE; 293470d625cbSJaegeuk Kim } 293543727527SJaegeuk Kim 2936d27c3d89SChao Yu for (; cnt-- > 0; reversed ? i-- : i++) { 2937093749e2SChao Yu if (i == seg_type) 2938c192f7a4SJaegeuk Kim continue; 2939093749e2SChao Yu if (!v_ops->get_victim(sbi, &segno, BG_GC, i, alloc_mode, age)) { 2940e066b83cSJaegeuk Kim curseg->next_segno = segno; 294143727527SJaegeuk Kim return 1; 2942c192f7a4SJaegeuk Kim } 2943e066b83cSJaegeuk Kim } 29444354994fSDaniel Rosenberg 29454354994fSDaniel Rosenberg /* find valid_blocks=0 in dirty list */ 29464354994fSDaniel Rosenberg if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { 29474354994fSDaniel Rosenberg segno = get_free_segment(sbi); 29484354994fSDaniel Rosenberg if (segno != NULL_SEGNO) { 29494354994fSDaniel Rosenberg curseg->next_segno = segno; 29504354994fSDaniel Rosenberg return 1; 29514354994fSDaniel Rosenberg } 29524354994fSDaniel Rosenberg } 295343727527SJaegeuk Kim return 0; 295443727527SJaegeuk Kim } 295543727527SJaegeuk Kim 2956351df4b2SJaegeuk Kim /* 2957351df4b2SJaegeuk Kim * flush out current segment and replace it with new segment 2958351df4b2SJaegeuk Kim * This function should be returned with success, otherwise BUG 2959351df4b2SJaegeuk Kim */ 2960351df4b2SJaegeuk Kim static void allocate_segment_by_default(struct f2fs_sb_info *sbi, 2961351df4b2SJaegeuk Kim int type, bool force) 2962351df4b2SJaegeuk Kim { 2963a7881893SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 2964a7881893SJaegeuk Kim 29657b405275SGu Zheng if (force) 2966351df4b2SJaegeuk Kim new_curseg(sbi, type, true); 29675b6c6be2SJaegeuk Kim else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) && 2968093749e2SChao Yu curseg->seg_type == CURSEG_WARM_NODE) 2969351df4b2SJaegeuk Kim new_curseg(sbi, type, false); 2970093749e2SChao Yu else if (curseg->alloc_type == LFS && 2971093749e2SChao Yu is_next_segment_free(sbi, curseg, type) && 29724354994fSDaniel Rosenberg likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 2973a7881893SJaegeuk Kim new_curseg(sbi, type, false); 2974093749e2SChao Yu else if (f2fs_need_SSR(sbi) && 2975093749e2SChao Yu get_ssr_segment(sbi, type, SSR, 0)) 2976093749e2SChao Yu change_curseg(sbi, type, true); 2977351df4b2SJaegeuk Kim else 2978351df4b2SJaegeuk Kim new_curseg(sbi, type, false); 2979dcdfff65SJaegeuk Kim 2980a7881893SJaegeuk Kim stat_inc_seg_type(sbi, curseg); 2981351df4b2SJaegeuk Kim } 2982351df4b2SJaegeuk Kim 29830ef81833SChao Yu void f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type, 298404f0b2eaSQiuyang Sun unsigned int start, unsigned int end) 298504f0b2eaSQiuyang Sun { 298604f0b2eaSQiuyang Sun struct curseg_info *curseg = CURSEG_I(sbi, type); 298704f0b2eaSQiuyang Sun unsigned int segno; 298804f0b2eaSQiuyang Sun 2989e4544b63STim Murray f2fs_down_read(&SM_I(sbi)->curseg_lock); 299004f0b2eaSQiuyang Sun mutex_lock(&curseg->curseg_mutex); 299104f0b2eaSQiuyang Sun down_write(&SIT_I(sbi)->sentry_lock); 299204f0b2eaSQiuyang Sun 299304f0b2eaSQiuyang Sun segno = CURSEG_I(sbi, type)->segno; 299404f0b2eaSQiuyang Sun if (segno < start || segno > end) 299504f0b2eaSQiuyang Sun goto unlock; 299604f0b2eaSQiuyang Sun 2997093749e2SChao Yu if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type, SSR, 0)) 2998093749e2SChao Yu change_curseg(sbi, type, true); 299904f0b2eaSQiuyang Sun else 300004f0b2eaSQiuyang Sun new_curseg(sbi, type, true); 300104f0b2eaSQiuyang Sun 300204f0b2eaSQiuyang Sun stat_inc_seg_type(sbi, curseg); 300304f0b2eaSQiuyang Sun 300404f0b2eaSQiuyang Sun locate_dirty_segment(sbi, segno); 300504f0b2eaSQiuyang Sun unlock: 300604f0b2eaSQiuyang Sun up_write(&SIT_I(sbi)->sentry_lock); 300704f0b2eaSQiuyang Sun 300804f0b2eaSQiuyang Sun if (segno != curseg->segno) 3009dcbb4c10SJoe Perches f2fs_notice(sbi, "For resize: curseg of type %d: %u ==> %u", 301004f0b2eaSQiuyang Sun type, segno, curseg->segno); 301104f0b2eaSQiuyang Sun 301204f0b2eaSQiuyang Sun mutex_unlock(&curseg->curseg_mutex); 3013e4544b63STim Murray f2fs_up_read(&SM_I(sbi)->curseg_lock); 301404f0b2eaSQiuyang Sun } 301504f0b2eaSQiuyang Sun 3016e1175f02SChao Yu static void __allocate_new_segment(struct f2fs_sb_info *sbi, int type, 3017509f1010SChao Yu bool new_sec, bool force) 3018351df4b2SJaegeuk Kim { 3019901d745fSChao Yu struct curseg_info *curseg = CURSEG_I(sbi, type); 30206ae1be13SJaegeuk Kim unsigned int old_segno; 3021901d745fSChao Yu 3022d0b9e42aSChao Yu if (!curseg->inited) 3023d0b9e42aSChao Yu goto alloc; 3024d0b9e42aSChao Yu 3025509f1010SChao Yu if (force || curseg->next_blkoff || 3026e1175f02SChao Yu get_valid_blocks(sbi, curseg->segno, new_sec)) 3027e1175f02SChao Yu goto alloc; 3028901d745fSChao Yu 302961461fc9SChao Yu if (!get_ckpt_valid_blocks(sbi, curseg->segno, new_sec)) 3030901d745fSChao Yu return; 3031d0b9e42aSChao Yu alloc: 3032901d745fSChao Yu old_segno = curseg->segno; 3033901d745fSChao Yu SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true); 3034901d745fSChao Yu locate_dirty_segment(sbi, old_segno); 3035901d745fSChao Yu } 3036901d745fSChao Yu 3037509f1010SChao Yu static void __allocate_new_section(struct f2fs_sb_info *sbi, 3038509f1010SChao Yu int type, bool force) 3039901d745fSChao Yu { 3040509f1010SChao Yu __allocate_new_segment(sbi, type, true, force); 3041e1175f02SChao Yu } 3042e1175f02SChao Yu 3043509f1010SChao Yu void f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force) 3044901d745fSChao Yu { 3045e4544b63STim Murray f2fs_down_read(&SM_I(sbi)->curseg_lock); 3046901d745fSChao Yu down_write(&SIT_I(sbi)->sentry_lock); 3047509f1010SChao Yu __allocate_new_section(sbi, type, force); 3048901d745fSChao Yu up_write(&SIT_I(sbi)->sentry_lock); 3049e4544b63STim Murray f2fs_up_read(&SM_I(sbi)->curseg_lock); 3050901d745fSChao Yu } 3051901d745fSChao Yu 3052901d745fSChao Yu void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi) 3053901d745fSChao Yu { 3054351df4b2SJaegeuk Kim int i; 3055351df4b2SJaegeuk Kim 3056e4544b63STim Murray f2fs_down_read(&SM_I(sbi)->curseg_lock); 30573d26fa6bSChao Yu down_write(&SIT_I(sbi)->sentry_lock); 3058901d745fSChao Yu for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) 3059509f1010SChao Yu __allocate_new_segment(sbi, i, false, false); 30603d26fa6bSChao Yu up_write(&SIT_I(sbi)->sentry_lock); 3061e4544b63STim Murray f2fs_up_read(&SM_I(sbi)->curseg_lock); 3062351df4b2SJaegeuk Kim } 3063351df4b2SJaegeuk Kim 3064351df4b2SJaegeuk Kim static const struct segment_allocation default_salloc_ops = { 3065351df4b2SJaegeuk Kim .allocate_segment = allocate_segment_by_default, 3066351df4b2SJaegeuk Kim }; 3067351df4b2SJaegeuk Kim 30684d57b86dSChao Yu bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, 30694d57b86dSChao Yu struct cp_control *cpc) 307025290fa5SJaegeuk Kim { 307125290fa5SJaegeuk Kim __u64 trim_start = cpc->trim_start; 307225290fa5SJaegeuk Kim bool has_candidate = false; 307325290fa5SJaegeuk Kim 30743d26fa6bSChao Yu down_write(&SIT_I(sbi)->sentry_lock); 307525290fa5SJaegeuk Kim for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { 307625290fa5SJaegeuk Kim if (add_discard_addrs(sbi, cpc, true)) { 307725290fa5SJaegeuk Kim has_candidate = true; 307825290fa5SJaegeuk Kim break; 307925290fa5SJaegeuk Kim } 308025290fa5SJaegeuk Kim } 30813d26fa6bSChao Yu up_write(&SIT_I(sbi)->sentry_lock); 308225290fa5SJaegeuk Kim 308325290fa5SJaegeuk Kim cpc->trim_start = trim_start; 308425290fa5SJaegeuk Kim return has_candidate; 308525290fa5SJaegeuk Kim } 308625290fa5SJaegeuk Kim 308701f9cf6dSChao Yu static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, 30889a997188SJaegeuk Kim struct discard_policy *dpolicy, 30899a997188SJaegeuk Kim unsigned int start, unsigned int end) 30909a997188SJaegeuk Kim { 30919a997188SJaegeuk Kim struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 30929a997188SJaegeuk Kim struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 30939a997188SJaegeuk Kim struct rb_node **insert_p = NULL, *insert_parent = NULL; 30949a997188SJaegeuk Kim struct discard_cmd *dc; 30959a997188SJaegeuk Kim struct blk_plug plug; 30969a997188SJaegeuk Kim int issued; 309701f9cf6dSChao Yu unsigned int trimmed = 0; 30989a997188SJaegeuk Kim 30999a997188SJaegeuk Kim next: 31009a997188SJaegeuk Kim issued = 0; 31019a997188SJaegeuk Kim 31029a997188SJaegeuk Kim mutex_lock(&dcc->cmd_lock); 310367fce70bSChao Yu if (unlikely(dcc->rbtree_check)) 310467fce70bSChao Yu f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi, 31052e9b2bb2SChao Yu &dcc->root, false)); 31069a997188SJaegeuk Kim 31074d57b86dSChao Yu dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, 31089a997188SJaegeuk Kim NULL, start, 31099a997188SJaegeuk Kim (struct rb_entry **)&prev_dc, 31109a997188SJaegeuk Kim (struct rb_entry **)&next_dc, 31114dada3fdSChao Yu &insert_p, &insert_parent, true, NULL); 31129a997188SJaegeuk Kim if (!dc) 31139a997188SJaegeuk Kim dc = next_dc; 31149a997188SJaegeuk Kim 31159a997188SJaegeuk Kim blk_start_plug(&plug); 31169a997188SJaegeuk Kim 31179a997188SJaegeuk Kim while (dc && dc->lstart <= end) { 31189a997188SJaegeuk Kim struct rb_node *node; 31196b9cb124SChao Yu int err = 0; 31209a997188SJaegeuk Kim 31219a997188SJaegeuk Kim if (dc->len < dpolicy->granularity) 31229a997188SJaegeuk Kim goto skip; 31239a997188SJaegeuk Kim 31249a997188SJaegeuk Kim if (dc->state != D_PREP) { 31259a997188SJaegeuk Kim list_move_tail(&dc->list, &dcc->fstrim_list); 31269a997188SJaegeuk Kim goto skip; 31279a997188SJaegeuk Kim } 31289a997188SJaegeuk Kim 31296b9cb124SChao Yu err = __submit_discard_cmd(sbi, dpolicy, dc, &issued); 31309a997188SJaegeuk Kim 313135ec7d57SChao Yu if (issued >= dpolicy->max_requests) { 31329a997188SJaegeuk Kim start = dc->lstart + dc->len; 31339a997188SJaegeuk Kim 31346b9cb124SChao Yu if (err) 31356b9cb124SChao Yu __remove_discard_cmd(sbi, dc); 31366b9cb124SChao Yu 31379a997188SJaegeuk Kim blk_finish_plug(&plug); 31389a997188SJaegeuk Kim mutex_unlock(&dcc->cmd_lock); 313901f9cf6dSChao Yu trimmed += __wait_all_discard_cmd(sbi, NULL); 31405df7731fSChao Yu congestion_wait(BLK_RW_ASYNC, DEFAULT_IO_TIMEOUT); 31419a997188SJaegeuk Kim goto next; 31429a997188SJaegeuk Kim } 31439a997188SJaegeuk Kim skip: 31449a997188SJaegeuk Kim node = rb_next(&dc->rb_node); 31456b9cb124SChao Yu if (err) 31466b9cb124SChao Yu __remove_discard_cmd(sbi, dc); 31479a997188SJaegeuk Kim dc = rb_entry_safe(node, struct discard_cmd, rb_node); 31489a997188SJaegeuk Kim 31499a997188SJaegeuk Kim if (fatal_signal_pending(current)) 31509a997188SJaegeuk Kim break; 31519a997188SJaegeuk Kim } 31529a997188SJaegeuk Kim 31539a997188SJaegeuk Kim blk_finish_plug(&plug); 31549a997188SJaegeuk Kim mutex_unlock(&dcc->cmd_lock); 315501f9cf6dSChao Yu 315601f9cf6dSChao Yu return trimmed; 31579a997188SJaegeuk Kim } 31589a997188SJaegeuk Kim 31594b2fecc8SJaegeuk Kim int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) 31604b2fecc8SJaegeuk Kim { 3161f7ef9b83SJaegeuk Kim __u64 start = F2FS_BYTES_TO_BLK(range->start); 3162f7ef9b83SJaegeuk Kim __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; 3163377224c4SChao Yu unsigned int start_segno, end_segno; 31648412663dSChao Yu block_t start_block, end_block; 31654b2fecc8SJaegeuk Kim struct cp_control cpc; 316678997b56SChao Yu struct discard_policy dpolicy; 31670ea80512SChao Yu unsigned long long trimmed = 0; 3168c34f42e2SChao Yu int err = 0; 3169b0332a0fSChao Yu bool need_align = f2fs_lfs_mode(sbi) && __is_large_section(sbi); 31704b2fecc8SJaegeuk Kim 3171836b5a63SJaegeuk Kim if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) 31724b2fecc8SJaegeuk Kim return -EINVAL; 31734b2fecc8SJaegeuk Kim 31743f16ecd9SChao Yu if (end < MAIN_BLKADDR(sbi)) 31753f16ecd9SChao Yu goto out; 31764b2fecc8SJaegeuk Kim 3177ed214a11SYunlei He if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { 3178dcbb4c10SJoe Perches f2fs_warn(sbi, "Found FS corruption, run fsck to fix."); 317910f966bbSChao Yu return -EFSCORRUPTED; 3180ed214a11SYunlei He } 3181ed214a11SYunlei He 31824b2fecc8SJaegeuk Kim /* start/end segment number in main_area */ 31837cd8558bSJaegeuk Kim start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); 31847cd8558bSJaegeuk Kim end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : 31857cd8558bSJaegeuk Kim GET_SEGNO(sbi, end); 3186ad6672bbSYunlong Song if (need_align) { 3187ad6672bbSYunlong Song start_segno = rounddown(start_segno, sbi->segs_per_sec); 3188ad6672bbSYunlong Song end_segno = roundup(end_segno + 1, sbi->segs_per_sec) - 1; 3189ad6672bbSYunlong Song } 31908412663dSChao Yu 31914b2fecc8SJaegeuk Kim cpc.reason = CP_DISCARD; 3192836b5a63SJaegeuk Kim cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); 3193377224c4SChao Yu cpc.trim_start = start_segno; 3194377224c4SChao Yu cpc.trim_end = end_segno; 3195a66cdd98SJaegeuk Kim 3196a66cdd98SJaegeuk Kim if (sbi->discard_blks == 0) 3197377224c4SChao Yu goto out; 3198bba681cbSJaegeuk Kim 3199e4544b63STim Murray f2fs_down_write(&sbi->gc_lock); 32004d57b86dSChao Yu err = f2fs_write_checkpoint(sbi, &cpc); 3201e4544b63STim Murray f2fs_up_write(&sbi->gc_lock); 3202e9328353SChao Yu if (err) 3203377224c4SChao Yu goto out; 32048412663dSChao Yu 3205e555da9fSJaegeuk Kim /* 3206e555da9fSJaegeuk Kim * We filed discard candidates, but actually we don't need to wait for 3207e555da9fSJaegeuk Kim * all of them, since they'll be issued in idle time along with runtime 3208e555da9fSJaegeuk Kim * discard option. User configuration looks like using runtime discard 3209e555da9fSJaegeuk Kim * or periodic fstrim instead of it. 3210e555da9fSJaegeuk Kim */ 32117d20c8abSChao Yu if (f2fs_realtime_discard_enable(sbi)) 32125a615492SJaegeuk Kim goto out; 32135a615492SJaegeuk Kim 32145a615492SJaegeuk Kim start_block = START_BLOCK(sbi, start_segno); 32155a615492SJaegeuk Kim end_block = START_BLOCK(sbi, end_segno + 1); 32165a615492SJaegeuk Kim 32175a615492SJaegeuk Kim __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); 321801f9cf6dSChao Yu trimmed = __issue_discard_cmd_range(sbi, &dpolicy, 321901f9cf6dSChao Yu start_block, end_block); 32205a615492SJaegeuk Kim 322101f9cf6dSChao Yu trimmed += __wait_discard_cmd_range(sbi, &dpolicy, 32220ea80512SChao Yu start_block, end_block); 3223377224c4SChao Yu out: 32246eae2694SChao Yu if (!err) 32256eae2694SChao Yu range->len = F2FS_BLK_TO_BYTES(trimmed); 3226c34f42e2SChao Yu return err; 32274b2fecc8SJaegeuk Kim } 32284b2fecc8SJaegeuk Kim 3229093749e2SChao Yu static bool __has_curseg_space(struct f2fs_sb_info *sbi, 3230093749e2SChao Yu struct curseg_info *curseg) 3231351df4b2SJaegeuk Kim { 3232de881df9SAravind Ramesh return curseg->next_blkoff < f2fs_usable_blks_in_seg(sbi, 3233de881df9SAravind Ramesh curseg->segno); 3234351df4b2SJaegeuk Kim } 3235351df4b2SJaegeuk Kim 32364d57b86dSChao Yu int f2fs_rw_hint_to_seg_type(enum rw_hint hint) 32374f0a03d3SHyunchul Lee { 32384f0a03d3SHyunchul Lee switch (hint) { 32394f0a03d3SHyunchul Lee case WRITE_LIFE_SHORT: 32404f0a03d3SHyunchul Lee return CURSEG_HOT_DATA; 32414f0a03d3SHyunchul Lee case WRITE_LIFE_EXTREME: 32424f0a03d3SHyunchul Lee return CURSEG_COLD_DATA; 32434f0a03d3SHyunchul Lee default: 32444f0a03d3SHyunchul Lee return CURSEG_WARM_DATA; 32454f0a03d3SHyunchul Lee } 32464f0a03d3SHyunchul Lee } 32474f0a03d3SHyunchul Lee 32480cdd3195SHyunchul Lee /* This returns write hints for each segment type. This hints will be 32490cdd3195SHyunchul Lee * passed down to block layer. There are mapping tables which depend on 32500cdd3195SHyunchul Lee * the mount option 'whint_mode'. 32510cdd3195SHyunchul Lee * 32520cdd3195SHyunchul Lee * 1) whint_mode=off. F2FS only passes down WRITE_LIFE_NOT_SET. 32530cdd3195SHyunchul Lee * 32540cdd3195SHyunchul Lee * 2) whint_mode=user-based. F2FS tries to pass down hints given by users. 32550cdd3195SHyunchul Lee * 32560cdd3195SHyunchul Lee * User F2FS Block 32570cdd3195SHyunchul Lee * ---- ---- ----- 32580cdd3195SHyunchul Lee * META WRITE_LIFE_NOT_SET 32590cdd3195SHyunchul Lee * HOT_NODE " 32600cdd3195SHyunchul Lee * WARM_NODE " 32610cdd3195SHyunchul Lee * COLD_NODE " 32620cdd3195SHyunchul Lee * ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME 32630cdd3195SHyunchul Lee * extension list " " 32640cdd3195SHyunchul Lee * 32650cdd3195SHyunchul Lee * -- buffered io 32660cdd3195SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 32670cdd3195SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 32680cdd3195SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET 32690cdd3195SHyunchul Lee * WRITE_LIFE_NONE " " 32700cdd3195SHyunchul Lee * WRITE_LIFE_MEDIUM " " 32710cdd3195SHyunchul Lee * WRITE_LIFE_LONG " " 32720cdd3195SHyunchul Lee * 32730cdd3195SHyunchul Lee * -- direct io 32740cdd3195SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 32750cdd3195SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 32760cdd3195SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET 32770cdd3195SHyunchul Lee * WRITE_LIFE_NONE " WRITE_LIFE_NONE 32780cdd3195SHyunchul Lee * WRITE_LIFE_MEDIUM " WRITE_LIFE_MEDIUM 32790cdd3195SHyunchul Lee * WRITE_LIFE_LONG " WRITE_LIFE_LONG 32800cdd3195SHyunchul Lee * 3281f2e703f9SHyunchul Lee * 3) whint_mode=fs-based. F2FS passes down hints with its policy. 3282f2e703f9SHyunchul Lee * 3283f2e703f9SHyunchul Lee * User F2FS Block 3284f2e703f9SHyunchul Lee * ---- ---- ----- 3285f2e703f9SHyunchul Lee * META WRITE_LIFE_MEDIUM; 3286f2e703f9SHyunchul Lee * HOT_NODE WRITE_LIFE_NOT_SET 3287f2e703f9SHyunchul Lee * WARM_NODE " 3288f2e703f9SHyunchul Lee * COLD_NODE WRITE_LIFE_NONE 3289f2e703f9SHyunchul Lee * ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME 3290f2e703f9SHyunchul Lee * extension list " " 3291f2e703f9SHyunchul Lee * 3292f2e703f9SHyunchul Lee * -- buffered io 3293f2e703f9SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 3294f2e703f9SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 3295f2e703f9SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_LONG 3296f2e703f9SHyunchul Lee * WRITE_LIFE_NONE " " 3297f2e703f9SHyunchul Lee * WRITE_LIFE_MEDIUM " " 3298f2e703f9SHyunchul Lee * WRITE_LIFE_LONG " " 3299f2e703f9SHyunchul Lee * 3300f2e703f9SHyunchul Lee * -- direct io 3301f2e703f9SHyunchul Lee * WRITE_LIFE_EXTREME COLD_DATA WRITE_LIFE_EXTREME 3302f2e703f9SHyunchul Lee * WRITE_LIFE_SHORT HOT_DATA WRITE_LIFE_SHORT 3303f2e703f9SHyunchul Lee * WRITE_LIFE_NOT_SET WARM_DATA WRITE_LIFE_NOT_SET 3304f2e703f9SHyunchul Lee * WRITE_LIFE_NONE " WRITE_LIFE_NONE 3305f2e703f9SHyunchul Lee * WRITE_LIFE_MEDIUM " WRITE_LIFE_MEDIUM 3306f2e703f9SHyunchul Lee * WRITE_LIFE_LONG " WRITE_LIFE_LONG 33070cdd3195SHyunchul Lee */ 33080cdd3195SHyunchul Lee 33094d57b86dSChao Yu enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi, 33100cdd3195SHyunchul Lee enum page_type type, enum temp_type temp) 33110cdd3195SHyunchul Lee { 331263189b78SChao Yu if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_USER) { 33130cdd3195SHyunchul Lee if (type == DATA) { 3314f2e703f9SHyunchul Lee if (temp == WARM) 3315f2e703f9SHyunchul Lee return WRITE_LIFE_NOT_SET; 3316f2e703f9SHyunchul Lee else if (temp == HOT) 33170cdd3195SHyunchul Lee return WRITE_LIFE_SHORT; 3318f2e703f9SHyunchul Lee else if (temp == COLD) 3319f2e703f9SHyunchul Lee return WRITE_LIFE_EXTREME; 33200cdd3195SHyunchul Lee } else { 33210cdd3195SHyunchul Lee return WRITE_LIFE_NOT_SET; 33220cdd3195SHyunchul Lee } 332363189b78SChao Yu } else if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_FS) { 3324f2e703f9SHyunchul Lee if (type == DATA) { 3325f2e703f9SHyunchul Lee if (temp == WARM) 3326f2e703f9SHyunchul Lee return WRITE_LIFE_LONG; 3327f2e703f9SHyunchul Lee else if (temp == HOT) 3328f2e703f9SHyunchul Lee return WRITE_LIFE_SHORT; 3329f2e703f9SHyunchul Lee else if (temp == COLD) 3330f2e703f9SHyunchul Lee return WRITE_LIFE_EXTREME; 3331f2e703f9SHyunchul Lee } else if (type == NODE) { 3332f2e703f9SHyunchul Lee if (temp == WARM || temp == HOT) 33330cdd3195SHyunchul Lee return WRITE_LIFE_NOT_SET; 3334f2e703f9SHyunchul Lee else if (temp == COLD) 3335f2e703f9SHyunchul Lee return WRITE_LIFE_NONE; 3336f2e703f9SHyunchul Lee } else if (type == META) { 3337f2e703f9SHyunchul Lee return WRITE_LIFE_MEDIUM; 33380cdd3195SHyunchul Lee } 33390cdd3195SHyunchul Lee } 3340f2e703f9SHyunchul Lee return WRITE_LIFE_NOT_SET; 3341f2e703f9SHyunchul Lee } 33420cdd3195SHyunchul Lee 334381377bd6SJaegeuk Kim static int __get_segment_type_2(struct f2fs_io_info *fio) 3344351df4b2SJaegeuk Kim { 334581377bd6SJaegeuk Kim if (fio->type == DATA) 3346351df4b2SJaegeuk Kim return CURSEG_HOT_DATA; 3347351df4b2SJaegeuk Kim else 3348351df4b2SJaegeuk Kim return CURSEG_HOT_NODE; 3349351df4b2SJaegeuk Kim } 3350351df4b2SJaegeuk Kim 335181377bd6SJaegeuk Kim static int __get_segment_type_4(struct f2fs_io_info *fio) 3352351df4b2SJaegeuk Kim { 335381377bd6SJaegeuk Kim if (fio->type == DATA) { 335481377bd6SJaegeuk Kim struct inode *inode = fio->page->mapping->host; 3355351df4b2SJaegeuk Kim 3356351df4b2SJaegeuk Kim if (S_ISDIR(inode->i_mode)) 3357351df4b2SJaegeuk Kim return CURSEG_HOT_DATA; 3358351df4b2SJaegeuk Kim else 3359351df4b2SJaegeuk Kim return CURSEG_COLD_DATA; 3360351df4b2SJaegeuk Kim } else { 336181377bd6SJaegeuk Kim if (IS_DNODE(fio->page) && is_cold_node(fio->page)) 3362a344b9fdSJaegeuk Kim return CURSEG_WARM_NODE; 3363351df4b2SJaegeuk Kim else 3364351df4b2SJaegeuk Kim return CURSEG_COLD_NODE; 3365351df4b2SJaegeuk Kim } 3366351df4b2SJaegeuk Kim } 3367351df4b2SJaegeuk Kim 336881377bd6SJaegeuk Kim static int __get_segment_type_6(struct f2fs_io_info *fio) 3369351df4b2SJaegeuk Kim { 337081377bd6SJaegeuk Kim if (fio->type == DATA) { 337181377bd6SJaegeuk Kim struct inode *inode = fio->page->mapping->host; 3372351df4b2SJaegeuk Kim 3373859fca6bSChao Yu if (is_inode_flag_set(inode, FI_ALIGNED_WRITE)) 3374859fca6bSChao Yu return CURSEG_COLD_DATA_PINNED; 3375859fca6bSChao Yu 3376b763f3beSChao Yu if (page_private_gcing(fio->page)) { 3377ac2d750bSWeichao Guo if (fio->sbi->am.atgc_enabled && 3378ac2d750bSWeichao Guo (fio->io_type == FS_DATA_IO) && 3379ac2d750bSWeichao Guo (fio->sbi->gc_mode != GC_URGENT_HIGH)) 3380093749e2SChao Yu return CURSEG_ALL_DATA_ATGC; 3381093749e2SChao Yu else 3382093749e2SChao Yu return CURSEG_COLD_DATA; 3383093749e2SChao Yu } 3384602a16d5SDaeho Jeong if (file_is_cold(inode) || f2fs_need_compress_data(inode)) 3385351df4b2SJaegeuk Kim return CURSEG_COLD_DATA; 3386b6a06cbbSChao Yu if (file_is_hot(inode) || 3387b4c3ca8bSChao Yu is_inode_flag_set(inode, FI_HOT_DATA) || 33882079f115SChao Yu f2fs_is_atomic_file(inode) || 33892079f115SChao Yu f2fs_is_volatile_file(inode)) 3390ef095d19SJaegeuk Kim return CURSEG_HOT_DATA; 33914d57b86dSChao Yu return f2fs_rw_hint_to_seg_type(inode->i_write_hint); 3392351df4b2SJaegeuk Kim } else { 339381377bd6SJaegeuk Kim if (IS_DNODE(fio->page)) 339481377bd6SJaegeuk Kim return is_cold_node(fio->page) ? CURSEG_WARM_NODE : 3395351df4b2SJaegeuk Kim CURSEG_HOT_NODE; 3396351df4b2SJaegeuk Kim return CURSEG_COLD_NODE; 3397351df4b2SJaegeuk Kim } 3398351df4b2SJaegeuk Kim } 3399351df4b2SJaegeuk Kim 340081377bd6SJaegeuk Kim static int __get_segment_type(struct f2fs_io_info *fio) 3401351df4b2SJaegeuk Kim { 3402a912b54dSJaegeuk Kim int type = 0; 3403a912b54dSJaegeuk Kim 340463189b78SChao Yu switch (F2FS_OPTION(fio->sbi).active_logs) { 3405351df4b2SJaegeuk Kim case 2: 3406a912b54dSJaegeuk Kim type = __get_segment_type_2(fio); 3407a912b54dSJaegeuk Kim break; 3408351df4b2SJaegeuk Kim case 4: 3409a912b54dSJaegeuk Kim type = __get_segment_type_4(fio); 3410a912b54dSJaegeuk Kim break; 3411a912b54dSJaegeuk Kim case 6: 3412a912b54dSJaegeuk Kim type = __get_segment_type_6(fio); 3413a912b54dSJaegeuk Kim break; 3414a912b54dSJaegeuk Kim default: 3415a912b54dSJaegeuk Kim f2fs_bug_on(fio->sbi, true); 3416351df4b2SJaegeuk Kim } 341781377bd6SJaegeuk Kim 3418a912b54dSJaegeuk Kim if (IS_HOT(type)) 3419a912b54dSJaegeuk Kim fio->temp = HOT; 3420a912b54dSJaegeuk Kim else if (IS_WARM(type)) 3421a912b54dSJaegeuk Kim fio->temp = WARM; 3422a912b54dSJaegeuk Kim else 3423a912b54dSJaegeuk Kim fio->temp = COLD; 3424a912b54dSJaegeuk Kim return type; 3425351df4b2SJaegeuk Kim } 3426351df4b2SJaegeuk Kim 34274d57b86dSChao Yu void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, 3428351df4b2SJaegeuk Kim block_t old_blkaddr, block_t *new_blkaddr, 3429fb830fc5SChao Yu struct f2fs_summary *sum, int type, 3430093749e2SChao Yu struct f2fs_io_info *fio) 3431351df4b2SJaegeuk Kim { 3432351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 34336ae1be13SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, type); 3434c5d02785SChao Yu unsigned long long old_mtime; 3435093749e2SChao Yu bool from_gc = (type == CURSEG_ALL_DATA_ATGC); 3436093749e2SChao Yu struct seg_entry *se = NULL; 3437351df4b2SJaegeuk Kim 3438e4544b63STim Murray f2fs_down_read(&SM_I(sbi)->curseg_lock); 34392b60311dSChao Yu 3440351df4b2SJaegeuk Kim mutex_lock(&curseg->curseg_mutex); 34413d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 3442351df4b2SJaegeuk Kim 3443093749e2SChao Yu if (from_gc) { 3444093749e2SChao Yu f2fs_bug_on(sbi, GET_SEGNO(sbi, old_blkaddr) == NULL_SEGNO); 3445093749e2SChao Yu se = get_seg_entry(sbi, GET_SEGNO(sbi, old_blkaddr)); 3446093749e2SChao Yu sanity_check_seg_type(sbi, se->type); 3447093749e2SChao Yu f2fs_bug_on(sbi, IS_NODESEG(se->type)); 3448093749e2SChao Yu } 3449351df4b2SJaegeuk Kim *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 3450351df4b2SJaegeuk Kim 3451093749e2SChao Yu f2fs_bug_on(sbi, curseg->next_blkoff >= sbi->blocks_per_seg); 3452093749e2SChao Yu 34534e6a8d9bSJaegeuk Kim f2fs_wait_discard_bio(sbi, *new_blkaddr); 34544e6a8d9bSJaegeuk Kim 3455351df4b2SJaegeuk Kim /* 3456351df4b2SJaegeuk Kim * __add_sum_entry should be resided under the curseg_mutex 3457351df4b2SJaegeuk Kim * because, this function updates a summary entry in the 3458351df4b2SJaegeuk Kim * current summary block. 3459351df4b2SJaegeuk Kim */ 3460e79efe3bSHaicheng Li __add_sum_entry(sbi, type, sum); 3461351df4b2SJaegeuk Kim 3462351df4b2SJaegeuk Kim __refresh_next_blkoff(sbi, curseg); 3463dcdfff65SJaegeuk Kim 3464dcdfff65SJaegeuk Kim stat_inc_block_count(sbi, curseg); 3465351df4b2SJaegeuk Kim 3466c5d02785SChao Yu if (from_gc) { 3467c5d02785SChao Yu old_mtime = get_segment_mtime(sbi, old_blkaddr); 3468c5d02785SChao Yu } else { 3469c5d02785SChao Yu update_segment_mtime(sbi, old_blkaddr, 0); 3470c5d02785SChao Yu old_mtime = 0; 3471c5d02785SChao Yu } 3472c5d02785SChao Yu update_segment_mtime(sbi, *new_blkaddr, old_mtime); 3473c5d02785SChao Yu 347465f1b80bSYunlong Song /* 347565f1b80bSYunlong Song * SIT information should be updated before segment allocation, 347665f1b80bSYunlong Song * since SSR needs latest valid block information. 347765f1b80bSYunlong Song */ 347865f1b80bSYunlong Song update_sit_entry(sbi, *new_blkaddr, 1); 347965f1b80bSYunlong Song if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) 348065f1b80bSYunlong Song update_sit_entry(sbi, old_blkaddr, -1); 348165f1b80bSYunlong Song 3482093749e2SChao Yu if (!__has_curseg_space(sbi, curseg)) { 3483093749e2SChao Yu if (from_gc) 3484093749e2SChao Yu get_atssr_segment(sbi, type, se->type, 3485093749e2SChao Yu AT_SSR, se->mtime); 3486093749e2SChao Yu else 34873436c4bdSYunlong Song sit_i->s_ops->allocate_segment(sbi, type, false); 3488093749e2SChao Yu } 3489c6f82fe9SJaegeuk Kim /* 349065f1b80bSYunlong Song * segment dirty status should be updated after segment allocation, 349165f1b80bSYunlong Song * so we just need to update status only one time after previous 349265f1b80bSYunlong Song * segment being closed. 3493c6f82fe9SJaegeuk Kim */ 349465f1b80bSYunlong Song locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); 349565f1b80bSYunlong Song locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr)); 34963436c4bdSYunlong Song 34973d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 3498351df4b2SJaegeuk Kim 3499704956ecSChao Yu if (page && IS_NODESEG(type)) { 3500351df4b2SJaegeuk Kim fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); 3501351df4b2SJaegeuk Kim 3502704956ecSChao Yu f2fs_inode_chksum_set(sbi, page); 3503704956ecSChao Yu } 3504704956ecSChao Yu 3505f608c38cSChao Yu if (fio) { 3506fb830fc5SChao Yu struct f2fs_bio_info *io; 3507fb830fc5SChao Yu 350825ae837eSChao Yu if (F2FS_IO_ALIGNED(sbi)) 350925ae837eSChao Yu fio->retry = false; 351025ae837eSChao Yu 3511fb830fc5SChao Yu INIT_LIST_HEAD(&fio->list); 3512fb830fc5SChao Yu fio->in_list = true; 3513fb830fc5SChao Yu io = sbi->write_io[fio->type] + fio->temp; 3514fb830fc5SChao Yu spin_lock(&io->io_lock); 3515fb830fc5SChao Yu list_add_tail(&fio->list, &io->io_list); 3516fb830fc5SChao Yu spin_unlock(&io->io_lock); 3517fb830fc5SChao Yu } 3518fb830fc5SChao Yu 3519bfad7c2dSJaegeuk Kim mutex_unlock(&curseg->curseg_mutex); 35202b60311dSChao Yu 3521e4544b63STim Murray f2fs_up_read(&SM_I(sbi)->curseg_lock); 3522bfad7c2dSJaegeuk Kim } 3523bfad7c2dSJaegeuk Kim 352471f2c820SChao Yu void f2fs_update_device_state(struct f2fs_sb_info *sbi, nid_t ino, 352571f2c820SChao Yu block_t blkaddr, unsigned int blkcnt) 352639d787beSChao Yu { 35270916878dSDamien Le Moal if (!f2fs_is_multi_device(sbi)) 352839d787beSChao Yu return; 352939d787beSChao Yu 353071f2c820SChao Yu while (1) { 353171f2c820SChao Yu unsigned int devidx = f2fs_target_device_index(sbi, blkaddr); 353271f2c820SChao Yu unsigned int blks = FDEV(devidx).end_blk - blkaddr + 1; 353339d787beSChao Yu 353439d787beSChao Yu /* update device state for fsync */ 353571f2c820SChao Yu f2fs_set_dirty_device(sbi, ino, devidx, FLUSH_INO); 35361228b482SChao Yu 35371228b482SChao Yu /* update device state for checkpoint */ 35381228b482SChao Yu if (!f2fs_test_bit(devidx, (char *)&sbi->dirty_device)) { 35391228b482SChao Yu spin_lock(&sbi->dev_lock); 35401228b482SChao Yu f2fs_set_bit(devidx, (char *)&sbi->dirty_device); 35411228b482SChao Yu spin_unlock(&sbi->dev_lock); 35421228b482SChao Yu } 354371f2c820SChao Yu 354471f2c820SChao Yu if (blkcnt <= blks) 354571f2c820SChao Yu break; 354671f2c820SChao Yu blkcnt -= blks; 354771f2c820SChao Yu blkaddr += blks; 354871f2c820SChao Yu } 354939d787beSChao Yu } 355039d787beSChao Yu 355105ca3632SJaegeuk Kim static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) 3552bfad7c2dSJaegeuk Kim { 355381377bd6SJaegeuk Kim int type = __get_segment_type(fio); 3554b0332a0fSChao Yu bool keep_order = (f2fs_lfs_mode(fio->sbi) && type == CURSEG_COLD_DATA); 3555bfad7c2dSJaegeuk Kim 3556107a805dSChao Yu if (keep_order) 3557e4544b63STim Murray f2fs_down_read(&fio->sbi->io_order_lock); 35580a595ebaSJaegeuk Kim reallocate: 35594d57b86dSChao Yu f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, 3560093749e2SChao Yu &fio->new_blkaddr, sum, type, fio); 35616ce19affSChao Yu if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) { 35626aa58d8aSChao Yu invalidate_mapping_pages(META_MAPPING(fio->sbi), 35636aa58d8aSChao Yu fio->old_blkaddr, fio->old_blkaddr); 35646ce19affSChao Yu f2fs_invalidate_compress_page(fio->sbi, fio->old_blkaddr); 35656ce19affSChao Yu } 3566bfad7c2dSJaegeuk Kim 3567351df4b2SJaegeuk Kim /* writeout dirty page into bdev */ 3568fe16efe6SChao Yu f2fs_submit_page_write(fio); 3569fe16efe6SChao Yu if (fio->retry) { 35700a595ebaSJaegeuk Kim fio->old_blkaddr = fio->new_blkaddr; 35710a595ebaSJaegeuk Kim goto reallocate; 35720a595ebaSJaegeuk Kim } 3573fe16efe6SChao Yu 357471f2c820SChao Yu f2fs_update_device_state(fio->sbi, fio->ino, fio->new_blkaddr, 1); 3575fe16efe6SChao Yu 3576107a805dSChao Yu if (keep_order) 3577e4544b63STim Murray f2fs_up_read(&fio->sbi->io_order_lock); 3578351df4b2SJaegeuk Kim } 3579351df4b2SJaegeuk Kim 35804d57b86dSChao Yu void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page, 3581b0af6d49SChao Yu enum iostat_type io_type) 3582351df4b2SJaegeuk Kim { 3583458e6197SJaegeuk Kim struct f2fs_io_info fio = { 358405ca3632SJaegeuk Kim .sbi = sbi, 3585458e6197SJaegeuk Kim .type = META, 35860cdd3195SHyunchul Lee .temp = HOT, 358704d328deSMike Christie .op = REQ_OP_WRITE, 358870fd7614SChristoph Hellwig .op_flags = REQ_SYNC | REQ_META | REQ_PRIO, 35897a9d7548SChao Yu .old_blkaddr = page->index, 35907a9d7548SChao Yu .new_blkaddr = page->index, 359105ca3632SJaegeuk Kim .page = page, 35924375a336SJaegeuk Kim .encrypted_page = NULL, 3593fb830fc5SChao Yu .in_list = false, 3594458e6197SJaegeuk Kim }; 3595458e6197SJaegeuk Kim 35962b947003SChao Yu if (unlikely(page->index >= MAIN_BLKADDR(sbi))) 359704d328deSMike Christie fio.op_flags &= ~REQ_META; 35982b947003SChao Yu 3599351df4b2SJaegeuk Kim set_page_writeback(page); 360017c50035SJaegeuk Kim ClearPageError(page); 3601b9109b0eSJaegeuk Kim f2fs_submit_page_write(&fio); 3602b0af6d49SChao Yu 3603b63e7be5SChao Yu stat_inc_meta_count(sbi, page->index); 3604b0af6d49SChao Yu f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE); 3605351df4b2SJaegeuk Kim } 3606351df4b2SJaegeuk Kim 36074d57b86dSChao Yu void f2fs_do_write_node_page(unsigned int nid, struct f2fs_io_info *fio) 3608351df4b2SJaegeuk Kim { 3609351df4b2SJaegeuk Kim struct f2fs_summary sum; 361005ca3632SJaegeuk Kim 3611351df4b2SJaegeuk Kim set_summary(&sum, nid, 0, 0); 361205ca3632SJaegeuk Kim do_write_page(&sum, fio); 3613b0af6d49SChao Yu 3614b0af6d49SChao Yu f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); 3615351df4b2SJaegeuk Kim } 3616351df4b2SJaegeuk Kim 36174d57b86dSChao Yu void f2fs_outplace_write_data(struct dnode_of_data *dn, 36184d57b86dSChao Yu struct f2fs_io_info *fio) 3619351df4b2SJaegeuk Kim { 362005ca3632SJaegeuk Kim struct f2fs_sb_info *sbi = fio->sbi; 3621351df4b2SJaegeuk Kim struct f2fs_summary sum; 3622351df4b2SJaegeuk Kim 36239850cf4aSJaegeuk Kim f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR); 36247735730dSChao Yu set_summary(&sum, dn->nid, dn->ofs_in_node, fio->version); 362505ca3632SJaegeuk Kim do_write_page(&sum, fio); 3626f28b3434SChao Yu f2fs_update_data_blkaddr(dn, fio->new_blkaddr); 3627b0af6d49SChao Yu 3628b0af6d49SChao Yu f2fs_update_iostat(sbi, fio->io_type, F2FS_BLKSIZE); 3629351df4b2SJaegeuk Kim } 3630351df4b2SJaegeuk Kim 36314d57b86dSChao Yu int f2fs_inplace_write_data(struct f2fs_io_info *fio) 3632351df4b2SJaegeuk Kim { 3633b0af6d49SChao Yu int err; 3634d21b0f23SYunlei He struct f2fs_sb_info *sbi = fio->sbi; 363505573d6cSChao Yu unsigned int segno; 3636b0af6d49SChao Yu 36377a9d7548SChao Yu fio->new_blkaddr = fio->old_blkaddr; 36380cdd3195SHyunchul Lee /* i/o temperature is needed for passing down write hints */ 36390cdd3195SHyunchul Lee __get_segment_type(fio); 3640d21b0f23SYunlei He 364105573d6cSChao Yu segno = GET_SEGNO(sbi, fio->new_blkaddr); 364205573d6cSChao Yu 364305573d6cSChao Yu if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) { 364405573d6cSChao Yu set_sbi_flag(sbi, SBI_NEED_FSCK); 36452d821c12SChao Yu f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.", 36462d821c12SChao Yu __func__, segno); 364795577278SChao Yu err = -EFSCORRUPTED; 364895577278SChao Yu goto drop_bio; 364995577278SChao Yu } 365095577278SChao Yu 36511ffc8f5fSJaegeuk Kim if (f2fs_cp_error(sbi)) { 365295577278SChao Yu err = -EIO; 365395577278SChao Yu goto drop_bio; 365405573d6cSChao Yu } 3655d21b0f23SYunlei He 3656e3b49ea3SHyeong-Jun Kim invalidate_mapping_pages(META_MAPPING(sbi), 3657e3b49ea3SHyeong-Jun Kim fio->new_blkaddr, fio->new_blkaddr); 3658e3b49ea3SHyeong-Jun Kim 365905ca3632SJaegeuk Kim stat_inc_inplace_blocks(fio->sbi); 3660b0af6d49SChao Yu 36610e7f4197SJaegeuk Kim if (fio->bio && !(SM_I(sbi)->ipu_policy & (1 << F2FS_IPU_NOCACHE))) 36628648de2cSChao Yu err = f2fs_merge_page_bio(fio); 36638648de2cSChao Yu else 3664b0af6d49SChao Yu err = f2fs_submit_page_bio(fio); 3665e46f6bd8SChao Yu if (!err) { 366671f2c820SChao Yu f2fs_update_device_state(fio->sbi, fio->ino, 366771f2c820SChao Yu fio->new_blkaddr, 1); 3668b0af6d49SChao Yu f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE); 3669e46f6bd8SChao Yu } 3670b0af6d49SChao Yu 3671b0af6d49SChao Yu return err; 367295577278SChao Yu drop_bio: 3673349c4d6cSJaegeuk Kim if (fio->bio && *(fio->bio)) { 367495577278SChao Yu struct bio *bio = *(fio->bio); 367595577278SChao Yu 367695577278SChao Yu bio->bi_status = BLK_STS_IOERR; 367795577278SChao Yu bio_endio(bio); 3678349c4d6cSJaegeuk Kim *(fio->bio) = NULL; 367995577278SChao Yu } 368095577278SChao Yu return err; 3681351df4b2SJaegeuk Kim } 3682351df4b2SJaegeuk Kim 36832b60311dSChao Yu static inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi, 36842b60311dSChao Yu unsigned int segno) 36852b60311dSChao Yu { 36862b60311dSChao Yu int i; 36872b60311dSChao Yu 36882b60311dSChao Yu for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) { 36892b60311dSChao Yu if (CURSEG_I(sbi, i)->segno == segno) 36902b60311dSChao Yu break; 36912b60311dSChao Yu } 36922b60311dSChao Yu return i; 36932b60311dSChao Yu } 36942b60311dSChao Yu 36954d57b86dSChao Yu void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, 369619f106bcSChao Yu block_t old_blkaddr, block_t new_blkaddr, 3697c5d02785SChao Yu bool recover_curseg, bool recover_newaddr, 3698c5d02785SChao Yu bool from_gc) 3699351df4b2SJaegeuk Kim { 3700351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 3701351df4b2SJaegeuk Kim struct curseg_info *curseg; 3702351df4b2SJaegeuk Kim unsigned int segno, old_cursegno; 3703351df4b2SJaegeuk Kim struct seg_entry *se; 3704351df4b2SJaegeuk Kim int type; 370519f106bcSChao Yu unsigned short old_blkoff; 3706753a8ed0SWang Xiaojun unsigned char old_alloc_type; 3707351df4b2SJaegeuk Kim 3708351df4b2SJaegeuk Kim segno = GET_SEGNO(sbi, new_blkaddr); 3709351df4b2SJaegeuk Kim se = get_seg_entry(sbi, segno); 3710351df4b2SJaegeuk Kim type = se->type; 3711351df4b2SJaegeuk Kim 3712e4544b63STim Murray f2fs_down_write(&SM_I(sbi)->curseg_lock); 37132b60311dSChao Yu 371419f106bcSChao Yu if (!recover_curseg) { 371519f106bcSChao Yu /* for recovery flow */ 3716351df4b2SJaegeuk Kim if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { 3717351df4b2SJaegeuk Kim if (old_blkaddr == NULL_ADDR) 3718351df4b2SJaegeuk Kim type = CURSEG_COLD_DATA; 3719351df4b2SJaegeuk Kim else 3720351df4b2SJaegeuk Kim type = CURSEG_WARM_DATA; 3721351df4b2SJaegeuk Kim } 372219f106bcSChao Yu } else { 37232b60311dSChao Yu if (IS_CURSEG(sbi, segno)) { 37242b60311dSChao Yu /* se->type is volatile as SSR allocation */ 37252b60311dSChao Yu type = __f2fs_get_curseg(sbi, segno); 37262b60311dSChao Yu f2fs_bug_on(sbi, type == NO_CHECK_TYPE); 37272b60311dSChao Yu } else { 372819f106bcSChao Yu type = CURSEG_WARM_DATA; 372919f106bcSChao Yu } 37302b60311dSChao Yu } 373119f106bcSChao Yu 37322c190504SYunlong Song f2fs_bug_on(sbi, !IS_DATASEG(type)); 3733351df4b2SJaegeuk Kim curseg = CURSEG_I(sbi, type); 3734351df4b2SJaegeuk Kim 3735351df4b2SJaegeuk Kim mutex_lock(&curseg->curseg_mutex); 37363d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 3737351df4b2SJaegeuk Kim 3738351df4b2SJaegeuk Kim old_cursegno = curseg->segno; 373919f106bcSChao Yu old_blkoff = curseg->next_blkoff; 3740753a8ed0SWang Xiaojun old_alloc_type = curseg->alloc_type; 3741351df4b2SJaegeuk Kim 3742351df4b2SJaegeuk Kim /* change the current segment */ 3743351df4b2SJaegeuk Kim if (segno != curseg->segno) { 3744351df4b2SJaegeuk Kim curseg->next_segno = segno; 3745093749e2SChao Yu change_curseg(sbi, type, true); 3746351df4b2SJaegeuk Kim } 3747351df4b2SJaegeuk Kim 3748491c0854SJaegeuk Kim curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); 3749e79efe3bSHaicheng Li __add_sum_entry(sbi, type, sum); 3750351df4b2SJaegeuk Kim 3751c5d02785SChao Yu if (!recover_curseg || recover_newaddr) { 3752c5d02785SChao Yu if (!from_gc) 3753c5d02785SChao Yu update_segment_mtime(sbi, new_blkaddr, 0); 37546e2c64adSJaegeuk Kim update_sit_entry(sbi, new_blkaddr, 1); 3755c5d02785SChao Yu } 37566aa58d8aSChao Yu if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) { 37576aa58d8aSChao Yu invalidate_mapping_pages(META_MAPPING(sbi), 37586aa58d8aSChao Yu old_blkaddr, old_blkaddr); 37596ce19affSChao Yu f2fs_invalidate_compress_page(sbi, old_blkaddr); 3760c5d02785SChao Yu if (!from_gc) 3761c5d02785SChao Yu update_segment_mtime(sbi, old_blkaddr, 0); 37626e2c64adSJaegeuk Kim update_sit_entry(sbi, old_blkaddr, -1); 37636aa58d8aSChao Yu } 37646e2c64adSJaegeuk Kim 37656e2c64adSJaegeuk Kim locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); 37666e2c64adSJaegeuk Kim locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr)); 37676e2c64adSJaegeuk Kim 3768351df4b2SJaegeuk Kim locate_dirty_segment(sbi, old_cursegno); 3769351df4b2SJaegeuk Kim 377019f106bcSChao Yu if (recover_curseg) { 377119f106bcSChao Yu if (old_cursegno != curseg->segno) { 377219f106bcSChao Yu curseg->next_segno = old_cursegno; 3773093749e2SChao Yu change_curseg(sbi, type, true); 377419f106bcSChao Yu } 377519f106bcSChao Yu curseg->next_blkoff = old_blkoff; 3776753a8ed0SWang Xiaojun curseg->alloc_type = old_alloc_type; 377719f106bcSChao Yu } 377819f106bcSChao Yu 37793d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 3780351df4b2SJaegeuk Kim mutex_unlock(&curseg->curseg_mutex); 3781e4544b63STim Murray f2fs_up_write(&SM_I(sbi)->curseg_lock); 3782351df4b2SJaegeuk Kim } 3783351df4b2SJaegeuk Kim 3784528e3459SChao Yu void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, 3785528e3459SChao Yu block_t old_addr, block_t new_addr, 378628bc106bSChao Yu unsigned char version, bool recover_curseg, 378728bc106bSChao Yu bool recover_newaddr) 3788528e3459SChao Yu { 3789528e3459SChao Yu struct f2fs_summary sum; 3790528e3459SChao Yu 3791528e3459SChao Yu set_summary(&sum, dn->nid, dn->ofs_in_node, version); 3792528e3459SChao Yu 37934d57b86dSChao Yu f2fs_do_replace_block(sbi, &sum, old_addr, new_addr, 3794c5d02785SChao Yu recover_curseg, recover_newaddr, false); 3795528e3459SChao Yu 3796f28b3434SChao Yu f2fs_update_data_blkaddr(dn, new_addr); 3797528e3459SChao Yu } 3798528e3459SChao Yu 379993dfe2acSJaegeuk Kim void f2fs_wait_on_page_writeback(struct page *page, 3800bae0ee7aSChao Yu enum page_type type, bool ordered, bool locked) 380193dfe2acSJaegeuk Kim { 380293dfe2acSJaegeuk Kim if (PageWriteback(page)) { 38034081363fSJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_P_SB(page); 38044081363fSJaegeuk Kim 38050b20fcecSChao Yu /* submit cached LFS IO */ 3806bab475c5SChao Yu f2fs_submit_merged_write_cond(sbi, NULL, page, 0, type); 38070b20fcecSChao Yu /* sbumit cached IPU IO */ 38080b20fcecSChao Yu f2fs_submit_merged_ipu_write(sbi, NULL, page); 3809bae0ee7aSChao Yu if (ordered) { 381093dfe2acSJaegeuk Kim wait_on_page_writeback(page); 3811bae0ee7aSChao Yu f2fs_bug_on(sbi, locked && PageWriteback(page)); 3812bae0ee7aSChao Yu } else { 3813fec1d657SJaegeuk Kim wait_for_stable_page(page); 381493dfe2acSJaegeuk Kim } 381593dfe2acSJaegeuk Kim } 3816bae0ee7aSChao Yu } 381793dfe2acSJaegeuk Kim 38180ded69f6SJaegeuk Kim void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr) 381908b39fbdSChao Yu { 38200ded69f6SJaegeuk Kim struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 382108b39fbdSChao Yu struct page *cpage; 382208b39fbdSChao Yu 38230ded69f6SJaegeuk Kim if (!f2fs_post_read_required(inode)) 38240ded69f6SJaegeuk Kim return; 38250ded69f6SJaegeuk Kim 382693770ab7SChao Yu if (!__is_valid_data_blkaddr(blkaddr)) 382708b39fbdSChao Yu return; 382808b39fbdSChao Yu 382908b39fbdSChao Yu cpage = find_lock_page(META_MAPPING(sbi), blkaddr); 383008b39fbdSChao Yu if (cpage) { 3831bae0ee7aSChao Yu f2fs_wait_on_page_writeback(cpage, DATA, true, true); 383208b39fbdSChao Yu f2fs_put_page(cpage, 1); 383308b39fbdSChao Yu } 383408b39fbdSChao Yu } 383508b39fbdSChao Yu 38361e78e8bdSSahitya Tummala void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, 38371e78e8bdSSahitya Tummala block_t len) 38381e78e8bdSSahitya Tummala { 38391e78e8bdSSahitya Tummala block_t i; 38401e78e8bdSSahitya Tummala 38411e78e8bdSSahitya Tummala for (i = 0; i < len; i++) 38421e78e8bdSSahitya Tummala f2fs_wait_on_block_writeback(inode, blkaddr + i); 38431e78e8bdSSahitya Tummala } 38441e78e8bdSSahitya Tummala 38457735730dSChao Yu static int read_compacted_summaries(struct f2fs_sb_info *sbi) 3846351df4b2SJaegeuk Kim { 3847351df4b2SJaegeuk Kim struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 3848351df4b2SJaegeuk Kim struct curseg_info *seg_i; 3849351df4b2SJaegeuk Kim unsigned char *kaddr; 3850351df4b2SJaegeuk Kim struct page *page; 3851351df4b2SJaegeuk Kim block_t start; 3852351df4b2SJaegeuk Kim int i, j, offset; 3853351df4b2SJaegeuk Kim 3854351df4b2SJaegeuk Kim start = start_sum_block(sbi); 3855351df4b2SJaegeuk Kim 38564d57b86dSChao Yu page = f2fs_get_meta_page(sbi, start++); 38577735730dSChao Yu if (IS_ERR(page)) 38587735730dSChao Yu return PTR_ERR(page); 3859351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 3860351df4b2SJaegeuk Kim 3861351df4b2SJaegeuk Kim /* Step 1: restore nat cache */ 3862351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); 3863b7ad7512SChao Yu memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE); 3864351df4b2SJaegeuk Kim 3865351df4b2SJaegeuk Kim /* Step 2: restore sit cache */ 3866351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); 3867b7ad7512SChao Yu memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); 3868351df4b2SJaegeuk Kim offset = 2 * SUM_JOURNAL_SIZE; 3869351df4b2SJaegeuk Kim 3870351df4b2SJaegeuk Kim /* Step 3: restore summary entries */ 3871351df4b2SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 3872351df4b2SJaegeuk Kim unsigned short blk_off; 3873351df4b2SJaegeuk Kim unsigned int segno; 3874351df4b2SJaegeuk Kim 3875351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, i); 3876351df4b2SJaegeuk Kim segno = le32_to_cpu(ckpt->cur_data_segno[i]); 3877351df4b2SJaegeuk Kim blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); 3878351df4b2SJaegeuk Kim seg_i->next_segno = segno; 3879351df4b2SJaegeuk Kim reset_curseg(sbi, i, 0); 3880351df4b2SJaegeuk Kim seg_i->alloc_type = ckpt->alloc_type[i]; 3881351df4b2SJaegeuk Kim seg_i->next_blkoff = blk_off; 3882351df4b2SJaegeuk Kim 3883351df4b2SJaegeuk Kim if (seg_i->alloc_type == SSR) 3884351df4b2SJaegeuk Kim blk_off = sbi->blocks_per_seg; 3885351df4b2SJaegeuk Kim 3886351df4b2SJaegeuk Kim for (j = 0; j < blk_off; j++) { 3887351df4b2SJaegeuk Kim struct f2fs_summary *s; 38885f029c04SYi Zhuang 3889351df4b2SJaegeuk Kim s = (struct f2fs_summary *)(kaddr + offset); 3890351df4b2SJaegeuk Kim seg_i->sum_blk->entries[j] = *s; 3891351df4b2SJaegeuk Kim offset += SUMMARY_SIZE; 389209cbfeafSKirill A. Shutemov if (offset + SUMMARY_SIZE <= PAGE_SIZE - 3893351df4b2SJaegeuk Kim SUM_FOOTER_SIZE) 3894351df4b2SJaegeuk Kim continue; 3895351df4b2SJaegeuk Kim 3896351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 3897351df4b2SJaegeuk Kim page = NULL; 3898351df4b2SJaegeuk Kim 38994d57b86dSChao Yu page = f2fs_get_meta_page(sbi, start++); 39007735730dSChao Yu if (IS_ERR(page)) 39017735730dSChao Yu return PTR_ERR(page); 3902351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 3903351df4b2SJaegeuk Kim offset = 0; 3904351df4b2SJaegeuk Kim } 3905351df4b2SJaegeuk Kim } 3906351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 39077735730dSChao Yu return 0; 3908351df4b2SJaegeuk Kim } 3909351df4b2SJaegeuk Kim 3910351df4b2SJaegeuk Kim static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) 3911351df4b2SJaegeuk Kim { 3912351df4b2SJaegeuk Kim struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 3913351df4b2SJaegeuk Kim struct f2fs_summary_block *sum; 3914351df4b2SJaegeuk Kim struct curseg_info *curseg; 3915351df4b2SJaegeuk Kim struct page *new; 3916351df4b2SJaegeuk Kim unsigned short blk_off; 3917351df4b2SJaegeuk Kim unsigned int segno = 0; 3918351df4b2SJaegeuk Kim block_t blk_addr = 0; 39197735730dSChao Yu int err = 0; 3920351df4b2SJaegeuk Kim 3921351df4b2SJaegeuk Kim /* get segment number and block addr */ 3922351df4b2SJaegeuk Kim if (IS_DATASEG(type)) { 3923351df4b2SJaegeuk Kim segno = le32_to_cpu(ckpt->cur_data_segno[type]); 3924351df4b2SJaegeuk Kim blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - 3925351df4b2SJaegeuk Kim CURSEG_HOT_DATA]); 3926119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) 3927d0b9e42aSChao Yu blk_addr = sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type); 3928351df4b2SJaegeuk Kim else 3929351df4b2SJaegeuk Kim blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); 3930351df4b2SJaegeuk Kim } else { 3931351df4b2SJaegeuk Kim segno = le32_to_cpu(ckpt->cur_node_segno[type - 3932351df4b2SJaegeuk Kim CURSEG_HOT_NODE]); 3933351df4b2SJaegeuk Kim blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - 3934351df4b2SJaegeuk Kim CURSEG_HOT_NODE]); 3935119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) 3936351df4b2SJaegeuk Kim blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, 3937351df4b2SJaegeuk Kim type - CURSEG_HOT_NODE); 3938351df4b2SJaegeuk Kim else 3939351df4b2SJaegeuk Kim blk_addr = GET_SUM_BLOCK(sbi, segno); 3940351df4b2SJaegeuk Kim } 3941351df4b2SJaegeuk Kim 39424d57b86dSChao Yu new = f2fs_get_meta_page(sbi, blk_addr); 39437735730dSChao Yu if (IS_ERR(new)) 39447735730dSChao Yu return PTR_ERR(new); 3945351df4b2SJaegeuk Kim sum = (struct f2fs_summary_block *)page_address(new); 3946351df4b2SJaegeuk Kim 3947351df4b2SJaegeuk Kim if (IS_NODESEG(type)) { 3948119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) { 3949351df4b2SJaegeuk Kim struct f2fs_summary *ns = &sum->entries[0]; 3950351df4b2SJaegeuk Kim int i; 39515f029c04SYi Zhuang 3952351df4b2SJaegeuk Kim for (i = 0; i < sbi->blocks_per_seg; i++, ns++) { 3953351df4b2SJaegeuk Kim ns->version = 0; 3954351df4b2SJaegeuk Kim ns->ofs_in_node = 0; 3955351df4b2SJaegeuk Kim } 3956351df4b2SJaegeuk Kim } else { 39577735730dSChao Yu err = f2fs_restore_node_summary(sbi, segno, sum); 39587735730dSChao Yu if (err) 39597735730dSChao Yu goto out; 3960351df4b2SJaegeuk Kim } 3961351df4b2SJaegeuk Kim } 3962351df4b2SJaegeuk Kim 3963351df4b2SJaegeuk Kim /* set uncompleted segment to curseg */ 3964351df4b2SJaegeuk Kim curseg = CURSEG_I(sbi, type); 3965351df4b2SJaegeuk Kim mutex_lock(&curseg->curseg_mutex); 3966b7ad7512SChao Yu 3967b7ad7512SChao Yu /* update journal info */ 3968b7ad7512SChao Yu down_write(&curseg->journal_rwsem); 3969b7ad7512SChao Yu memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE); 3970b7ad7512SChao Yu up_write(&curseg->journal_rwsem); 3971b7ad7512SChao Yu 3972b7ad7512SChao Yu memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE); 3973b7ad7512SChao Yu memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE); 3974351df4b2SJaegeuk Kim curseg->next_segno = segno; 3975351df4b2SJaegeuk Kim reset_curseg(sbi, type, 0); 3976351df4b2SJaegeuk Kim curseg->alloc_type = ckpt->alloc_type[type]; 3977351df4b2SJaegeuk Kim curseg->next_blkoff = blk_off; 3978351df4b2SJaegeuk Kim mutex_unlock(&curseg->curseg_mutex); 39797735730dSChao Yu out: 3980351df4b2SJaegeuk Kim f2fs_put_page(new, 1); 39817735730dSChao Yu return err; 3982351df4b2SJaegeuk Kim } 3983351df4b2SJaegeuk Kim 3984351df4b2SJaegeuk Kim static int restore_curseg_summaries(struct f2fs_sb_info *sbi) 3985351df4b2SJaegeuk Kim { 398621d3f8e1SJin Qian struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal; 398721d3f8e1SJin Qian struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal; 3988351df4b2SJaegeuk Kim int type = CURSEG_HOT_DATA; 3989e4fc5fbfSChao Yu int err; 3990351df4b2SJaegeuk Kim 3991aaec2b1dSChao Yu if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) { 39924d57b86dSChao Yu int npages = f2fs_npages_for_summary_flush(sbi, true); 39933fa06d7bSChao Yu 39943fa06d7bSChao Yu if (npages >= 2) 39954d57b86dSChao Yu f2fs_ra_meta_pages(sbi, start_sum_block(sbi), npages, 399626879fb1SChao Yu META_CP, true); 39973fa06d7bSChao Yu 3998351df4b2SJaegeuk Kim /* restore for compacted data summary */ 39997735730dSChao Yu err = read_compacted_summaries(sbi); 40007735730dSChao Yu if (err) 40017735730dSChao Yu return err; 4002351df4b2SJaegeuk Kim type = CURSEG_HOT_NODE; 4003351df4b2SJaegeuk Kim } 4004351df4b2SJaegeuk Kim 4005119ee914SJaegeuk Kim if (__exist_node_summaries(sbi)) 4006d0b9e42aSChao Yu f2fs_ra_meta_pages(sbi, 4007d0b9e42aSChao Yu sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type), 4008d0b9e42aSChao Yu NR_CURSEG_PERSIST_TYPE - type, META_CP, true); 40093fa06d7bSChao Yu 4010e4fc5fbfSChao Yu for (; type <= CURSEG_COLD_NODE; type++) { 4011e4fc5fbfSChao Yu err = read_normal_summaries(sbi, type); 4012e4fc5fbfSChao Yu if (err) 4013e4fc5fbfSChao Yu return err; 4014e4fc5fbfSChao Yu } 4015e4fc5fbfSChao Yu 401621d3f8e1SJin Qian /* sanity check for summary blocks */ 401721d3f8e1SJin Qian if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || 40189227d522SSahitya Tummala sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) { 4019833dcd35SJoe Perches f2fs_err(sbi, "invalid journal entries nats %u sits %u", 40209227d522SSahitya Tummala nats_in_cursum(nat_j), sits_in_cursum(sit_j)); 402121d3f8e1SJin Qian return -EINVAL; 40229227d522SSahitya Tummala } 402321d3f8e1SJin Qian 4024351df4b2SJaegeuk Kim return 0; 4025351df4b2SJaegeuk Kim } 4026351df4b2SJaegeuk Kim 4027351df4b2SJaegeuk Kim static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) 4028351df4b2SJaegeuk Kim { 4029351df4b2SJaegeuk Kim struct page *page; 4030351df4b2SJaegeuk Kim unsigned char *kaddr; 4031351df4b2SJaegeuk Kim struct f2fs_summary *summary; 4032351df4b2SJaegeuk Kim struct curseg_info *seg_i; 4033351df4b2SJaegeuk Kim int written_size = 0; 4034351df4b2SJaegeuk Kim int i, j; 4035351df4b2SJaegeuk Kim 40364d57b86dSChao Yu page = f2fs_grab_meta_page(sbi, blkaddr++); 4037351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 403881114baaSChao Yu memset(kaddr, 0, PAGE_SIZE); 4039351df4b2SJaegeuk Kim 4040351df4b2SJaegeuk Kim /* Step 1: write nat cache */ 4041351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); 4042b7ad7512SChao Yu memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE); 4043351df4b2SJaegeuk Kim written_size += SUM_JOURNAL_SIZE; 4044351df4b2SJaegeuk Kim 4045351df4b2SJaegeuk Kim /* Step 2: write sit cache */ 4046351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); 4047b7ad7512SChao Yu memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE); 4048351df4b2SJaegeuk Kim written_size += SUM_JOURNAL_SIZE; 4049351df4b2SJaegeuk Kim 4050351df4b2SJaegeuk Kim /* Step 3: write summary entries */ 4051351df4b2SJaegeuk Kim for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 4052351df4b2SJaegeuk Kim unsigned short blkoff; 40535f029c04SYi Zhuang 4054351df4b2SJaegeuk Kim seg_i = CURSEG_I(sbi, i); 4055351df4b2SJaegeuk Kim if (sbi->ckpt->alloc_type[i] == SSR) 4056351df4b2SJaegeuk Kim blkoff = sbi->blocks_per_seg; 4057351df4b2SJaegeuk Kim else 4058351df4b2SJaegeuk Kim blkoff = curseg_blkoff(sbi, i); 4059351df4b2SJaegeuk Kim 4060351df4b2SJaegeuk Kim for (j = 0; j < blkoff; j++) { 4061351df4b2SJaegeuk Kim if (!page) { 40624d57b86dSChao Yu page = f2fs_grab_meta_page(sbi, blkaddr++); 4063351df4b2SJaegeuk Kim kaddr = (unsigned char *)page_address(page); 406481114baaSChao Yu memset(kaddr, 0, PAGE_SIZE); 4065351df4b2SJaegeuk Kim written_size = 0; 4066351df4b2SJaegeuk Kim } 4067351df4b2SJaegeuk Kim summary = (struct f2fs_summary *)(kaddr + written_size); 4068351df4b2SJaegeuk Kim *summary = seg_i->sum_blk->entries[j]; 4069351df4b2SJaegeuk Kim written_size += SUMMARY_SIZE; 4070351df4b2SJaegeuk Kim 407109cbfeafSKirill A. Shutemov if (written_size + SUMMARY_SIZE <= PAGE_SIZE - 4072351df4b2SJaegeuk Kim SUM_FOOTER_SIZE) 4073351df4b2SJaegeuk Kim continue; 4074351df4b2SJaegeuk Kim 4075e8d61a74SChao Yu set_page_dirty(page); 4076351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 4077351df4b2SJaegeuk Kim page = NULL; 4078351df4b2SJaegeuk Kim } 4079351df4b2SJaegeuk Kim } 4080e8d61a74SChao Yu if (page) { 4081e8d61a74SChao Yu set_page_dirty(page); 4082351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 4083351df4b2SJaegeuk Kim } 4084e8d61a74SChao Yu } 4085351df4b2SJaegeuk Kim 4086351df4b2SJaegeuk Kim static void write_normal_summaries(struct f2fs_sb_info *sbi, 4087351df4b2SJaegeuk Kim block_t blkaddr, int type) 4088351df4b2SJaegeuk Kim { 4089351df4b2SJaegeuk Kim int i, end; 40905f029c04SYi Zhuang 4091351df4b2SJaegeuk Kim if (IS_DATASEG(type)) 4092351df4b2SJaegeuk Kim end = type + NR_CURSEG_DATA_TYPE; 4093351df4b2SJaegeuk Kim else 4094351df4b2SJaegeuk Kim end = type + NR_CURSEG_NODE_TYPE; 4095351df4b2SJaegeuk Kim 4096b7ad7512SChao Yu for (i = type; i < end; i++) 4097b7ad7512SChao Yu write_current_sum_page(sbi, i, blkaddr + (i - type)); 4098351df4b2SJaegeuk Kim } 4099351df4b2SJaegeuk Kim 41004d57b86dSChao Yu void f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) 4101351df4b2SJaegeuk Kim { 4102aaec2b1dSChao Yu if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) 4103351df4b2SJaegeuk Kim write_compacted_summaries(sbi, start_blk); 4104351df4b2SJaegeuk Kim else 4105351df4b2SJaegeuk Kim write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); 4106351df4b2SJaegeuk Kim } 4107351df4b2SJaegeuk Kim 41084d57b86dSChao Yu void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) 4109351df4b2SJaegeuk Kim { 4110351df4b2SJaegeuk Kim write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); 4111351df4b2SJaegeuk Kim } 4112351df4b2SJaegeuk Kim 41134d57b86dSChao Yu int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, 4114351df4b2SJaegeuk Kim unsigned int val, int alloc) 4115351df4b2SJaegeuk Kim { 4116351df4b2SJaegeuk Kim int i; 4117351df4b2SJaegeuk Kim 4118351df4b2SJaegeuk Kim if (type == NAT_JOURNAL) { 4119dfc08a12SChao Yu for (i = 0; i < nats_in_cursum(journal); i++) { 4120dfc08a12SChao Yu if (le32_to_cpu(nid_in_journal(journal, i)) == val) 4121351df4b2SJaegeuk Kim return i; 4122351df4b2SJaegeuk Kim } 4123dfc08a12SChao Yu if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL)) 4124dfc08a12SChao Yu return update_nats_in_cursum(journal, 1); 4125351df4b2SJaegeuk Kim } else if (type == SIT_JOURNAL) { 4126dfc08a12SChao Yu for (i = 0; i < sits_in_cursum(journal); i++) 4127dfc08a12SChao Yu if (le32_to_cpu(segno_in_journal(journal, i)) == val) 4128351df4b2SJaegeuk Kim return i; 4129dfc08a12SChao Yu if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL)) 4130dfc08a12SChao Yu return update_sits_in_cursum(journal, 1); 4131351df4b2SJaegeuk Kim } 4132351df4b2SJaegeuk Kim return -1; 4133351df4b2SJaegeuk Kim } 4134351df4b2SJaegeuk Kim 4135351df4b2SJaegeuk Kim static struct page *get_current_sit_page(struct f2fs_sb_info *sbi, 4136351df4b2SJaegeuk Kim unsigned int segno) 4137351df4b2SJaegeuk Kim { 413886f33603SJaegeuk Kim return f2fs_get_meta_page(sbi, current_sit_addr(sbi, segno)); 4139351df4b2SJaegeuk Kim } 4140351df4b2SJaegeuk Kim 4141351df4b2SJaegeuk Kim static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, 4142351df4b2SJaegeuk Kim unsigned int start) 4143351df4b2SJaegeuk Kim { 4144351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 4145068c3cd8SYunlei He struct page *page; 4146351df4b2SJaegeuk Kim pgoff_t src_off, dst_off; 4147351df4b2SJaegeuk Kim 4148351df4b2SJaegeuk Kim src_off = current_sit_addr(sbi, start); 4149351df4b2SJaegeuk Kim dst_off = next_sit_addr(sbi, src_off); 4150351df4b2SJaegeuk Kim 41514d57b86dSChao Yu page = f2fs_grab_meta_page(sbi, dst_off); 4152068c3cd8SYunlei He seg_info_to_sit_page(sbi, page, start); 4153351df4b2SJaegeuk Kim 4154068c3cd8SYunlei He set_page_dirty(page); 4155351df4b2SJaegeuk Kim set_to_next_sit(sit_i, start); 4156351df4b2SJaegeuk Kim 4157068c3cd8SYunlei He return page; 4158351df4b2SJaegeuk Kim } 4159351df4b2SJaegeuk Kim 4160184a5cd2SChao Yu static struct sit_entry_set *grab_sit_entry_set(void) 4161184a5cd2SChao Yu { 4162184a5cd2SChao Yu struct sit_entry_set *ses = 416332410577SChao Yu f2fs_kmem_cache_alloc(sit_entry_set_slab, 416432410577SChao Yu GFP_NOFS, true, NULL); 4165184a5cd2SChao Yu 4166184a5cd2SChao Yu ses->entry_cnt = 0; 4167184a5cd2SChao Yu INIT_LIST_HEAD(&ses->set_list); 4168184a5cd2SChao Yu return ses; 4169184a5cd2SChao Yu } 4170184a5cd2SChao Yu 4171184a5cd2SChao Yu static void release_sit_entry_set(struct sit_entry_set *ses) 4172184a5cd2SChao Yu { 4173184a5cd2SChao Yu list_del(&ses->set_list); 4174184a5cd2SChao Yu kmem_cache_free(sit_entry_set_slab, ses); 4175184a5cd2SChao Yu } 4176184a5cd2SChao Yu 4177184a5cd2SChao Yu static void adjust_sit_entry_set(struct sit_entry_set *ses, 4178184a5cd2SChao Yu struct list_head *head) 4179184a5cd2SChao Yu { 4180184a5cd2SChao Yu struct sit_entry_set *next = ses; 4181184a5cd2SChao Yu 4182184a5cd2SChao Yu if (list_is_last(&ses->set_list, head)) 4183184a5cd2SChao Yu return; 4184184a5cd2SChao Yu 4185184a5cd2SChao Yu list_for_each_entry_continue(next, head, set_list) 4186184a5cd2SChao Yu if (ses->entry_cnt <= next->entry_cnt) 4187184a5cd2SChao Yu break; 4188184a5cd2SChao Yu 4189184a5cd2SChao Yu list_move_tail(&ses->set_list, &next->set_list); 4190184a5cd2SChao Yu } 4191184a5cd2SChao Yu 4192184a5cd2SChao Yu static void add_sit_entry(unsigned int segno, struct list_head *head) 4193184a5cd2SChao Yu { 4194184a5cd2SChao Yu struct sit_entry_set *ses; 4195184a5cd2SChao Yu unsigned int start_segno = START_SEGNO(segno); 4196184a5cd2SChao Yu 4197184a5cd2SChao Yu list_for_each_entry(ses, head, set_list) { 4198184a5cd2SChao Yu if (ses->start_segno == start_segno) { 4199184a5cd2SChao Yu ses->entry_cnt++; 4200184a5cd2SChao Yu adjust_sit_entry_set(ses, head); 4201184a5cd2SChao Yu return; 4202184a5cd2SChao Yu } 4203184a5cd2SChao Yu } 4204184a5cd2SChao Yu 4205184a5cd2SChao Yu ses = grab_sit_entry_set(); 4206184a5cd2SChao Yu 4207184a5cd2SChao Yu ses->start_segno = start_segno; 4208184a5cd2SChao Yu ses->entry_cnt++; 4209184a5cd2SChao Yu list_add(&ses->set_list, head); 4210184a5cd2SChao Yu } 4211184a5cd2SChao Yu 4212184a5cd2SChao Yu static void add_sits_in_set(struct f2fs_sb_info *sbi) 4213184a5cd2SChao Yu { 4214184a5cd2SChao Yu struct f2fs_sm_info *sm_info = SM_I(sbi); 4215184a5cd2SChao Yu struct list_head *set_list = &sm_info->sit_entry_set; 4216184a5cd2SChao Yu unsigned long *bitmap = SIT_I(sbi)->dirty_sentries_bitmap; 4217184a5cd2SChao Yu unsigned int segno; 4218184a5cd2SChao Yu 42197cd8558bSJaegeuk Kim for_each_set_bit(segno, bitmap, MAIN_SEGS(sbi)) 4220184a5cd2SChao Yu add_sit_entry(segno, set_list); 4221184a5cd2SChao Yu } 4222184a5cd2SChao Yu 4223184a5cd2SChao Yu static void remove_sits_in_journal(struct f2fs_sb_info *sbi) 4224351df4b2SJaegeuk Kim { 4225351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 4226b7ad7512SChao Yu struct f2fs_journal *journal = curseg->journal; 4227351df4b2SJaegeuk Kim int i; 4228351df4b2SJaegeuk Kim 4229b7ad7512SChao Yu down_write(&curseg->journal_rwsem); 4230dfc08a12SChao Yu for (i = 0; i < sits_in_cursum(journal); i++) { 4231351df4b2SJaegeuk Kim unsigned int segno; 4232184a5cd2SChao Yu bool dirtied; 4233184a5cd2SChao Yu 4234dfc08a12SChao Yu segno = le32_to_cpu(segno_in_journal(journal, i)); 4235184a5cd2SChao Yu dirtied = __mark_sit_entry_dirty(sbi, segno); 4236184a5cd2SChao Yu 4237184a5cd2SChao Yu if (!dirtied) 4238184a5cd2SChao Yu add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); 4239351df4b2SJaegeuk Kim } 4240dfc08a12SChao Yu update_sits_in_cursum(journal, -i); 4241b7ad7512SChao Yu up_write(&curseg->journal_rwsem); 4242351df4b2SJaegeuk Kim } 4243351df4b2SJaegeuk Kim 42440a8165d7SJaegeuk Kim /* 4245351df4b2SJaegeuk Kim * CP calls this function, which flushes SIT entries including sit_journal, 4246351df4b2SJaegeuk Kim * and moves prefree segs to free segs. 4247351df4b2SJaegeuk Kim */ 42484d57b86dSChao Yu void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) 4249351df4b2SJaegeuk Kim { 4250351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 4251351df4b2SJaegeuk Kim unsigned long *bitmap = sit_i->dirty_sentries_bitmap; 4252351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 4253b7ad7512SChao Yu struct f2fs_journal *journal = curseg->journal; 4254184a5cd2SChao Yu struct sit_entry_set *ses, *tmp; 4255184a5cd2SChao Yu struct list_head *head = &SM_I(sbi)->sit_entry_set; 425604f0b2eaSQiuyang Sun bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); 42574b2fecc8SJaegeuk Kim struct seg_entry *se; 4258351df4b2SJaegeuk Kim 42593d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 4260351df4b2SJaegeuk Kim 42612b11a74bSWanpeng Li if (!sit_i->dirty_sentries) 42622b11a74bSWanpeng Li goto out; 42632b11a74bSWanpeng Li 4264351df4b2SJaegeuk Kim /* 4265184a5cd2SChao Yu * add and account sit entries of dirty bitmap in sit entry 4266184a5cd2SChao Yu * set temporarily 4267351df4b2SJaegeuk Kim */ 4268184a5cd2SChao Yu add_sits_in_set(sbi); 4269351df4b2SJaegeuk Kim 4270184a5cd2SChao Yu /* 4271184a5cd2SChao Yu * if there are no enough space in journal to store dirty sit 4272184a5cd2SChao Yu * entries, remove all entries from journal and add and account 4273184a5cd2SChao Yu * them in sit entry set. 4274184a5cd2SChao Yu */ 427504f0b2eaSQiuyang Sun if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) || 427604f0b2eaSQiuyang Sun !to_journal) 4277184a5cd2SChao Yu remove_sits_in_journal(sbi); 4278184a5cd2SChao Yu 4279184a5cd2SChao Yu /* 4280184a5cd2SChao Yu * there are two steps to flush sit entries: 4281184a5cd2SChao Yu * #1, flush sit entries to journal in current cold data summary block. 4282184a5cd2SChao Yu * #2, flush sit entries to sit page. 4283184a5cd2SChao Yu */ 4284184a5cd2SChao Yu list_for_each_entry_safe(ses, tmp, head, set_list) { 42854a257ed6SJaegeuk Kim struct page *page = NULL; 4286184a5cd2SChao Yu struct f2fs_sit_block *raw_sit = NULL; 4287184a5cd2SChao Yu unsigned int start_segno = ses->start_segno; 4288184a5cd2SChao Yu unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, 42897cd8558bSJaegeuk Kim (unsigned long)MAIN_SEGS(sbi)); 4290184a5cd2SChao Yu unsigned int segno = start_segno; 4291184a5cd2SChao Yu 4292184a5cd2SChao Yu if (to_journal && 4293dfc08a12SChao Yu !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) 4294184a5cd2SChao Yu to_journal = false; 4295184a5cd2SChao Yu 4296b7ad7512SChao Yu if (to_journal) { 4297b7ad7512SChao Yu down_write(&curseg->journal_rwsem); 4298b7ad7512SChao Yu } else { 4299184a5cd2SChao Yu page = get_next_sit_page(sbi, start_segno); 4300184a5cd2SChao Yu raw_sit = page_address(page); 4301184a5cd2SChao Yu } 4302184a5cd2SChao Yu 4303184a5cd2SChao Yu /* flush dirty sit entries in region of current sit set */ 4304184a5cd2SChao Yu for_each_set_bit_from(segno, bitmap, end) { 4305184a5cd2SChao Yu int offset, sit_offset; 43064b2fecc8SJaegeuk Kim 43074b2fecc8SJaegeuk Kim se = get_seg_entry(sbi, segno); 430856b07e7eSZhikang Zhang #ifdef CONFIG_F2FS_CHECK_FS 430956b07e7eSZhikang Zhang if (memcmp(se->cur_valid_map, se->cur_valid_map_mir, 431056b07e7eSZhikang Zhang SIT_VBLOCK_MAP_SIZE)) 431156b07e7eSZhikang Zhang f2fs_bug_on(sbi, 1); 431256b07e7eSZhikang Zhang #endif 4313351df4b2SJaegeuk Kim 4314b2955550SJaegeuk Kim /* add discard candidates */ 4315c473f1a9SChao Yu if (!(cpc->reason & CP_DISCARD)) { 43164b2fecc8SJaegeuk Kim cpc->trim_start = segno; 431725290fa5SJaegeuk Kim add_discard_addrs(sbi, cpc, false); 43184b2fecc8SJaegeuk Kim } 4319b2955550SJaegeuk Kim 4320184a5cd2SChao Yu if (to_journal) { 43214d57b86dSChao Yu offset = f2fs_lookup_journal_in_cursum(journal, 4322184a5cd2SChao Yu SIT_JOURNAL, segno, 1); 4323184a5cd2SChao Yu f2fs_bug_on(sbi, offset < 0); 4324dfc08a12SChao Yu segno_in_journal(journal, offset) = 4325184a5cd2SChao Yu cpu_to_le32(segno); 4326184a5cd2SChao Yu seg_info_to_raw_sit(se, 4327dfc08a12SChao Yu &sit_in_journal(journal, offset)); 432856b07e7eSZhikang Zhang check_block_count(sbi, segno, 432956b07e7eSZhikang Zhang &sit_in_journal(journal, offset)); 4330184a5cd2SChao Yu } else { 4331184a5cd2SChao Yu sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); 4332184a5cd2SChao Yu seg_info_to_raw_sit(se, 4333184a5cd2SChao Yu &raw_sit->entries[sit_offset]); 433456b07e7eSZhikang Zhang check_block_count(sbi, segno, 433556b07e7eSZhikang Zhang &raw_sit->entries[sit_offset]); 4336351df4b2SJaegeuk Kim } 4337351df4b2SJaegeuk Kim 4338351df4b2SJaegeuk Kim __clear_bit(segno, bitmap); 4339351df4b2SJaegeuk Kim sit_i->dirty_sentries--; 4340184a5cd2SChao Yu ses->entry_cnt--; 4341351df4b2SJaegeuk Kim } 4342184a5cd2SChao Yu 4343b7ad7512SChao Yu if (to_journal) 4344b7ad7512SChao Yu up_write(&curseg->journal_rwsem); 4345b7ad7512SChao Yu else 4346184a5cd2SChao Yu f2fs_put_page(page, 1); 4347184a5cd2SChao Yu 4348184a5cd2SChao Yu f2fs_bug_on(sbi, ses->entry_cnt); 4349184a5cd2SChao Yu release_sit_entry_set(ses); 4350184a5cd2SChao Yu } 4351184a5cd2SChao Yu 4352184a5cd2SChao Yu f2fs_bug_on(sbi, !list_empty(head)); 4353184a5cd2SChao Yu f2fs_bug_on(sbi, sit_i->dirty_sentries); 4354184a5cd2SChao Yu out: 4355c473f1a9SChao Yu if (cpc->reason & CP_DISCARD) { 4356650d3c4eSYunlei He __u64 trim_start = cpc->trim_start; 4357650d3c4eSYunlei He 43584b2fecc8SJaegeuk Kim for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) 435925290fa5SJaegeuk Kim add_discard_addrs(sbi, cpc, false); 4360650d3c4eSYunlei He 4361650d3c4eSYunlei He cpc->trim_start = trim_start; 43624b2fecc8SJaegeuk Kim } 43633d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 4364351df4b2SJaegeuk Kim 4365351df4b2SJaegeuk Kim set_prefree_as_free_segments(sbi); 4366351df4b2SJaegeuk Kim } 4367351df4b2SJaegeuk Kim 4368351df4b2SJaegeuk Kim static int build_sit_info(struct f2fs_sb_info *sbi) 4369351df4b2SJaegeuk Kim { 4370351df4b2SJaegeuk Kim struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); 4371351df4b2SJaegeuk Kim struct sit_info *sit_i; 4372351df4b2SJaegeuk Kim unsigned int sit_segs, start; 43732fde3dd1SChao Yu char *src_bitmap, *bitmap; 4374bbf9f7d9SSahitya Tummala unsigned int bitmap_size, main_bitmap_size, sit_bitmap_size; 43754f993264SChao Yu unsigned int discard_map = f2fs_block_unit_discard(sbi) ? 1 : 0; 4376351df4b2SJaegeuk Kim 4377351df4b2SJaegeuk Kim /* allocate memory for SIT information */ 4378acbf054dSChao Yu sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); 4379351df4b2SJaegeuk Kim if (!sit_i) 4380351df4b2SJaegeuk Kim return -ENOMEM; 4381351df4b2SJaegeuk Kim 4382351df4b2SJaegeuk Kim SM_I(sbi)->sit_info = sit_i; 4383351df4b2SJaegeuk Kim 43849d2a789cSKees Cook sit_i->sentries = 43859d2a789cSKees Cook f2fs_kvzalloc(sbi, array_size(sizeof(struct seg_entry), 43869d2a789cSKees Cook MAIN_SEGS(sbi)), 43879d2a789cSKees Cook GFP_KERNEL); 4388351df4b2SJaegeuk Kim if (!sit_i->sentries) 4389351df4b2SJaegeuk Kim return -ENOMEM; 4390351df4b2SJaegeuk Kim 4391bbf9f7d9SSahitya Tummala main_bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 4392bbf9f7d9SSahitya Tummala sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, main_bitmap_size, 4393628b3d14SChao Yu GFP_KERNEL); 4394351df4b2SJaegeuk Kim if (!sit_i->dirty_sentries_bitmap) 4395351df4b2SJaegeuk Kim return -ENOMEM; 4396351df4b2SJaegeuk Kim 43972fde3dd1SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 43984f993264SChao Yu bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map); 43992fde3dd1SChao Yu #else 44004f993264SChao Yu bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); 44012fde3dd1SChao Yu #endif 44022fde3dd1SChao Yu sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 44032fde3dd1SChao Yu if (!sit_i->bitmap) 44043e025740SJaegeuk Kim return -ENOMEM; 44053e025740SJaegeuk Kim 44062fde3dd1SChao Yu bitmap = sit_i->bitmap; 44072fde3dd1SChao Yu 44082fde3dd1SChao Yu for (start = 0; start < MAIN_SEGS(sbi); start++) { 44092fde3dd1SChao Yu sit_i->sentries[start].cur_valid_map = bitmap; 44102fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 44112fde3dd1SChao Yu 44122fde3dd1SChao Yu sit_i->sentries[start].ckpt_valid_map = bitmap; 44132fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 44142fde3dd1SChao Yu 4415355e7891SChao Yu #ifdef CONFIG_F2FS_CHECK_FS 44162fde3dd1SChao Yu sit_i->sentries[start].cur_valid_map_mir = bitmap; 44172fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 4418355e7891SChao Yu #endif 4419355e7891SChao Yu 44204f993264SChao Yu if (discard_map) { 44212fde3dd1SChao Yu sit_i->sentries[start].discard_map = bitmap; 44222fde3dd1SChao Yu bitmap += SIT_VBLOCK_MAP_SIZE; 4423351df4b2SJaegeuk Kim } 44244f993264SChao Yu } 4425351df4b2SJaegeuk Kim 4426acbf054dSChao Yu sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); 442760a3b782SJaegeuk Kim if (!sit_i->tmp_map) 442860a3b782SJaegeuk Kim return -ENOMEM; 442960a3b782SJaegeuk Kim 44302c70c5e3SChao Yu if (__is_large_section(sbi)) { 44319d2a789cSKees Cook sit_i->sec_entries = 44329d2a789cSKees Cook f2fs_kvzalloc(sbi, array_size(sizeof(struct sec_entry), 44339d2a789cSKees Cook MAIN_SECS(sbi)), 44349d2a789cSKees Cook GFP_KERNEL); 4435351df4b2SJaegeuk Kim if (!sit_i->sec_entries) 4436351df4b2SJaegeuk Kim return -ENOMEM; 4437351df4b2SJaegeuk Kim } 4438351df4b2SJaegeuk Kim 4439351df4b2SJaegeuk Kim /* get information related with SIT */ 4440351df4b2SJaegeuk Kim sit_segs = le32_to_cpu(raw_super->segment_count_sit) >> 1; 4441351df4b2SJaegeuk Kim 4442351df4b2SJaegeuk Kim /* setup SIT bitmap from ckeckpoint pack */ 4443bbf9f7d9SSahitya Tummala sit_bitmap_size = __bitmap_size(sbi, SIT_BITMAP); 4444351df4b2SJaegeuk Kim src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); 4445351df4b2SJaegeuk Kim 4446bbf9f7d9SSahitya Tummala sit_i->sit_bitmap = kmemdup(src_bitmap, sit_bitmap_size, GFP_KERNEL); 4447ae27d62eSChao Yu if (!sit_i->sit_bitmap) 4448351df4b2SJaegeuk Kim return -ENOMEM; 4449351df4b2SJaegeuk Kim 4450ae27d62eSChao Yu #ifdef CONFIG_F2FS_CHECK_FS 4451bbf9f7d9SSahitya Tummala sit_i->sit_bitmap_mir = kmemdup(src_bitmap, 4452bbf9f7d9SSahitya Tummala sit_bitmap_size, GFP_KERNEL); 4453ae27d62eSChao Yu if (!sit_i->sit_bitmap_mir) 4454ae27d62eSChao Yu return -ENOMEM; 4455bbf9f7d9SSahitya Tummala 4456bbf9f7d9SSahitya Tummala sit_i->invalid_segmap = f2fs_kvzalloc(sbi, 4457bbf9f7d9SSahitya Tummala main_bitmap_size, GFP_KERNEL); 4458bbf9f7d9SSahitya Tummala if (!sit_i->invalid_segmap) 4459bbf9f7d9SSahitya Tummala return -ENOMEM; 4460ae27d62eSChao Yu #endif 4461ae27d62eSChao Yu 4462351df4b2SJaegeuk Kim /* init SIT information */ 4463351df4b2SJaegeuk Kim sit_i->s_ops = &default_salloc_ops; 4464351df4b2SJaegeuk Kim 4465351df4b2SJaegeuk Kim sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); 4466351df4b2SJaegeuk Kim sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; 4467c79b7ff1SJaegeuk Kim sit_i->written_valid_blocks = 0; 4468bbf9f7d9SSahitya Tummala sit_i->bitmap_size = sit_bitmap_size; 4469351df4b2SJaegeuk Kim sit_i->dirty_sentries = 0; 4470351df4b2SJaegeuk Kim sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; 4471351df4b2SJaegeuk Kim sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); 4472a7e679b5SJaegeuk Kim sit_i->mounted_time = ktime_get_boottime_seconds(); 44733d26fa6bSChao Yu init_rwsem(&sit_i->sentry_lock); 4474351df4b2SJaegeuk Kim return 0; 4475351df4b2SJaegeuk Kim } 4476351df4b2SJaegeuk Kim 4477351df4b2SJaegeuk Kim static int build_free_segmap(struct f2fs_sb_info *sbi) 4478351df4b2SJaegeuk Kim { 4479351df4b2SJaegeuk Kim struct free_segmap_info *free_i; 4480351df4b2SJaegeuk Kim unsigned int bitmap_size, sec_bitmap_size; 4481351df4b2SJaegeuk Kim 4482351df4b2SJaegeuk Kim /* allocate memory for free segmap information */ 4483acbf054dSChao Yu free_i = f2fs_kzalloc(sbi, sizeof(struct free_segmap_info), GFP_KERNEL); 4484351df4b2SJaegeuk Kim if (!free_i) 4485351df4b2SJaegeuk Kim return -ENOMEM; 4486351df4b2SJaegeuk Kim 4487351df4b2SJaegeuk Kim SM_I(sbi)->free_info = free_i; 4488351df4b2SJaegeuk Kim 44897cd8558bSJaegeuk Kim bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 4490628b3d14SChao Yu free_i->free_segmap = f2fs_kvmalloc(sbi, bitmap_size, GFP_KERNEL); 4491351df4b2SJaegeuk Kim if (!free_i->free_segmap) 4492351df4b2SJaegeuk Kim return -ENOMEM; 4493351df4b2SJaegeuk Kim 44947cd8558bSJaegeuk Kim sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 4495628b3d14SChao Yu free_i->free_secmap = f2fs_kvmalloc(sbi, sec_bitmap_size, GFP_KERNEL); 4496351df4b2SJaegeuk Kim if (!free_i->free_secmap) 4497351df4b2SJaegeuk Kim return -ENOMEM; 4498351df4b2SJaegeuk Kim 4499351df4b2SJaegeuk Kim /* set all segments as dirty temporarily */ 4500351df4b2SJaegeuk Kim memset(free_i->free_segmap, 0xff, bitmap_size); 4501351df4b2SJaegeuk Kim memset(free_i->free_secmap, 0xff, sec_bitmap_size); 4502351df4b2SJaegeuk Kim 4503351df4b2SJaegeuk Kim /* init free segmap information */ 45047cd8558bSJaegeuk Kim free_i->start_segno = GET_SEGNO_FROM_SEG0(sbi, MAIN_BLKADDR(sbi)); 4505351df4b2SJaegeuk Kim free_i->free_segments = 0; 4506351df4b2SJaegeuk Kim free_i->free_sections = 0; 45071a118ccfSChao Yu spin_lock_init(&free_i->segmap_lock); 4508351df4b2SJaegeuk Kim return 0; 4509351df4b2SJaegeuk Kim } 4510351df4b2SJaegeuk Kim 4511351df4b2SJaegeuk Kim static int build_curseg(struct f2fs_sb_info *sbi) 4512351df4b2SJaegeuk Kim { 45131042d60fSNamjae Jeon struct curseg_info *array; 4514351df4b2SJaegeuk Kim int i; 4515351df4b2SJaegeuk Kim 4516d0b9e42aSChao Yu array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, 4517d0b9e42aSChao Yu sizeof(*array)), GFP_KERNEL); 4518351df4b2SJaegeuk Kim if (!array) 4519351df4b2SJaegeuk Kim return -ENOMEM; 4520351df4b2SJaegeuk Kim 4521351df4b2SJaegeuk Kim SM_I(sbi)->curseg_array = array; 4522351df4b2SJaegeuk Kim 4523d0b9e42aSChao Yu for (i = 0; i < NO_CHECK_TYPE; i++) { 4524351df4b2SJaegeuk Kim mutex_init(&array[i].curseg_mutex); 4525acbf054dSChao Yu array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); 4526351df4b2SJaegeuk Kim if (!array[i].sum_blk) 4527351df4b2SJaegeuk Kim return -ENOMEM; 4528b7ad7512SChao Yu init_rwsem(&array[i].journal_rwsem); 4529acbf054dSChao Yu array[i].journal = f2fs_kzalloc(sbi, 4530acbf054dSChao Yu sizeof(struct f2fs_journal), GFP_KERNEL); 4531b7ad7512SChao Yu if (!array[i].journal) 4532b7ad7512SChao Yu return -ENOMEM; 4533d0b9e42aSChao Yu if (i < NR_PERSISTENT_LOG) 4534d0b9e42aSChao Yu array[i].seg_type = CURSEG_HOT_DATA + i; 4535d0b9e42aSChao Yu else if (i == CURSEG_COLD_DATA_PINNED) 4536d0b9e42aSChao Yu array[i].seg_type = CURSEG_COLD_DATA; 4537093749e2SChao Yu else if (i == CURSEG_ALL_DATA_ATGC) 4538093749e2SChao Yu array[i].seg_type = CURSEG_COLD_DATA; 4539351df4b2SJaegeuk Kim array[i].segno = NULL_SEGNO; 4540351df4b2SJaegeuk Kim array[i].next_blkoff = 0; 4541d0b9e42aSChao Yu array[i].inited = false; 4542351df4b2SJaegeuk Kim } 4543351df4b2SJaegeuk Kim return restore_curseg_summaries(sbi); 4544351df4b2SJaegeuk Kim } 4545351df4b2SJaegeuk Kim 4546c39a1b34SJaegeuk Kim static int build_sit_entries(struct f2fs_sb_info *sbi) 4547351df4b2SJaegeuk Kim { 4548351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 4549351df4b2SJaegeuk Kim struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 4550b7ad7512SChao Yu struct f2fs_journal *journal = curseg->journal; 45519c094040SYunlei He struct seg_entry *se; 45529c094040SYunlei He struct f2fs_sit_entry sit; 455374de593aSChao Yu int sit_blk_cnt = SIT_BLK_CNT(sbi); 455474de593aSChao Yu unsigned int i, start, end; 455574de593aSChao Yu unsigned int readed, start_blk = 0; 4556c39a1b34SJaegeuk Kim int err = 0; 45578a29c126SJaegeuk Kim block_t total_node_blocks = 0; 4558351df4b2SJaegeuk Kim 455974de593aSChao Yu do { 4560a8affc03SChristoph Hellwig readed = f2fs_ra_meta_pages(sbi, start_blk, BIO_MAX_VECS, 4561664ba972SJaegeuk Kim META_SIT, true); 456274de593aSChao Yu 456374de593aSChao Yu start = start_blk * sit_i->sents_per_block; 456474de593aSChao Yu end = (start_blk + readed) * sit_i->sents_per_block; 456574de593aSChao Yu 45667cd8558bSJaegeuk Kim for (; start < end && start < MAIN_SEGS(sbi); start++) { 4567351df4b2SJaegeuk Kim struct f2fs_sit_block *sit_blk; 4568351df4b2SJaegeuk Kim struct page *page; 4569351df4b2SJaegeuk Kim 45709c094040SYunlei He se = &sit_i->sentries[start]; 4571351df4b2SJaegeuk Kim page = get_current_sit_page(sbi, start); 4572edc55aafSJaegeuk Kim if (IS_ERR(page)) 4573edc55aafSJaegeuk Kim return PTR_ERR(page); 4574351df4b2SJaegeuk Kim sit_blk = (struct f2fs_sit_block *)page_address(page); 4575351df4b2SJaegeuk Kim sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; 4576351df4b2SJaegeuk Kim f2fs_put_page(page, 1); 4577d600af23SChao Yu 4578c39a1b34SJaegeuk Kim err = check_block_count(sbi, start, &sit); 4579c39a1b34SJaegeuk Kim if (err) 4580c39a1b34SJaegeuk Kim return err; 4581351df4b2SJaegeuk Kim seg_info_from_raw_sit(se, &sit); 45828a29c126SJaegeuk Kim if (IS_NODESEG(se->type)) 45838a29c126SJaegeuk Kim total_node_blocks += se->valid_blocks; 4584a66cdd98SJaegeuk Kim 45854f993264SChao Yu if (f2fs_block_unit_discard(sbi)) { 4586a66cdd98SJaegeuk Kim /* build discard map only one time */ 45871f43e2adSChao Yu if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { 45881f43e2adSChao Yu memset(se->discard_map, 0xff, 45893e025740SJaegeuk Kim SIT_VBLOCK_MAP_SIZE); 45901f43e2adSChao Yu } else { 45911f43e2adSChao Yu memcpy(se->discard_map, 45921f43e2adSChao Yu se->cur_valid_map, 45931f43e2adSChao Yu SIT_VBLOCK_MAP_SIZE); 45941f43e2adSChao Yu sbi->discard_blks += 45951f43e2adSChao Yu sbi->blocks_per_seg - 45963e025740SJaegeuk Kim se->valid_blocks; 45973e025740SJaegeuk Kim } 45984f993264SChao Yu } 4599a66cdd98SJaegeuk Kim 46002c70c5e3SChao Yu if (__is_large_section(sbi)) 4601d600af23SChao Yu get_sec_entry(sbi, start)->valid_blocks += 4602d600af23SChao Yu se->valid_blocks; 4603351df4b2SJaegeuk Kim } 460474de593aSChao Yu start_blk += readed; 460574de593aSChao Yu } while (start_blk < sit_blk_cnt); 4606d600af23SChao Yu 4607d600af23SChao Yu down_read(&curseg->journal_rwsem); 4608d600af23SChao Yu for (i = 0; i < sits_in_cursum(journal); i++) { 4609d600af23SChao Yu unsigned int old_valid_blocks; 4610d600af23SChao Yu 4611d600af23SChao Yu start = le32_to_cpu(segno_in_journal(journal, i)); 4612b2ca374fSJaegeuk Kim if (start >= MAIN_SEGS(sbi)) { 4613dcbb4c10SJoe Perches f2fs_err(sbi, "Wrong journal entry on segno %u", 4614b2ca374fSJaegeuk Kim start); 461510f966bbSChao Yu err = -EFSCORRUPTED; 4616b2ca374fSJaegeuk Kim break; 4617b2ca374fSJaegeuk Kim } 4618b2ca374fSJaegeuk Kim 4619d600af23SChao Yu se = &sit_i->sentries[start]; 4620d600af23SChao Yu sit = sit_in_journal(journal, i); 4621d600af23SChao Yu 4622d600af23SChao Yu old_valid_blocks = se->valid_blocks; 46238a29c126SJaegeuk Kim if (IS_NODESEG(se->type)) 46248a29c126SJaegeuk Kim total_node_blocks -= old_valid_blocks; 4625d600af23SChao Yu 4626c39a1b34SJaegeuk Kim err = check_block_count(sbi, start, &sit); 4627c39a1b34SJaegeuk Kim if (err) 4628c39a1b34SJaegeuk Kim break; 4629d600af23SChao Yu seg_info_from_raw_sit(se, &sit); 46308a29c126SJaegeuk Kim if (IS_NODESEG(se->type)) 46318a29c126SJaegeuk Kim total_node_blocks += se->valid_blocks; 4632d600af23SChao Yu 46334f993264SChao Yu if (f2fs_block_unit_discard(sbi)) { 46341f43e2adSChao Yu if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { 46357d20c8abSChao Yu memset(se->discard_map, 0xff, SIT_VBLOCK_MAP_SIZE); 46361f43e2adSChao Yu } else { 4637d600af23SChao Yu memcpy(se->discard_map, se->cur_valid_map, 4638d600af23SChao Yu SIT_VBLOCK_MAP_SIZE); 4639a9af3fdcSChao Yu sbi->discard_blks += old_valid_blocks; 4640a9af3fdcSChao Yu sbi->discard_blks -= se->valid_blocks; 4641d600af23SChao Yu } 46424f993264SChao Yu } 4643d600af23SChao Yu 46442c70c5e3SChao Yu if (__is_large_section(sbi)) { 4645d600af23SChao Yu get_sec_entry(sbi, start)->valid_blocks += 4646a9af3fdcSChao Yu se->valid_blocks; 4647a9af3fdcSChao Yu get_sec_entry(sbi, start)->valid_blocks -= 4648a9af3fdcSChao Yu old_valid_blocks; 4649a9af3fdcSChao Yu } 4650d600af23SChao Yu } 4651d600af23SChao Yu up_read(&curseg->journal_rwsem); 46528a29c126SJaegeuk Kim 46538a29c126SJaegeuk Kim if (!err && total_node_blocks != valid_node_count(sbi)) { 4654dcbb4c10SJoe Perches f2fs_err(sbi, "SIT is corrupted node# %u vs %u", 46558a29c126SJaegeuk Kim total_node_blocks, valid_node_count(sbi)); 465610f966bbSChao Yu err = -EFSCORRUPTED; 46578a29c126SJaegeuk Kim } 46588a29c126SJaegeuk Kim 4659c39a1b34SJaegeuk Kim return err; 4660351df4b2SJaegeuk Kim } 4661351df4b2SJaegeuk Kim 4662351df4b2SJaegeuk Kim static void init_free_segmap(struct f2fs_sb_info *sbi) 4663351df4b2SJaegeuk Kim { 4664351df4b2SJaegeuk Kim unsigned int start; 4665351df4b2SJaegeuk Kim int type; 4666de881df9SAravind Ramesh struct seg_entry *sentry; 4667351df4b2SJaegeuk Kim 46687cd8558bSJaegeuk Kim for (start = 0; start < MAIN_SEGS(sbi); start++) { 4669de881df9SAravind Ramesh if (f2fs_usable_blks_in_seg(sbi, start) == 0) 4670de881df9SAravind Ramesh continue; 4671de881df9SAravind Ramesh sentry = get_seg_entry(sbi, start); 4672351df4b2SJaegeuk Kim if (!sentry->valid_blocks) 4673351df4b2SJaegeuk Kim __set_free(sbi, start); 4674c79b7ff1SJaegeuk Kim else 4675c79b7ff1SJaegeuk Kim SIT_I(sbi)->written_valid_blocks += 4676c79b7ff1SJaegeuk Kim sentry->valid_blocks; 4677351df4b2SJaegeuk Kim } 4678351df4b2SJaegeuk Kim 4679351df4b2SJaegeuk Kim /* set use the current segments */ 4680351df4b2SJaegeuk Kim for (type = CURSEG_HOT_DATA; type <= CURSEG_COLD_NODE; type++) { 4681351df4b2SJaegeuk Kim struct curseg_info *curseg_t = CURSEG_I(sbi, type); 46825f029c04SYi Zhuang 4683351df4b2SJaegeuk Kim __set_test_and_inuse(sbi, curseg_t->segno); 4684351df4b2SJaegeuk Kim } 4685351df4b2SJaegeuk Kim } 4686351df4b2SJaegeuk Kim 4687351df4b2SJaegeuk Kim static void init_dirty_segmap(struct f2fs_sb_info *sbi) 4688351df4b2SJaegeuk Kim { 4689351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 4690351df4b2SJaegeuk Kim struct free_segmap_info *free_i = FREE_I(sbi); 4691da52f8adSJack Qiu unsigned int segno = 0, offset = 0, secno; 4692de881df9SAravind Ramesh block_t valid_blocks, usable_blks_in_seg; 4693123aaf77SShin'ichiro Kawasaki block_t blks_per_sec = BLKS_PER_SEC(sbi); 4694351df4b2SJaegeuk Kim 46958736fbf0SNamjae Jeon while (1) { 4696351df4b2SJaegeuk Kim /* find dirty segment based on free segmap */ 46977cd8558bSJaegeuk Kim segno = find_next_inuse(free_i, MAIN_SEGS(sbi), offset); 46987cd8558bSJaegeuk Kim if (segno >= MAIN_SEGS(sbi)) 4699351df4b2SJaegeuk Kim break; 4700351df4b2SJaegeuk Kim offset = segno + 1; 4701302bd348SJaegeuk Kim valid_blocks = get_valid_blocks(sbi, segno, false); 4702de881df9SAravind Ramesh usable_blks_in_seg = f2fs_usable_blks_in_seg(sbi, segno); 4703de881df9SAravind Ramesh if (valid_blocks == usable_blks_in_seg || !valid_blocks) 4704351df4b2SJaegeuk Kim continue; 4705de881df9SAravind Ramesh if (valid_blocks > usable_blks_in_seg) { 4706ec325b52SJaegeuk Kim f2fs_bug_on(sbi, 1); 4707ec325b52SJaegeuk Kim continue; 4708ec325b52SJaegeuk Kim } 4709351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 4710351df4b2SJaegeuk Kim __locate_dirty_segment(sbi, segno, DIRTY); 4711351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 4712351df4b2SJaegeuk Kim } 4713da52f8adSJack Qiu 4714da52f8adSJack Qiu if (!__is_large_section(sbi)) 4715da52f8adSJack Qiu return; 4716da52f8adSJack Qiu 4717da52f8adSJack Qiu mutex_lock(&dirty_i->seglist_lock); 47185335bfc6SJack Qiu for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { 4719da52f8adSJack Qiu valid_blocks = get_valid_blocks(sbi, segno, true); 4720da52f8adSJack Qiu secno = GET_SEC_FROM_SEG(sbi, segno); 4721da52f8adSJack Qiu 4722da52f8adSJack Qiu if (!valid_blocks || valid_blocks == blks_per_sec) 4723da52f8adSJack Qiu continue; 4724da52f8adSJack Qiu if (IS_CURSEC(sbi, secno)) 4725da52f8adSJack Qiu continue; 4726da52f8adSJack Qiu set_bit(secno, dirty_i->dirty_secmap); 4727da52f8adSJack Qiu } 4728da52f8adSJack Qiu mutex_unlock(&dirty_i->seglist_lock); 4729351df4b2SJaegeuk Kim } 4730351df4b2SJaegeuk Kim 47315ec4e49fSJaegeuk Kim static int init_victim_secmap(struct f2fs_sb_info *sbi) 4732351df4b2SJaegeuk Kim { 4733351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 47347cd8558bSJaegeuk Kim unsigned int bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 4735351df4b2SJaegeuk Kim 4736628b3d14SChao Yu dirty_i->victim_secmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 47375ec4e49fSJaegeuk Kim if (!dirty_i->victim_secmap) 4738351df4b2SJaegeuk Kim return -ENOMEM; 4739351df4b2SJaegeuk Kim return 0; 4740351df4b2SJaegeuk Kim } 4741351df4b2SJaegeuk Kim 4742351df4b2SJaegeuk Kim static int build_dirty_segmap(struct f2fs_sb_info *sbi) 4743351df4b2SJaegeuk Kim { 4744351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i; 4745351df4b2SJaegeuk Kim unsigned int bitmap_size, i; 4746351df4b2SJaegeuk Kim 4747351df4b2SJaegeuk Kim /* allocate memory for dirty segments list information */ 4748acbf054dSChao Yu dirty_i = f2fs_kzalloc(sbi, sizeof(struct dirty_seglist_info), 4749acbf054dSChao Yu GFP_KERNEL); 4750351df4b2SJaegeuk Kim if (!dirty_i) 4751351df4b2SJaegeuk Kim return -ENOMEM; 4752351df4b2SJaegeuk Kim 4753351df4b2SJaegeuk Kim SM_I(sbi)->dirty_info = dirty_i; 4754351df4b2SJaegeuk Kim mutex_init(&dirty_i->seglist_lock); 4755351df4b2SJaegeuk Kim 47567cd8558bSJaegeuk Kim bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 4757351df4b2SJaegeuk Kim 4758351df4b2SJaegeuk Kim for (i = 0; i < NR_DIRTY_TYPE; i++) { 4759628b3d14SChao Yu dirty_i->dirty_segmap[i] = f2fs_kvzalloc(sbi, bitmap_size, 4760628b3d14SChao Yu GFP_KERNEL); 4761351df4b2SJaegeuk Kim if (!dirty_i->dirty_segmap[i]) 4762351df4b2SJaegeuk Kim return -ENOMEM; 4763351df4b2SJaegeuk Kim } 4764351df4b2SJaegeuk Kim 4765da52f8adSJack Qiu if (__is_large_section(sbi)) { 4766da52f8adSJack Qiu bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 4767da52f8adSJack Qiu dirty_i->dirty_secmap = f2fs_kvzalloc(sbi, 4768da52f8adSJack Qiu bitmap_size, GFP_KERNEL); 4769da52f8adSJack Qiu if (!dirty_i->dirty_secmap) 4770da52f8adSJack Qiu return -ENOMEM; 4771da52f8adSJack Qiu } 4772da52f8adSJack Qiu 4773351df4b2SJaegeuk Kim init_dirty_segmap(sbi); 47745ec4e49fSJaegeuk Kim return init_victim_secmap(sbi); 4775351df4b2SJaegeuk Kim } 4776351df4b2SJaegeuk Kim 4777c854f4d6SChao Yu static int sanity_check_curseg(struct f2fs_sb_info *sbi) 4778c854f4d6SChao Yu { 4779c854f4d6SChao Yu int i; 4780c854f4d6SChao Yu 4781c854f4d6SChao Yu /* 4782c854f4d6SChao Yu * In LFS/SSR curseg, .next_blkoff should point to an unused blkaddr; 4783c854f4d6SChao Yu * In LFS curseg, all blkaddr after .next_blkoff should be unused. 4784c854f4d6SChao Yu */ 4785d0b9e42aSChao Yu for (i = 0; i < NR_PERSISTENT_LOG; i++) { 4786c854f4d6SChao Yu struct curseg_info *curseg = CURSEG_I(sbi, i); 4787c854f4d6SChao Yu struct seg_entry *se = get_seg_entry(sbi, curseg->segno); 4788c854f4d6SChao Yu unsigned int blkofs = curseg->next_blkoff; 4789c854f4d6SChao Yu 4790a7d9fe3cSJaegeuk Kim if (f2fs_sb_has_readonly(sbi) && 4791a7d9fe3cSJaegeuk Kim i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE) 4792a7d9fe3cSJaegeuk Kim continue; 4793a7d9fe3cSJaegeuk Kim 4794093749e2SChao Yu sanity_check_seg_type(sbi, curseg->seg_type); 4795093749e2SChao Yu 4796c854f4d6SChao Yu if (f2fs_test_bit(blkofs, se->cur_valid_map)) 4797c854f4d6SChao Yu goto out; 4798c854f4d6SChao Yu 4799c854f4d6SChao Yu if (curseg->alloc_type == SSR) 4800c854f4d6SChao Yu continue; 4801c854f4d6SChao Yu 4802c854f4d6SChao Yu for (blkofs += 1; blkofs < sbi->blocks_per_seg; blkofs++) { 4803c854f4d6SChao Yu if (!f2fs_test_bit(blkofs, se->cur_valid_map)) 4804c854f4d6SChao Yu continue; 4805c854f4d6SChao Yu out: 4806dcbb4c10SJoe Perches f2fs_err(sbi, 4807dcbb4c10SJoe Perches "Current segment's next free block offset is inconsistent with bitmap, logtype:%u, segno:%u, type:%u, next_blkoff:%u, blkofs:%u", 4808c854f4d6SChao Yu i, curseg->segno, curseg->alloc_type, 4809c854f4d6SChao Yu curseg->next_blkoff, blkofs); 481010f966bbSChao Yu return -EFSCORRUPTED; 4811c854f4d6SChao Yu } 4812c854f4d6SChao Yu } 4813c854f4d6SChao Yu return 0; 4814c854f4d6SChao Yu } 4815c854f4d6SChao Yu 4816c426d991SShin'ichiro Kawasaki #ifdef CONFIG_BLK_DEV_ZONED 4817c426d991SShin'ichiro Kawasaki 4818d508c94eSShin'ichiro Kawasaki static int check_zone_write_pointer(struct f2fs_sb_info *sbi, 4819d508c94eSShin'ichiro Kawasaki struct f2fs_dev_info *fdev, 4820d508c94eSShin'ichiro Kawasaki struct blk_zone *zone) 4821d508c94eSShin'ichiro Kawasaki { 4822d508c94eSShin'ichiro Kawasaki unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno; 4823d508c94eSShin'ichiro Kawasaki block_t zone_block, wp_block, last_valid_block; 4824d508c94eSShin'ichiro Kawasaki unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; 4825d508c94eSShin'ichiro Kawasaki int i, s, b, ret; 4826d508c94eSShin'ichiro Kawasaki struct seg_entry *se; 4827d508c94eSShin'ichiro Kawasaki 4828d508c94eSShin'ichiro Kawasaki if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ) 4829d508c94eSShin'ichiro Kawasaki return 0; 4830d508c94eSShin'ichiro Kawasaki 4831d508c94eSShin'ichiro Kawasaki wp_block = fdev->start_blk + (zone->wp >> log_sectors_per_block); 4832d508c94eSShin'ichiro Kawasaki wp_segno = GET_SEGNO(sbi, wp_block); 4833d508c94eSShin'ichiro Kawasaki wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); 4834d508c94eSShin'ichiro Kawasaki zone_block = fdev->start_blk + (zone->start >> log_sectors_per_block); 4835d508c94eSShin'ichiro Kawasaki zone_segno = GET_SEGNO(sbi, zone_block); 4836d508c94eSShin'ichiro Kawasaki zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno); 4837d508c94eSShin'ichiro Kawasaki 4838d508c94eSShin'ichiro Kawasaki if (zone_segno >= MAIN_SEGS(sbi)) 4839d508c94eSShin'ichiro Kawasaki return 0; 4840d508c94eSShin'ichiro Kawasaki 4841d508c94eSShin'ichiro Kawasaki /* 4842d508c94eSShin'ichiro Kawasaki * Skip check of zones cursegs point to, since 4843d508c94eSShin'ichiro Kawasaki * fix_curseg_write_pointer() checks them. 4844d508c94eSShin'ichiro Kawasaki */ 4845d508c94eSShin'ichiro Kawasaki for (i = 0; i < NO_CHECK_TYPE; i++) 4846d508c94eSShin'ichiro Kawasaki if (zone_secno == GET_SEC_FROM_SEG(sbi, 4847d508c94eSShin'ichiro Kawasaki CURSEG_I(sbi, i)->segno)) 4848d508c94eSShin'ichiro Kawasaki return 0; 4849d508c94eSShin'ichiro Kawasaki 4850d508c94eSShin'ichiro Kawasaki /* 4851d508c94eSShin'ichiro Kawasaki * Get last valid block of the zone. 4852d508c94eSShin'ichiro Kawasaki */ 4853d508c94eSShin'ichiro Kawasaki last_valid_block = zone_block - 1; 4854d508c94eSShin'ichiro Kawasaki for (s = sbi->segs_per_sec - 1; s >= 0; s--) { 4855d508c94eSShin'ichiro Kawasaki segno = zone_segno + s; 4856d508c94eSShin'ichiro Kawasaki se = get_seg_entry(sbi, segno); 4857d508c94eSShin'ichiro Kawasaki for (b = sbi->blocks_per_seg - 1; b >= 0; b--) 4858d508c94eSShin'ichiro Kawasaki if (f2fs_test_bit(b, se->cur_valid_map)) { 4859d508c94eSShin'ichiro Kawasaki last_valid_block = START_BLOCK(sbi, segno) + b; 4860d508c94eSShin'ichiro Kawasaki break; 4861d508c94eSShin'ichiro Kawasaki } 4862d508c94eSShin'ichiro Kawasaki if (last_valid_block >= zone_block) 4863d508c94eSShin'ichiro Kawasaki break; 4864d508c94eSShin'ichiro Kawasaki } 4865d508c94eSShin'ichiro Kawasaki 4866d508c94eSShin'ichiro Kawasaki /* 4867d508c94eSShin'ichiro Kawasaki * If last valid block is beyond the write pointer, report the 4868d508c94eSShin'ichiro Kawasaki * inconsistency. This inconsistency does not cause write error 4869d508c94eSShin'ichiro Kawasaki * because the zone will not be selected for write operation until 4870d508c94eSShin'ichiro Kawasaki * it get discarded. Just report it. 4871d508c94eSShin'ichiro Kawasaki */ 4872d508c94eSShin'ichiro Kawasaki if (last_valid_block >= wp_block) { 4873d508c94eSShin'ichiro Kawasaki f2fs_notice(sbi, "Valid block beyond write pointer: " 4874d508c94eSShin'ichiro Kawasaki "valid block[0x%x,0x%x] wp[0x%x,0x%x]", 4875d508c94eSShin'ichiro Kawasaki GET_SEGNO(sbi, last_valid_block), 4876d508c94eSShin'ichiro Kawasaki GET_BLKOFF_FROM_SEG0(sbi, last_valid_block), 4877d508c94eSShin'ichiro Kawasaki wp_segno, wp_blkoff); 4878d508c94eSShin'ichiro Kawasaki return 0; 4879d508c94eSShin'ichiro Kawasaki } 4880d508c94eSShin'ichiro Kawasaki 4881d508c94eSShin'ichiro Kawasaki /* 4882d508c94eSShin'ichiro Kawasaki * If there is no valid block in the zone and if write pointer is 4883d508c94eSShin'ichiro Kawasaki * not at zone start, reset the write pointer. 4884d508c94eSShin'ichiro Kawasaki */ 4885d508c94eSShin'ichiro Kawasaki if (last_valid_block + 1 == zone_block && zone->wp != zone->start) { 4886d508c94eSShin'ichiro Kawasaki f2fs_notice(sbi, 4887d508c94eSShin'ichiro Kawasaki "Zone without valid block has non-zero write " 4888d508c94eSShin'ichiro Kawasaki "pointer. Reset the write pointer: wp[0x%x,0x%x]", 4889d508c94eSShin'ichiro Kawasaki wp_segno, wp_blkoff); 4890d508c94eSShin'ichiro Kawasaki ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block, 4891d508c94eSShin'ichiro Kawasaki zone->len >> log_sectors_per_block); 4892d508c94eSShin'ichiro Kawasaki if (ret) { 4893d508c94eSShin'ichiro Kawasaki f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", 4894d508c94eSShin'ichiro Kawasaki fdev->path, ret); 4895d508c94eSShin'ichiro Kawasaki return ret; 4896d508c94eSShin'ichiro Kawasaki } 4897d508c94eSShin'ichiro Kawasaki } 4898d508c94eSShin'ichiro Kawasaki 4899d508c94eSShin'ichiro Kawasaki return 0; 4900d508c94eSShin'ichiro Kawasaki } 4901d508c94eSShin'ichiro Kawasaki 4902c426d991SShin'ichiro Kawasaki static struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi, 4903c426d991SShin'ichiro Kawasaki block_t zone_blkaddr) 4904c426d991SShin'ichiro Kawasaki { 4905c426d991SShin'ichiro Kawasaki int i; 4906c426d991SShin'ichiro Kawasaki 4907c426d991SShin'ichiro Kawasaki for (i = 0; i < sbi->s_ndevs; i++) { 4908c426d991SShin'ichiro Kawasaki if (!bdev_is_zoned(FDEV(i).bdev)) 4909c426d991SShin'ichiro Kawasaki continue; 4910c426d991SShin'ichiro Kawasaki if (sbi->s_ndevs == 1 || (FDEV(i).start_blk <= zone_blkaddr && 4911c426d991SShin'ichiro Kawasaki zone_blkaddr <= FDEV(i).end_blk)) 4912c426d991SShin'ichiro Kawasaki return &FDEV(i); 4913c426d991SShin'ichiro Kawasaki } 4914c426d991SShin'ichiro Kawasaki 4915c426d991SShin'ichiro Kawasaki return NULL; 4916c426d991SShin'ichiro Kawasaki } 4917c426d991SShin'ichiro Kawasaki 4918c426d991SShin'ichiro Kawasaki static int report_one_zone_cb(struct blk_zone *zone, unsigned int idx, 49195f029c04SYi Zhuang void *data) 49205f029c04SYi Zhuang { 4921c426d991SShin'ichiro Kawasaki memcpy(data, zone, sizeof(struct blk_zone)); 4922c426d991SShin'ichiro Kawasaki return 0; 4923c426d991SShin'ichiro Kawasaki } 4924c426d991SShin'ichiro Kawasaki 4925c426d991SShin'ichiro Kawasaki static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) 4926c426d991SShin'ichiro Kawasaki { 4927c426d991SShin'ichiro Kawasaki struct curseg_info *cs = CURSEG_I(sbi, type); 4928c426d991SShin'ichiro Kawasaki struct f2fs_dev_info *zbd; 4929c426d991SShin'ichiro Kawasaki struct blk_zone zone; 4930c426d991SShin'ichiro Kawasaki unsigned int cs_section, wp_segno, wp_blkoff, wp_sector_off; 4931c426d991SShin'ichiro Kawasaki block_t cs_zone_block, wp_block; 4932c426d991SShin'ichiro Kawasaki unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; 4933c426d991SShin'ichiro Kawasaki sector_t zone_sector; 4934c426d991SShin'ichiro Kawasaki int err; 4935c426d991SShin'ichiro Kawasaki 4936c426d991SShin'ichiro Kawasaki cs_section = GET_SEC_FROM_SEG(sbi, cs->segno); 4937c426d991SShin'ichiro Kawasaki cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section)); 4938c426d991SShin'ichiro Kawasaki 4939c426d991SShin'ichiro Kawasaki zbd = get_target_zoned_dev(sbi, cs_zone_block); 4940c426d991SShin'ichiro Kawasaki if (!zbd) 4941c426d991SShin'ichiro Kawasaki return 0; 4942c426d991SShin'ichiro Kawasaki 4943c426d991SShin'ichiro Kawasaki /* report zone for the sector the curseg points to */ 4944c426d991SShin'ichiro Kawasaki zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) 4945c426d991SShin'ichiro Kawasaki << log_sectors_per_block; 4946c426d991SShin'ichiro Kawasaki err = blkdev_report_zones(zbd->bdev, zone_sector, 1, 4947c426d991SShin'ichiro Kawasaki report_one_zone_cb, &zone); 4948c426d991SShin'ichiro Kawasaki if (err != 1) { 4949c426d991SShin'ichiro Kawasaki f2fs_err(sbi, "Report zone failed: %s errno=(%d)", 4950c426d991SShin'ichiro Kawasaki zbd->path, err); 4951c426d991SShin'ichiro Kawasaki return err; 4952c426d991SShin'ichiro Kawasaki } 4953c426d991SShin'ichiro Kawasaki 4954c426d991SShin'ichiro Kawasaki if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) 4955c426d991SShin'ichiro Kawasaki return 0; 4956c426d991SShin'ichiro Kawasaki 4957c426d991SShin'ichiro Kawasaki wp_block = zbd->start_blk + (zone.wp >> log_sectors_per_block); 4958c426d991SShin'ichiro Kawasaki wp_segno = GET_SEGNO(sbi, wp_block); 4959c426d991SShin'ichiro Kawasaki wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); 4960c426d991SShin'ichiro Kawasaki wp_sector_off = zone.wp & GENMASK(log_sectors_per_block - 1, 0); 4961c426d991SShin'ichiro Kawasaki 4962c426d991SShin'ichiro Kawasaki if (cs->segno == wp_segno && cs->next_blkoff == wp_blkoff && 4963c426d991SShin'ichiro Kawasaki wp_sector_off == 0) 4964c426d991SShin'ichiro Kawasaki return 0; 4965c426d991SShin'ichiro Kawasaki 4966c426d991SShin'ichiro Kawasaki f2fs_notice(sbi, "Unaligned curseg[%d] with write pointer: " 4967c426d991SShin'ichiro Kawasaki "curseg[0x%x,0x%x] wp[0x%x,0x%x]", 4968c426d991SShin'ichiro Kawasaki type, cs->segno, cs->next_blkoff, wp_segno, wp_blkoff); 4969c426d991SShin'ichiro Kawasaki 4970c426d991SShin'ichiro Kawasaki f2fs_notice(sbi, "Assign new section to curseg[%d]: " 4971c426d991SShin'ichiro Kawasaki "curseg[0x%x,0x%x]", type, cs->segno, cs->next_blkoff); 4972509f1010SChao Yu 4973509f1010SChao Yu f2fs_allocate_new_section(sbi, type, true); 4974c426d991SShin'ichiro Kawasaki 4975d508c94eSShin'ichiro Kawasaki /* check consistency of the zone curseg pointed to */ 4976d508c94eSShin'ichiro Kawasaki if (check_zone_write_pointer(sbi, zbd, &zone)) 4977d508c94eSShin'ichiro Kawasaki return -EIO; 4978d508c94eSShin'ichiro Kawasaki 4979c426d991SShin'ichiro Kawasaki /* check newly assigned zone */ 4980c426d991SShin'ichiro Kawasaki cs_section = GET_SEC_FROM_SEG(sbi, cs->segno); 4981c426d991SShin'ichiro Kawasaki cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section)); 4982c426d991SShin'ichiro Kawasaki 4983c426d991SShin'ichiro Kawasaki zbd = get_target_zoned_dev(sbi, cs_zone_block); 4984c426d991SShin'ichiro Kawasaki if (!zbd) 4985c426d991SShin'ichiro Kawasaki return 0; 4986c426d991SShin'ichiro Kawasaki 4987c426d991SShin'ichiro Kawasaki zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) 4988c426d991SShin'ichiro Kawasaki << log_sectors_per_block; 4989c426d991SShin'ichiro Kawasaki err = blkdev_report_zones(zbd->bdev, zone_sector, 1, 4990c426d991SShin'ichiro Kawasaki report_one_zone_cb, &zone); 4991c426d991SShin'ichiro Kawasaki if (err != 1) { 4992c426d991SShin'ichiro Kawasaki f2fs_err(sbi, "Report zone failed: %s errno=(%d)", 4993c426d991SShin'ichiro Kawasaki zbd->path, err); 4994c426d991SShin'ichiro Kawasaki return err; 4995c426d991SShin'ichiro Kawasaki } 4996c426d991SShin'ichiro Kawasaki 4997c426d991SShin'ichiro Kawasaki if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) 4998c426d991SShin'ichiro Kawasaki return 0; 4999c426d991SShin'ichiro Kawasaki 5000c426d991SShin'ichiro Kawasaki if (zone.wp != zone.start) { 5001c426d991SShin'ichiro Kawasaki f2fs_notice(sbi, 5002c426d991SShin'ichiro Kawasaki "New zone for curseg[%d] is not yet discarded. " 5003c426d991SShin'ichiro Kawasaki "Reset the zone: curseg[0x%x,0x%x]", 5004c426d991SShin'ichiro Kawasaki type, cs->segno, cs->next_blkoff); 5005c426d991SShin'ichiro Kawasaki err = __f2fs_issue_discard_zone(sbi, zbd->bdev, 5006c426d991SShin'ichiro Kawasaki zone_sector >> log_sectors_per_block, 5007c426d991SShin'ichiro Kawasaki zone.len >> log_sectors_per_block); 5008c426d991SShin'ichiro Kawasaki if (err) { 5009c426d991SShin'ichiro Kawasaki f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", 5010c426d991SShin'ichiro Kawasaki zbd->path, err); 5011c426d991SShin'ichiro Kawasaki return err; 5012c426d991SShin'ichiro Kawasaki } 5013c426d991SShin'ichiro Kawasaki } 5014c426d991SShin'ichiro Kawasaki 5015c426d991SShin'ichiro Kawasaki return 0; 5016c426d991SShin'ichiro Kawasaki } 5017c426d991SShin'ichiro Kawasaki 5018c426d991SShin'ichiro Kawasaki int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) 5019c426d991SShin'ichiro Kawasaki { 5020c426d991SShin'ichiro Kawasaki int i, ret; 5021c426d991SShin'ichiro Kawasaki 5022d0b9e42aSChao Yu for (i = 0; i < NR_PERSISTENT_LOG; i++) { 5023c426d991SShin'ichiro Kawasaki ret = fix_curseg_write_pointer(sbi, i); 5024c426d991SShin'ichiro Kawasaki if (ret) 5025c426d991SShin'ichiro Kawasaki return ret; 5026c426d991SShin'ichiro Kawasaki } 5027c426d991SShin'ichiro Kawasaki 5028c426d991SShin'ichiro Kawasaki return 0; 5029c426d991SShin'ichiro Kawasaki } 5030d508c94eSShin'ichiro Kawasaki 5031d508c94eSShin'ichiro Kawasaki struct check_zone_write_pointer_args { 5032d508c94eSShin'ichiro Kawasaki struct f2fs_sb_info *sbi; 5033d508c94eSShin'ichiro Kawasaki struct f2fs_dev_info *fdev; 5034d508c94eSShin'ichiro Kawasaki }; 5035d508c94eSShin'ichiro Kawasaki 5036d508c94eSShin'ichiro Kawasaki static int check_zone_write_pointer_cb(struct blk_zone *zone, unsigned int idx, 50375f029c04SYi Zhuang void *data) 50385f029c04SYi Zhuang { 5039d508c94eSShin'ichiro Kawasaki struct check_zone_write_pointer_args *args; 50405f029c04SYi Zhuang 5041d508c94eSShin'ichiro Kawasaki args = (struct check_zone_write_pointer_args *)data; 5042d508c94eSShin'ichiro Kawasaki 5043d508c94eSShin'ichiro Kawasaki return check_zone_write_pointer(args->sbi, args->fdev, zone); 5044d508c94eSShin'ichiro Kawasaki } 5045d508c94eSShin'ichiro Kawasaki 5046d508c94eSShin'ichiro Kawasaki int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) 5047d508c94eSShin'ichiro Kawasaki { 5048d508c94eSShin'ichiro Kawasaki int i, ret; 5049d508c94eSShin'ichiro Kawasaki struct check_zone_write_pointer_args args; 5050d508c94eSShin'ichiro Kawasaki 5051d508c94eSShin'ichiro Kawasaki for (i = 0; i < sbi->s_ndevs; i++) { 5052d508c94eSShin'ichiro Kawasaki if (!bdev_is_zoned(FDEV(i).bdev)) 5053d508c94eSShin'ichiro Kawasaki continue; 5054d508c94eSShin'ichiro Kawasaki 5055d508c94eSShin'ichiro Kawasaki args.sbi = sbi; 5056d508c94eSShin'ichiro Kawasaki args.fdev = &FDEV(i); 5057d508c94eSShin'ichiro Kawasaki ret = blkdev_report_zones(FDEV(i).bdev, 0, BLK_ALL_ZONES, 5058d508c94eSShin'ichiro Kawasaki check_zone_write_pointer_cb, &args); 5059d508c94eSShin'ichiro Kawasaki if (ret < 0) 5060d508c94eSShin'ichiro Kawasaki return ret; 5061d508c94eSShin'ichiro Kawasaki } 5062d508c94eSShin'ichiro Kawasaki 5063d508c94eSShin'ichiro Kawasaki return 0; 5064d508c94eSShin'ichiro Kawasaki } 5065de881df9SAravind Ramesh 5066de881df9SAravind Ramesh static bool is_conv_zone(struct f2fs_sb_info *sbi, unsigned int zone_idx, 5067de881df9SAravind Ramesh unsigned int dev_idx) 5068de881df9SAravind Ramesh { 5069de881df9SAravind Ramesh if (!bdev_is_zoned(FDEV(dev_idx).bdev)) 5070de881df9SAravind Ramesh return true; 5071de881df9SAravind Ramesh return !test_bit(zone_idx, FDEV(dev_idx).blkz_seq); 5072de881df9SAravind Ramesh } 5073de881df9SAravind Ramesh 5074de881df9SAravind Ramesh /* Return the zone index in the given device */ 5075de881df9SAravind Ramesh static unsigned int get_zone_idx(struct f2fs_sb_info *sbi, unsigned int secno, 5076de881df9SAravind Ramesh int dev_idx) 5077de881df9SAravind Ramesh { 5078de881df9SAravind Ramesh block_t sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno)); 5079de881df9SAravind Ramesh 5080de881df9SAravind Ramesh return (sec_start_blkaddr - FDEV(dev_idx).start_blk) >> 5081de881df9SAravind Ramesh sbi->log_blocks_per_blkz; 5082de881df9SAravind Ramesh } 5083de881df9SAravind Ramesh 5084de881df9SAravind Ramesh /* 5085de881df9SAravind Ramesh * Return the usable segments in a section based on the zone's 5086de881df9SAravind Ramesh * corresponding zone capacity. Zone is equal to a section. 5087de881df9SAravind Ramesh */ 5088de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_segs_in_sec( 5089de881df9SAravind Ramesh struct f2fs_sb_info *sbi, unsigned int segno) 5090de881df9SAravind Ramesh { 5091de881df9SAravind Ramesh unsigned int dev_idx, zone_idx, unusable_segs_in_sec; 5092de881df9SAravind Ramesh 5093de881df9SAravind Ramesh dev_idx = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno)); 5094de881df9SAravind Ramesh zone_idx = get_zone_idx(sbi, GET_SEC_FROM_SEG(sbi, segno), dev_idx); 5095de881df9SAravind Ramesh 5096de881df9SAravind Ramesh /* Conventional zone's capacity is always equal to zone size */ 5097de881df9SAravind Ramesh if (is_conv_zone(sbi, zone_idx, dev_idx)) 5098de881df9SAravind Ramesh return sbi->segs_per_sec; 5099de881df9SAravind Ramesh 5100de881df9SAravind Ramesh /* 5101de881df9SAravind Ramesh * If the zone_capacity_blocks array is NULL, then zone capacity 5102de881df9SAravind Ramesh * is equal to the zone size for all zones 5103de881df9SAravind Ramesh */ 5104de881df9SAravind Ramesh if (!FDEV(dev_idx).zone_capacity_blocks) 5105de881df9SAravind Ramesh return sbi->segs_per_sec; 5106de881df9SAravind Ramesh 5107de881df9SAravind Ramesh /* Get the segment count beyond zone capacity block */ 5108de881df9SAravind Ramesh unusable_segs_in_sec = (sbi->blocks_per_blkz - 5109de881df9SAravind Ramesh FDEV(dev_idx).zone_capacity_blocks[zone_idx]) >> 5110de881df9SAravind Ramesh sbi->log_blocks_per_seg; 5111de881df9SAravind Ramesh return sbi->segs_per_sec - unusable_segs_in_sec; 5112de881df9SAravind Ramesh } 5113de881df9SAravind Ramesh 5114de881df9SAravind Ramesh /* 5115de881df9SAravind Ramesh * Return the number of usable blocks in a segment. The number of blocks 5116de881df9SAravind Ramesh * returned is always equal to the number of blocks in a segment for 5117de881df9SAravind Ramesh * segments fully contained within a sequential zone capacity or a 5118de881df9SAravind Ramesh * conventional zone. For segments partially contained in a sequential 5119de881df9SAravind Ramesh * zone capacity, the number of usable blocks up to the zone capacity 5120de881df9SAravind Ramesh * is returned. 0 is returned in all other cases. 5121de881df9SAravind Ramesh */ 5122de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_blks_in_seg( 5123de881df9SAravind Ramesh struct f2fs_sb_info *sbi, unsigned int segno) 5124de881df9SAravind Ramesh { 5125de881df9SAravind Ramesh block_t seg_start, sec_start_blkaddr, sec_cap_blkaddr; 5126de881df9SAravind Ramesh unsigned int zone_idx, dev_idx, secno; 5127de881df9SAravind Ramesh 5128de881df9SAravind Ramesh secno = GET_SEC_FROM_SEG(sbi, segno); 5129de881df9SAravind Ramesh seg_start = START_BLOCK(sbi, segno); 5130de881df9SAravind Ramesh dev_idx = f2fs_target_device_index(sbi, seg_start); 5131de881df9SAravind Ramesh zone_idx = get_zone_idx(sbi, secno, dev_idx); 5132de881df9SAravind Ramesh 5133de881df9SAravind Ramesh /* 5134de881df9SAravind Ramesh * Conventional zone's capacity is always equal to zone size, 5135de881df9SAravind Ramesh * so, blocks per segment is unchanged. 5136de881df9SAravind Ramesh */ 5137de881df9SAravind Ramesh if (is_conv_zone(sbi, zone_idx, dev_idx)) 5138de881df9SAravind Ramesh return sbi->blocks_per_seg; 5139de881df9SAravind Ramesh 5140de881df9SAravind Ramesh if (!FDEV(dev_idx).zone_capacity_blocks) 5141de881df9SAravind Ramesh return sbi->blocks_per_seg; 5142de881df9SAravind Ramesh 5143de881df9SAravind Ramesh sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno)); 5144de881df9SAravind Ramesh sec_cap_blkaddr = sec_start_blkaddr + 5145de881df9SAravind Ramesh FDEV(dev_idx).zone_capacity_blocks[zone_idx]; 5146de881df9SAravind Ramesh 5147de881df9SAravind Ramesh /* 5148de881df9SAravind Ramesh * If segment starts before zone capacity and spans beyond 5149de881df9SAravind Ramesh * zone capacity, then usable blocks are from seg start to 5150de881df9SAravind Ramesh * zone capacity. If the segment starts after the zone capacity, 5151de881df9SAravind Ramesh * then there are no usable blocks. 5152de881df9SAravind Ramesh */ 5153de881df9SAravind Ramesh if (seg_start >= sec_cap_blkaddr) 5154de881df9SAravind Ramesh return 0; 5155de881df9SAravind Ramesh if (seg_start + sbi->blocks_per_seg > sec_cap_blkaddr) 5156de881df9SAravind Ramesh return sec_cap_blkaddr - seg_start; 5157de881df9SAravind Ramesh 5158de881df9SAravind Ramesh return sbi->blocks_per_seg; 5159de881df9SAravind Ramesh } 5160c426d991SShin'ichiro Kawasaki #else 5161c426d991SShin'ichiro Kawasaki int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) 5162c426d991SShin'ichiro Kawasaki { 5163c426d991SShin'ichiro Kawasaki return 0; 5164c426d991SShin'ichiro Kawasaki } 5165d508c94eSShin'ichiro Kawasaki 5166d508c94eSShin'ichiro Kawasaki int f2fs_check_write_pointer(struct f2fs_sb_info *sbi) 5167d508c94eSShin'ichiro Kawasaki { 5168d508c94eSShin'ichiro Kawasaki return 0; 5169d508c94eSShin'ichiro Kawasaki } 5170de881df9SAravind Ramesh 5171de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_blks_in_seg(struct f2fs_sb_info *sbi, 5172de881df9SAravind Ramesh unsigned int segno) 5173de881df9SAravind Ramesh { 5174de881df9SAravind Ramesh return 0; 5175de881df9SAravind Ramesh } 5176de881df9SAravind Ramesh 5177de881df9SAravind Ramesh static inline unsigned int f2fs_usable_zone_segs_in_sec(struct f2fs_sb_info *sbi, 5178de881df9SAravind Ramesh unsigned int segno) 5179de881df9SAravind Ramesh { 5180de881df9SAravind Ramesh return 0; 5181de881df9SAravind Ramesh } 5182c426d991SShin'ichiro Kawasaki #endif 5183de881df9SAravind Ramesh unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi, 5184de881df9SAravind Ramesh unsigned int segno) 5185de881df9SAravind Ramesh { 5186de881df9SAravind Ramesh if (f2fs_sb_has_blkzoned(sbi)) 5187de881df9SAravind Ramesh return f2fs_usable_zone_blks_in_seg(sbi, segno); 5188de881df9SAravind Ramesh 5189de881df9SAravind Ramesh return sbi->blocks_per_seg; 5190de881df9SAravind Ramesh } 5191de881df9SAravind Ramesh 5192de881df9SAravind Ramesh unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi, 5193de881df9SAravind Ramesh unsigned int segno) 5194de881df9SAravind Ramesh { 5195de881df9SAravind Ramesh if (f2fs_sb_has_blkzoned(sbi)) 5196de881df9SAravind Ramesh return f2fs_usable_zone_segs_in_sec(sbi, segno); 5197de881df9SAravind Ramesh 5198de881df9SAravind Ramesh return sbi->segs_per_sec; 5199de881df9SAravind Ramesh } 5200c426d991SShin'ichiro Kawasaki 52010a8165d7SJaegeuk Kim /* 5202351df4b2SJaegeuk Kim * Update min, max modified time for cost-benefit GC algorithm 5203351df4b2SJaegeuk Kim */ 5204351df4b2SJaegeuk Kim static void init_min_max_mtime(struct f2fs_sb_info *sbi) 5205351df4b2SJaegeuk Kim { 5206351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 5207351df4b2SJaegeuk Kim unsigned int segno; 5208351df4b2SJaegeuk Kim 52093d26fa6bSChao Yu down_write(&sit_i->sentry_lock); 5210351df4b2SJaegeuk Kim 52115ad25442SChao Yu sit_i->min_mtime = ULLONG_MAX; 5212351df4b2SJaegeuk Kim 52137cd8558bSJaegeuk Kim for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { 5214351df4b2SJaegeuk Kim unsigned int i; 5215351df4b2SJaegeuk Kim unsigned long long mtime = 0; 5216351df4b2SJaegeuk Kim 5217351df4b2SJaegeuk Kim for (i = 0; i < sbi->segs_per_sec; i++) 5218351df4b2SJaegeuk Kim mtime += get_seg_entry(sbi, segno + i)->mtime; 5219351df4b2SJaegeuk Kim 5220351df4b2SJaegeuk Kim mtime = div_u64(mtime, sbi->segs_per_sec); 5221351df4b2SJaegeuk Kim 5222351df4b2SJaegeuk Kim if (sit_i->min_mtime > mtime) 5223351df4b2SJaegeuk Kim sit_i->min_mtime = mtime; 5224351df4b2SJaegeuk Kim } 5225a1f72ac2SChao Yu sit_i->max_mtime = get_mtime(sbi, false); 5226093749e2SChao Yu sit_i->dirty_max_mtime = 0; 52273d26fa6bSChao Yu up_write(&sit_i->sentry_lock); 5228351df4b2SJaegeuk Kim } 5229351df4b2SJaegeuk Kim 52304d57b86dSChao Yu int f2fs_build_segment_manager(struct f2fs_sb_info *sbi) 5231351df4b2SJaegeuk Kim { 5232351df4b2SJaegeuk Kim struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); 5233351df4b2SJaegeuk Kim struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 52341042d60fSNamjae Jeon struct f2fs_sm_info *sm_info; 5235351df4b2SJaegeuk Kim int err; 5236351df4b2SJaegeuk Kim 5237acbf054dSChao Yu sm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_sm_info), GFP_KERNEL); 5238351df4b2SJaegeuk Kim if (!sm_info) 5239351df4b2SJaegeuk Kim return -ENOMEM; 5240351df4b2SJaegeuk Kim 5241351df4b2SJaegeuk Kim /* init sm info */ 5242351df4b2SJaegeuk Kim sbi->sm_info = sm_info; 5243351df4b2SJaegeuk Kim sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); 5244351df4b2SJaegeuk Kim sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); 5245351df4b2SJaegeuk Kim sm_info->segment_count = le32_to_cpu(raw_super->segment_count); 5246351df4b2SJaegeuk Kim sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); 5247351df4b2SJaegeuk Kim sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); 5248351df4b2SJaegeuk Kim sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main); 5249351df4b2SJaegeuk Kim sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); 525058c41035SJaegeuk Kim sm_info->rec_prefree_segments = sm_info->main_segments * 525158c41035SJaegeuk Kim DEF_RECLAIM_PREFREE_SEGMENTS / 100; 525244a83499SJaegeuk Kim if (sm_info->rec_prefree_segments > DEF_MAX_RECLAIM_PREFREE_SEGMENTS) 525344a83499SJaegeuk Kim sm_info->rec_prefree_segments = DEF_MAX_RECLAIM_PREFREE_SEGMENTS; 525444a83499SJaegeuk Kim 5255b0332a0fSChao Yu if (!f2fs_lfs_mode(sbi)) 52569b5f136fSJaegeuk Kim sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC; 5257216fbd64SJaegeuk Kim sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; 5258c1ce1b02SJaegeuk Kim sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; 5259dc675a97SLaibin Qiu sm_info->min_seq_blocks = sbi->blocks_per_seg; 5260ef095d19SJaegeuk Kim sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; 5261a2a12b67SChao Yu sm_info->min_ssr_sections = reserved_sections(sbi); 5262351df4b2SJaegeuk Kim 5263184a5cd2SChao Yu INIT_LIST_HEAD(&sm_info->sit_entry_set); 5264184a5cd2SChao Yu 5265e4544b63STim Murray init_f2fs_rwsem(&sm_info->curseg_lock); 52662b60311dSChao Yu 5267d4fdf8baSYunlei He if (!f2fs_readonly(sbi->sb)) { 52684d57b86dSChao Yu err = f2fs_create_flush_cmd_control(sbi); 52692163d198SGu Zheng if (err) 5270a688b9d9SGu Zheng return err; 5271a688b9d9SGu Zheng } 52726b4afdd7SJaegeuk Kim 52730b54fb84SJaegeuk Kim err = create_discard_cmd_control(sbi); 52740b54fb84SJaegeuk Kim if (err) 52750b54fb84SJaegeuk Kim return err; 52760b54fb84SJaegeuk Kim 5277351df4b2SJaegeuk Kim err = build_sit_info(sbi); 5278351df4b2SJaegeuk Kim if (err) 5279351df4b2SJaegeuk Kim return err; 5280351df4b2SJaegeuk Kim err = build_free_segmap(sbi); 5281351df4b2SJaegeuk Kim if (err) 5282351df4b2SJaegeuk Kim return err; 5283351df4b2SJaegeuk Kim err = build_curseg(sbi); 5284351df4b2SJaegeuk Kim if (err) 5285351df4b2SJaegeuk Kim return err; 5286351df4b2SJaegeuk Kim 5287351df4b2SJaegeuk Kim /* reinit free segmap based on SIT */ 5288c39a1b34SJaegeuk Kim err = build_sit_entries(sbi); 5289c39a1b34SJaegeuk Kim if (err) 5290c39a1b34SJaegeuk Kim return err; 5291351df4b2SJaegeuk Kim 5292351df4b2SJaegeuk Kim init_free_segmap(sbi); 5293351df4b2SJaegeuk Kim err = build_dirty_segmap(sbi); 5294351df4b2SJaegeuk Kim if (err) 5295351df4b2SJaegeuk Kim return err; 5296351df4b2SJaegeuk Kim 5297c854f4d6SChao Yu err = sanity_check_curseg(sbi); 5298c854f4d6SChao Yu if (err) 5299c854f4d6SChao Yu return err; 5300c854f4d6SChao Yu 5301351df4b2SJaegeuk Kim init_min_max_mtime(sbi); 5302351df4b2SJaegeuk Kim return 0; 5303351df4b2SJaegeuk Kim } 5304351df4b2SJaegeuk Kim 5305351df4b2SJaegeuk Kim static void discard_dirty_segmap(struct f2fs_sb_info *sbi, 5306351df4b2SJaegeuk Kim enum dirty_type dirty_type) 5307351df4b2SJaegeuk Kim { 5308351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 5309351df4b2SJaegeuk Kim 5310351df4b2SJaegeuk Kim mutex_lock(&dirty_i->seglist_lock); 531139307a8eSJaegeuk Kim kvfree(dirty_i->dirty_segmap[dirty_type]); 5312351df4b2SJaegeuk Kim dirty_i->nr_dirty[dirty_type] = 0; 5313351df4b2SJaegeuk Kim mutex_unlock(&dirty_i->seglist_lock); 5314351df4b2SJaegeuk Kim } 5315351df4b2SJaegeuk Kim 53165ec4e49fSJaegeuk Kim static void destroy_victim_secmap(struct f2fs_sb_info *sbi) 5317351df4b2SJaegeuk Kim { 5318351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 53195f029c04SYi Zhuang 532039307a8eSJaegeuk Kim kvfree(dirty_i->victim_secmap); 5321351df4b2SJaegeuk Kim } 5322351df4b2SJaegeuk Kim 5323351df4b2SJaegeuk Kim static void destroy_dirty_segmap(struct f2fs_sb_info *sbi) 5324351df4b2SJaegeuk Kim { 5325351df4b2SJaegeuk Kim struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 5326351df4b2SJaegeuk Kim int i; 5327351df4b2SJaegeuk Kim 5328351df4b2SJaegeuk Kim if (!dirty_i) 5329351df4b2SJaegeuk Kim return; 5330351df4b2SJaegeuk Kim 5331351df4b2SJaegeuk Kim /* discard pre-free/dirty segments list */ 5332351df4b2SJaegeuk Kim for (i = 0; i < NR_DIRTY_TYPE; i++) 5333351df4b2SJaegeuk Kim discard_dirty_segmap(sbi, i); 5334351df4b2SJaegeuk Kim 5335da52f8adSJack Qiu if (__is_large_section(sbi)) { 5336da52f8adSJack Qiu mutex_lock(&dirty_i->seglist_lock); 5337da52f8adSJack Qiu kvfree(dirty_i->dirty_secmap); 5338da52f8adSJack Qiu mutex_unlock(&dirty_i->seglist_lock); 5339da52f8adSJack Qiu } 5340da52f8adSJack Qiu 53415ec4e49fSJaegeuk Kim destroy_victim_secmap(sbi); 5342351df4b2SJaegeuk Kim SM_I(sbi)->dirty_info = NULL; 5343c8eb7024SChao Yu kfree(dirty_i); 5344351df4b2SJaegeuk Kim } 5345351df4b2SJaegeuk Kim 5346351df4b2SJaegeuk Kim static void destroy_curseg(struct f2fs_sb_info *sbi) 5347351df4b2SJaegeuk Kim { 5348351df4b2SJaegeuk Kim struct curseg_info *array = SM_I(sbi)->curseg_array; 5349351df4b2SJaegeuk Kim int i; 5350351df4b2SJaegeuk Kim 5351351df4b2SJaegeuk Kim if (!array) 5352351df4b2SJaegeuk Kim return; 5353351df4b2SJaegeuk Kim SM_I(sbi)->curseg_array = NULL; 5354b7ad7512SChao Yu for (i = 0; i < NR_CURSEG_TYPE; i++) { 5355c8eb7024SChao Yu kfree(array[i].sum_blk); 5356c8eb7024SChao Yu kfree(array[i].journal); 5357b7ad7512SChao Yu } 5358c8eb7024SChao Yu kfree(array); 5359351df4b2SJaegeuk Kim } 5360351df4b2SJaegeuk Kim 5361351df4b2SJaegeuk Kim static void destroy_free_segmap(struct f2fs_sb_info *sbi) 5362351df4b2SJaegeuk Kim { 5363351df4b2SJaegeuk Kim struct free_segmap_info *free_i = SM_I(sbi)->free_info; 53645f029c04SYi Zhuang 5365351df4b2SJaegeuk Kim if (!free_i) 5366351df4b2SJaegeuk Kim return; 5367351df4b2SJaegeuk Kim SM_I(sbi)->free_info = NULL; 536839307a8eSJaegeuk Kim kvfree(free_i->free_segmap); 536939307a8eSJaegeuk Kim kvfree(free_i->free_secmap); 5370c8eb7024SChao Yu kfree(free_i); 5371351df4b2SJaegeuk Kim } 5372351df4b2SJaegeuk Kim 5373351df4b2SJaegeuk Kim static void destroy_sit_info(struct f2fs_sb_info *sbi) 5374351df4b2SJaegeuk Kim { 5375351df4b2SJaegeuk Kim struct sit_info *sit_i = SIT_I(sbi); 5376351df4b2SJaegeuk Kim 5377351df4b2SJaegeuk Kim if (!sit_i) 5378351df4b2SJaegeuk Kim return; 5379351df4b2SJaegeuk Kim 53802fde3dd1SChao Yu if (sit_i->sentries) 53812fde3dd1SChao Yu kvfree(sit_i->bitmap); 5382c8eb7024SChao Yu kfree(sit_i->tmp_map); 538360a3b782SJaegeuk Kim 538439307a8eSJaegeuk Kim kvfree(sit_i->sentries); 538539307a8eSJaegeuk Kim kvfree(sit_i->sec_entries); 538639307a8eSJaegeuk Kim kvfree(sit_i->dirty_sentries_bitmap); 5387351df4b2SJaegeuk Kim 5388351df4b2SJaegeuk Kim SM_I(sbi)->sit_info = NULL; 53895222595dSJaegeuk Kim kvfree(sit_i->sit_bitmap); 5390ae27d62eSChao Yu #ifdef CONFIG_F2FS_CHECK_FS 53915222595dSJaegeuk Kim kvfree(sit_i->sit_bitmap_mir); 5392bbf9f7d9SSahitya Tummala kvfree(sit_i->invalid_segmap); 5393ae27d62eSChao Yu #endif 5394c8eb7024SChao Yu kfree(sit_i); 5395351df4b2SJaegeuk Kim } 5396351df4b2SJaegeuk Kim 53974d57b86dSChao Yu void f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi) 5398351df4b2SJaegeuk Kim { 5399351df4b2SJaegeuk Kim struct f2fs_sm_info *sm_info = SM_I(sbi); 5400a688b9d9SGu Zheng 54013b03f724SChao Yu if (!sm_info) 54023b03f724SChao Yu return; 54034d57b86dSChao Yu f2fs_destroy_flush_cmd_control(sbi, true); 5404f099405fSChao Yu destroy_discard_cmd_control(sbi); 5405351df4b2SJaegeuk Kim destroy_dirty_segmap(sbi); 5406351df4b2SJaegeuk Kim destroy_curseg(sbi); 5407351df4b2SJaegeuk Kim destroy_free_segmap(sbi); 5408351df4b2SJaegeuk Kim destroy_sit_info(sbi); 5409351df4b2SJaegeuk Kim sbi->sm_info = NULL; 5410c8eb7024SChao Yu kfree(sm_info); 5411351df4b2SJaegeuk Kim } 54127fd9e544SJaegeuk Kim 54134d57b86dSChao Yu int __init f2fs_create_segment_manager_caches(void) 54147fd9e544SJaegeuk Kim { 541598510003SChao Yu discard_entry_slab = f2fs_kmem_cache_create("f2fs_discard_entry", 5416e8512d2eSGu Zheng sizeof(struct discard_entry)); 54177fd9e544SJaegeuk Kim if (!discard_entry_slab) 5418184a5cd2SChao Yu goto fail; 5419184a5cd2SChao Yu 542098510003SChao Yu discard_cmd_slab = f2fs_kmem_cache_create("f2fs_discard_cmd", 5421b01a9201SJaegeuk Kim sizeof(struct discard_cmd)); 5422b01a9201SJaegeuk Kim if (!discard_cmd_slab) 54236ab2a308SChao Yu goto destroy_discard_entry; 5424275b66b0SChao Yu 542598510003SChao Yu sit_entry_set_slab = f2fs_kmem_cache_create("f2fs_sit_entry_set", 5426c9ee0085SChangman Lee sizeof(struct sit_entry_set)); 5427184a5cd2SChao Yu if (!sit_entry_set_slab) 5428b01a9201SJaegeuk Kim goto destroy_discard_cmd; 542988b88a66SJaegeuk Kim 543098510003SChao Yu inmem_entry_slab = f2fs_kmem_cache_create("f2fs_inmem_page_entry", 543188b88a66SJaegeuk Kim sizeof(struct inmem_pages)); 543288b88a66SJaegeuk Kim if (!inmem_entry_slab) 543388b88a66SJaegeuk Kim goto destroy_sit_entry_set; 54347fd9e544SJaegeuk Kim return 0; 5435184a5cd2SChao Yu 543688b88a66SJaegeuk Kim destroy_sit_entry_set: 543788b88a66SJaegeuk Kim kmem_cache_destroy(sit_entry_set_slab); 5438b01a9201SJaegeuk Kim destroy_discard_cmd: 5439b01a9201SJaegeuk Kim kmem_cache_destroy(discard_cmd_slab); 54406ab2a308SChao Yu destroy_discard_entry: 5441184a5cd2SChao Yu kmem_cache_destroy(discard_entry_slab); 5442184a5cd2SChao Yu fail: 5443184a5cd2SChao Yu return -ENOMEM; 54447fd9e544SJaegeuk Kim } 54457fd9e544SJaegeuk Kim 54464d57b86dSChao Yu void f2fs_destroy_segment_manager_caches(void) 54477fd9e544SJaegeuk Kim { 5448184a5cd2SChao Yu kmem_cache_destroy(sit_entry_set_slab); 5449b01a9201SJaegeuk Kim kmem_cache_destroy(discard_cmd_slab); 54507fd9e544SJaegeuk Kim kmem_cache_destroy(discard_entry_slab); 545188b88a66SJaegeuk Kim kmem_cache_destroy(inmem_entry_slab); 54527fd9e544SJaegeuk Kim } 5453