162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/f2fs/segment.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2012 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * http://www.samsung.com/ 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/f2fs_fs.h> 1062306a36Sopenharmony_ci#include <linux/bio.h> 1162306a36Sopenharmony_ci#include <linux/blkdev.h> 1262306a36Sopenharmony_ci#include <linux/sched/mm.h> 1362306a36Sopenharmony_ci#include <linux/prefetch.h> 1462306a36Sopenharmony_ci#include <linux/kthread.h> 1562306a36Sopenharmony_ci#include <linux/swap.h> 1662306a36Sopenharmony_ci#include <linux/timer.h> 1762306a36Sopenharmony_ci#include <linux/freezer.h> 1862306a36Sopenharmony_ci#include <linux/sched/signal.h> 1962306a36Sopenharmony_ci#include <linux/random.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "f2fs.h" 2262306a36Sopenharmony_ci#include "segment.h" 2362306a36Sopenharmony_ci#include "node.h" 2462306a36Sopenharmony_ci#include "gc.h" 2562306a36Sopenharmony_ci#include "iostat.h" 2662306a36Sopenharmony_ci#include <trace/events/f2fs.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define __reverse_ffz(x) __reverse_ffs(~(x)) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic struct kmem_cache *discard_entry_slab; 3162306a36Sopenharmony_cistatic struct kmem_cache *discard_cmd_slab; 3262306a36Sopenharmony_cistatic struct kmem_cache *sit_entry_set_slab; 3362306a36Sopenharmony_cistatic struct kmem_cache *revoke_entry_slab; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic unsigned long __reverse_ulong(unsigned char *str) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned long tmp = 0; 3862306a36Sopenharmony_ci int shift = 24, idx = 0; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#if BITS_PER_LONG == 64 4162306a36Sopenharmony_ci shift = 56; 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci while (shift >= 0) { 4462306a36Sopenharmony_ci tmp |= (unsigned long)str[idx++] << shift; 4562306a36Sopenharmony_ci shift -= BITS_PER_BYTE; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci return tmp; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * __reverse_ffs is copied from include/asm-generic/bitops/__ffs.h since 5262306a36Sopenharmony_ci * MSB and LSB are reversed in a byte by f2fs_set_bit. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic inline unsigned long __reverse_ffs(unsigned long word) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int num = 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#if BITS_PER_LONG == 64 5962306a36Sopenharmony_ci if ((word & 0xffffffff00000000UL) == 0) 6062306a36Sopenharmony_ci num += 32; 6162306a36Sopenharmony_ci else 6262306a36Sopenharmony_ci word >>= 32; 6362306a36Sopenharmony_ci#endif 6462306a36Sopenharmony_ci if ((word & 0xffff0000) == 0) 6562306a36Sopenharmony_ci num += 16; 6662306a36Sopenharmony_ci else 6762306a36Sopenharmony_ci word >>= 16; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if ((word & 0xff00) == 0) 7062306a36Sopenharmony_ci num += 8; 7162306a36Sopenharmony_ci else 7262306a36Sopenharmony_ci word >>= 8; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if ((word & 0xf0) == 0) 7562306a36Sopenharmony_ci num += 4; 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci word >>= 4; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if ((word & 0xc) == 0) 8062306a36Sopenharmony_ci num += 2; 8162306a36Sopenharmony_ci else 8262306a36Sopenharmony_ci word >>= 2; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if ((word & 0x2) == 0) 8562306a36Sopenharmony_ci num += 1; 8662306a36Sopenharmony_ci return num; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * __find_rev_next(_zero)_bit is copied from lib/find_next_bit.c because 9162306a36Sopenharmony_ci * f2fs_set_bit makes MSB and LSB reversed in a byte. 9262306a36Sopenharmony_ci * @size must be integral times of unsigned long. 9362306a36Sopenharmony_ci * Example: 9462306a36Sopenharmony_ci * MSB <--> LSB 9562306a36Sopenharmony_ci * f2fs_set_bit(0, bitmap) => 1000 0000 9662306a36Sopenharmony_ci * f2fs_set_bit(7, bitmap) => 0000 0001 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic unsigned long __find_rev_next_bit(const unsigned long *addr, 9962306a36Sopenharmony_ci unsigned long size, unsigned long offset) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci const unsigned long *p = addr + BIT_WORD(offset); 10262306a36Sopenharmony_ci unsigned long result = size; 10362306a36Sopenharmony_ci unsigned long tmp; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (offset >= size) 10662306a36Sopenharmony_ci return size; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci size -= (offset & ~(BITS_PER_LONG - 1)); 10962306a36Sopenharmony_ci offset %= BITS_PER_LONG; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci while (1) { 11262306a36Sopenharmony_ci if (*p == 0) 11362306a36Sopenharmony_ci goto pass; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci tmp = __reverse_ulong((unsigned char *)p); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci tmp &= ~0UL >> offset; 11862306a36Sopenharmony_ci if (size < BITS_PER_LONG) 11962306a36Sopenharmony_ci tmp &= (~0UL << (BITS_PER_LONG - size)); 12062306a36Sopenharmony_ci if (tmp) 12162306a36Sopenharmony_ci goto found; 12262306a36Sopenharmony_cipass: 12362306a36Sopenharmony_ci if (size <= BITS_PER_LONG) 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci size -= BITS_PER_LONG; 12662306a36Sopenharmony_ci offset = 0; 12762306a36Sopenharmony_ci p++; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci return result; 13062306a36Sopenharmony_cifound: 13162306a36Sopenharmony_ci return result - size + __reverse_ffs(tmp); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic unsigned long __find_rev_next_zero_bit(const unsigned long *addr, 13562306a36Sopenharmony_ci unsigned long size, unsigned long offset) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci const unsigned long *p = addr + BIT_WORD(offset); 13862306a36Sopenharmony_ci unsigned long result = size; 13962306a36Sopenharmony_ci unsigned long tmp; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (offset >= size) 14262306a36Sopenharmony_ci return size; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci size -= (offset & ~(BITS_PER_LONG - 1)); 14562306a36Sopenharmony_ci offset %= BITS_PER_LONG; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci while (1) { 14862306a36Sopenharmony_ci if (*p == ~0UL) 14962306a36Sopenharmony_ci goto pass; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci tmp = __reverse_ulong((unsigned char *)p); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (offset) 15462306a36Sopenharmony_ci tmp |= ~0UL << (BITS_PER_LONG - offset); 15562306a36Sopenharmony_ci if (size < BITS_PER_LONG) 15662306a36Sopenharmony_ci tmp |= ~0UL >> size; 15762306a36Sopenharmony_ci if (tmp != ~0UL) 15862306a36Sopenharmony_ci goto found; 15962306a36Sopenharmony_cipass: 16062306a36Sopenharmony_ci if (size <= BITS_PER_LONG) 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci size -= BITS_PER_LONG; 16362306a36Sopenharmony_ci offset = 0; 16462306a36Sopenharmony_ci p++; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci return result; 16762306a36Sopenharmony_cifound: 16862306a36Sopenharmony_ci return result - size + __reverse_ffz(tmp); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cibool f2fs_need_SSR(struct f2fs_sb_info *sbi) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); 17462306a36Sopenharmony_ci int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); 17562306a36Sopenharmony_ci int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (f2fs_lfs_mode(sbi)) 17862306a36Sopenharmony_ci return false; 17962306a36Sopenharmony_ci if (sbi->gc_mode == GC_URGENT_HIGH) 18062306a36Sopenharmony_ci return true; 18162306a36Sopenharmony_ci if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 18262306a36Sopenharmony_ci return true; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs + 18562306a36Sopenharmony_ci SM_I(sbi)->min_ssr_sections + reserved_sections(sbi)); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_civoid f2fs_abort_atomic_write(struct inode *inode, bool clean) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct f2fs_inode_info *fi = F2FS_I(inode); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!f2fs_is_atomic_file(inode)) 19362306a36Sopenharmony_ci return; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci release_atomic_write_cnt(inode); 19662306a36Sopenharmony_ci clear_inode_flag(inode, FI_ATOMIC_COMMITTED); 19762306a36Sopenharmony_ci clear_inode_flag(inode, FI_ATOMIC_REPLACE); 19862306a36Sopenharmony_ci clear_inode_flag(inode, FI_ATOMIC_FILE); 19962306a36Sopenharmony_ci stat_dec_atomic_inode(inode); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci F2FS_I(inode)->atomic_write_task = NULL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (clean) { 20462306a36Sopenharmony_ci truncate_inode_pages_final(inode->i_mapping); 20562306a36Sopenharmony_ci f2fs_i_size_write(inode, fi->original_i_size); 20662306a36Sopenharmony_ci fi->original_i_size = 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci /* avoid stale dirty inode during eviction */ 20962306a36Sopenharmony_ci sync_inode_metadata(inode, 0); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int __replace_atomic_write_block(struct inode *inode, pgoff_t index, 21362306a36Sopenharmony_ci block_t new_addr, block_t *old_addr, bool recover) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 21662306a36Sopenharmony_ci struct dnode_of_data dn; 21762306a36Sopenharmony_ci struct node_info ni; 21862306a36Sopenharmony_ci int err; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciretry: 22162306a36Sopenharmony_ci set_new_dnode(&dn, inode, NULL, NULL, 0); 22262306a36Sopenharmony_ci err = f2fs_get_dnode_of_data(&dn, index, ALLOC_NODE); 22362306a36Sopenharmony_ci if (err) { 22462306a36Sopenharmony_ci if (err == -ENOMEM) { 22562306a36Sopenharmony_ci f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); 22662306a36Sopenharmony_ci goto retry; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci return err; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci err = f2fs_get_node_info(sbi, dn.nid, &ni, false); 23262306a36Sopenharmony_ci if (err) { 23362306a36Sopenharmony_ci f2fs_put_dnode(&dn); 23462306a36Sopenharmony_ci return err; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (recover) { 23862306a36Sopenharmony_ci /* dn.data_blkaddr is always valid */ 23962306a36Sopenharmony_ci if (!__is_valid_data_blkaddr(new_addr)) { 24062306a36Sopenharmony_ci if (new_addr == NULL_ADDR) 24162306a36Sopenharmony_ci dec_valid_block_count(sbi, inode, 1); 24262306a36Sopenharmony_ci f2fs_invalidate_blocks(sbi, dn.data_blkaddr); 24362306a36Sopenharmony_ci f2fs_update_data_blkaddr(&dn, new_addr); 24462306a36Sopenharmony_ci } else { 24562306a36Sopenharmony_ci f2fs_replace_block(sbi, &dn, dn.data_blkaddr, 24662306a36Sopenharmony_ci new_addr, ni.version, true, true); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } else { 24962306a36Sopenharmony_ci blkcnt_t count = 1; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci err = inc_valid_block_count(sbi, inode, &count, true); 25262306a36Sopenharmony_ci if (err) { 25362306a36Sopenharmony_ci f2fs_put_dnode(&dn); 25462306a36Sopenharmony_ci return err; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci *old_addr = dn.data_blkaddr; 25862306a36Sopenharmony_ci f2fs_truncate_data_blocks_range(&dn, 1); 25962306a36Sopenharmony_ci dec_valid_block_count(sbi, F2FS_I(inode)->cow_inode, count); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci f2fs_replace_block(sbi, &dn, dn.data_blkaddr, new_addr, 26262306a36Sopenharmony_ci ni.version, true, false); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci f2fs_put_dnode(&dn); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci trace_f2fs_replace_atomic_write_block(inode, F2FS_I(inode)->cow_inode, 26862306a36Sopenharmony_ci index, old_addr ? *old_addr : 0, new_addr, recover); 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void __complete_revoke_list(struct inode *inode, struct list_head *head, 27362306a36Sopenharmony_ci bool revoke) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct revoke_entry *cur, *tmp; 27662306a36Sopenharmony_ci pgoff_t start_index = 0; 27762306a36Sopenharmony_ci bool truncate = is_inode_flag_set(inode, FI_ATOMIC_REPLACE); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci list_for_each_entry_safe(cur, tmp, head, list) { 28062306a36Sopenharmony_ci if (revoke) { 28162306a36Sopenharmony_ci __replace_atomic_write_block(inode, cur->index, 28262306a36Sopenharmony_ci cur->old_addr, NULL, true); 28362306a36Sopenharmony_ci } else if (truncate) { 28462306a36Sopenharmony_ci f2fs_truncate_hole(inode, start_index, cur->index); 28562306a36Sopenharmony_ci start_index = cur->index + 1; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci list_del(&cur->list); 28962306a36Sopenharmony_ci kmem_cache_free(revoke_entry_slab, cur); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (!revoke && truncate) 29362306a36Sopenharmony_ci f2fs_do_truncate_blocks(inode, start_index * PAGE_SIZE, false); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int __f2fs_commit_atomic_write(struct inode *inode) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 29962306a36Sopenharmony_ci struct f2fs_inode_info *fi = F2FS_I(inode); 30062306a36Sopenharmony_ci struct inode *cow_inode = fi->cow_inode; 30162306a36Sopenharmony_ci struct revoke_entry *new; 30262306a36Sopenharmony_ci struct list_head revoke_list; 30362306a36Sopenharmony_ci block_t blkaddr; 30462306a36Sopenharmony_ci struct dnode_of_data dn; 30562306a36Sopenharmony_ci pgoff_t len = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); 30662306a36Sopenharmony_ci pgoff_t off = 0, blen, index; 30762306a36Sopenharmony_ci int ret = 0, i; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci INIT_LIST_HEAD(&revoke_list); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci while (len) { 31262306a36Sopenharmony_ci blen = min_t(pgoff_t, ADDRS_PER_BLOCK(cow_inode), len); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci set_new_dnode(&dn, cow_inode, NULL, NULL, 0); 31562306a36Sopenharmony_ci ret = f2fs_get_dnode_of_data(&dn, off, LOOKUP_NODE_RA); 31662306a36Sopenharmony_ci if (ret && ret != -ENOENT) { 31762306a36Sopenharmony_ci goto out; 31862306a36Sopenharmony_ci } else if (ret == -ENOENT) { 31962306a36Sopenharmony_ci ret = 0; 32062306a36Sopenharmony_ci if (dn.max_level == 0) 32162306a36Sopenharmony_ci goto out; 32262306a36Sopenharmony_ci goto next; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci blen = min((pgoff_t)ADDRS_PER_PAGE(dn.node_page, cow_inode), 32662306a36Sopenharmony_ci len); 32762306a36Sopenharmony_ci index = off; 32862306a36Sopenharmony_ci for (i = 0; i < blen; i++, dn.ofs_in_node++, index++) { 32962306a36Sopenharmony_ci blkaddr = f2fs_data_blkaddr(&dn); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!__is_valid_data_blkaddr(blkaddr)) { 33262306a36Sopenharmony_ci continue; 33362306a36Sopenharmony_ci } else if (!f2fs_is_valid_blkaddr(sbi, blkaddr, 33462306a36Sopenharmony_ci DATA_GENERIC_ENHANCE)) { 33562306a36Sopenharmony_ci f2fs_put_dnode(&dn); 33662306a36Sopenharmony_ci ret = -EFSCORRUPTED; 33762306a36Sopenharmony_ci f2fs_handle_error(sbi, 33862306a36Sopenharmony_ci ERROR_INVALID_BLKADDR); 33962306a36Sopenharmony_ci goto out; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci new = f2fs_kmem_cache_alloc(revoke_entry_slab, GFP_NOFS, 34362306a36Sopenharmony_ci true, NULL); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci ret = __replace_atomic_write_block(inode, index, blkaddr, 34662306a36Sopenharmony_ci &new->old_addr, false); 34762306a36Sopenharmony_ci if (ret) { 34862306a36Sopenharmony_ci f2fs_put_dnode(&dn); 34962306a36Sopenharmony_ci kmem_cache_free(revoke_entry_slab, new); 35062306a36Sopenharmony_ci goto out; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci f2fs_update_data_blkaddr(&dn, NULL_ADDR); 35462306a36Sopenharmony_ci new->index = index; 35562306a36Sopenharmony_ci list_add_tail(&new->list, &revoke_list); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci f2fs_put_dnode(&dn); 35862306a36Sopenharmony_cinext: 35962306a36Sopenharmony_ci off += blen; 36062306a36Sopenharmony_ci len -= blen; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ciout: 36462306a36Sopenharmony_ci if (ret) { 36562306a36Sopenharmony_ci sbi->revoked_atomic_block += fi->atomic_write_cnt; 36662306a36Sopenharmony_ci } else { 36762306a36Sopenharmony_ci sbi->committed_atomic_block += fi->atomic_write_cnt; 36862306a36Sopenharmony_ci set_inode_flag(inode, FI_ATOMIC_COMMITTED); 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci __complete_revoke_list(inode, &revoke_list, ret ? true : false); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciint f2fs_commit_atomic_write(struct inode *inode) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 37962306a36Sopenharmony_ci struct f2fs_inode_info *fi = F2FS_I(inode); 38062306a36Sopenharmony_ci int err; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci err = filemap_write_and_wait_range(inode->i_mapping, 0, LLONG_MAX); 38362306a36Sopenharmony_ci if (err) 38462306a36Sopenharmony_ci return err; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci f2fs_down_write(&fi->i_gc_rwsem[WRITE]); 38762306a36Sopenharmony_ci f2fs_lock_op(sbi); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci err = __f2fs_commit_atomic_write(inode); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci f2fs_unlock_op(sbi); 39262306a36Sopenharmony_ci f2fs_up_write(&fi->i_gc_rwsem[WRITE]); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return err; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* 39862306a36Sopenharmony_ci * This function balances dirty node and dentry pages. 39962306a36Sopenharmony_ci * In addition, it controls garbage collection. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_civoid f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci if (time_to_inject(sbi, FAULT_CHECKPOINT)) 40462306a36Sopenharmony_ci f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_FAULT_INJECT); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* balance_fs_bg is able to be pending */ 40762306a36Sopenharmony_ci if (need && excess_cached_nats(sbi)) 40862306a36Sopenharmony_ci f2fs_balance_fs_bg(sbi, false); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!f2fs_is_checkpoint_ready(sbi)) 41162306a36Sopenharmony_ci return; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * We should do GC or end up with checkpoint, if there are so many dirty 41562306a36Sopenharmony_ci * dir/node pages without enough free segments. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci if (has_enough_free_secs(sbi, 0, 0)) 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (test_opt(sbi, GC_MERGE) && sbi->gc_thread && 42162306a36Sopenharmony_ci sbi->gc_thread->f2fs_gc_task) { 42262306a36Sopenharmony_ci DEFINE_WAIT(wait); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci prepare_to_wait(&sbi->gc_thread->fggc_wq, &wait, 42562306a36Sopenharmony_ci TASK_UNINTERRUPTIBLE); 42662306a36Sopenharmony_ci wake_up(&sbi->gc_thread->gc_wait_queue_head); 42762306a36Sopenharmony_ci io_schedule(); 42862306a36Sopenharmony_ci finish_wait(&sbi->gc_thread->fggc_wq, &wait); 42962306a36Sopenharmony_ci } else { 43062306a36Sopenharmony_ci struct f2fs_gc_control gc_control = { 43162306a36Sopenharmony_ci .victim_segno = NULL_SEGNO, 43262306a36Sopenharmony_ci .init_gc_type = BG_GC, 43362306a36Sopenharmony_ci .no_bg_gc = true, 43462306a36Sopenharmony_ci .should_migrate_blocks = false, 43562306a36Sopenharmony_ci .err_gc_skipped = false, 43662306a36Sopenharmony_ci .nr_free_secs = 1 }; 43762306a36Sopenharmony_ci f2fs_down_write(&sbi->gc_lock); 43862306a36Sopenharmony_ci stat_inc_gc_call_count(sbi, FOREGROUND); 43962306a36Sopenharmony_ci f2fs_gc(sbi, &gc_control); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic inline bool excess_dirty_threshold(struct f2fs_sb_info *sbi) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int factor = f2fs_rwsem_is_locked(&sbi->cp_rwsem) ? 3 : 2; 44662306a36Sopenharmony_ci unsigned int dents = get_pages(sbi, F2FS_DIRTY_DENTS); 44762306a36Sopenharmony_ci unsigned int qdata = get_pages(sbi, F2FS_DIRTY_QDATA); 44862306a36Sopenharmony_ci unsigned int nodes = get_pages(sbi, F2FS_DIRTY_NODES); 44962306a36Sopenharmony_ci unsigned int meta = get_pages(sbi, F2FS_DIRTY_META); 45062306a36Sopenharmony_ci unsigned int imeta = get_pages(sbi, F2FS_DIRTY_IMETA); 45162306a36Sopenharmony_ci unsigned int threshold = sbi->blocks_per_seg * factor * 45262306a36Sopenharmony_ci DEFAULT_DIRTY_THRESHOLD; 45362306a36Sopenharmony_ci unsigned int global_threshold = threshold * 3 / 2; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (dents >= threshold || qdata >= threshold || 45662306a36Sopenharmony_ci nodes >= threshold || meta >= threshold || 45762306a36Sopenharmony_ci imeta >= threshold) 45862306a36Sopenharmony_ci return true; 45962306a36Sopenharmony_ci return dents + qdata + nodes + meta + imeta > global_threshold; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_civoid f2fs_balance_fs_bg(struct f2fs_sb_info *sbi, bool from_bg) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) 46562306a36Sopenharmony_ci return; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* try to shrink extent cache when there is no enough memory */ 46862306a36Sopenharmony_ci if (!f2fs_available_free_memory(sbi, READ_EXTENT_CACHE)) 46962306a36Sopenharmony_ci f2fs_shrink_read_extent_tree(sbi, 47062306a36Sopenharmony_ci READ_EXTENT_CACHE_SHRINK_NUMBER); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* try to shrink age extent cache when there is no enough memory */ 47362306a36Sopenharmony_ci if (!f2fs_available_free_memory(sbi, AGE_EXTENT_CACHE)) 47462306a36Sopenharmony_ci f2fs_shrink_age_extent_tree(sbi, 47562306a36Sopenharmony_ci AGE_EXTENT_CACHE_SHRINK_NUMBER); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* check the # of cached NAT entries */ 47862306a36Sopenharmony_ci if (!f2fs_available_free_memory(sbi, NAT_ENTRIES)) 47962306a36Sopenharmony_ci f2fs_try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (!f2fs_available_free_memory(sbi, FREE_NIDS)) 48262306a36Sopenharmony_ci f2fs_try_to_free_nids(sbi, MAX_FREE_NIDS); 48362306a36Sopenharmony_ci else 48462306a36Sopenharmony_ci f2fs_build_free_nids(sbi, false, false); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (excess_dirty_nats(sbi) || excess_dirty_threshold(sbi) || 48762306a36Sopenharmony_ci excess_prefree_segs(sbi) || !f2fs_space_for_roll_forward(sbi)) 48862306a36Sopenharmony_ci goto do_sync; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* there is background inflight IO or foreground operation recently */ 49162306a36Sopenharmony_ci if (is_inflight_io(sbi, REQ_TIME) || 49262306a36Sopenharmony_ci (!f2fs_time_over(sbi, REQ_TIME) && f2fs_rwsem_is_locked(&sbi->cp_rwsem))) 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* exceed periodical checkpoint timeout threshold */ 49662306a36Sopenharmony_ci if (f2fs_time_over(sbi, CP_TIME)) 49762306a36Sopenharmony_ci goto do_sync; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* checkpoint is the only way to shrink partial cached entries */ 50062306a36Sopenharmony_ci if (f2fs_available_free_memory(sbi, NAT_ENTRIES) && 50162306a36Sopenharmony_ci f2fs_available_free_memory(sbi, INO_ENTRIES)) 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cido_sync: 50562306a36Sopenharmony_ci if (test_opt(sbi, DATA_FLUSH) && from_bg) { 50662306a36Sopenharmony_ci struct blk_plug plug; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci mutex_lock(&sbi->flush_lock); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci blk_start_plug(&plug); 51162306a36Sopenharmony_ci f2fs_sync_dirty_inodes(sbi, FILE_INODE, false); 51262306a36Sopenharmony_ci blk_finish_plug(&plug); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci mutex_unlock(&sbi->flush_lock); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci stat_inc_cp_call_count(sbi, BACKGROUND); 51762306a36Sopenharmony_ci f2fs_sync_fs(sbi->sb, 1); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int __submit_flush_wait(struct f2fs_sb_info *sbi, 52162306a36Sopenharmony_ci struct block_device *bdev) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci int ret = blkdev_issue_flush(bdev); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci trace_f2fs_issue_flush(bdev, test_opt(sbi, NOBARRIER), 52662306a36Sopenharmony_ci test_opt(sbi, FLUSH_MERGE), ret); 52762306a36Sopenharmony_ci if (!ret) 52862306a36Sopenharmony_ci f2fs_update_iostat(sbi, NULL, FS_FLUSH_IO, 0); 52962306a36Sopenharmony_ci return ret; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci int ret = 0; 53562306a36Sopenharmony_ci int i; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!f2fs_is_multi_device(sbi)) 53862306a36Sopenharmony_ci return __submit_flush_wait(sbi, sbi->sb->s_bdev); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for (i = 0; i < sbi->s_ndevs; i++) { 54162306a36Sopenharmony_ci if (!f2fs_is_dirty_device(sbi, ino, i, FLUSH_INO)) 54262306a36Sopenharmony_ci continue; 54362306a36Sopenharmony_ci ret = __submit_flush_wait(sbi, FDEV(i).bdev); 54462306a36Sopenharmony_ci if (ret) 54562306a36Sopenharmony_ci break; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci return ret; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int issue_flush_thread(void *data) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct f2fs_sb_info *sbi = data; 55362306a36Sopenharmony_ci struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 55462306a36Sopenharmony_ci wait_queue_head_t *q = &fcc->flush_wait_queue; 55562306a36Sopenharmony_cirepeat: 55662306a36Sopenharmony_ci if (kthread_should_stop()) 55762306a36Sopenharmony_ci return 0; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (!llist_empty(&fcc->issue_list)) { 56062306a36Sopenharmony_ci struct flush_cmd *cmd, *next; 56162306a36Sopenharmony_ci int ret; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci fcc->dispatch_list = llist_del_all(&fcc->issue_list); 56462306a36Sopenharmony_ci fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci cmd = llist_entry(fcc->dispatch_list, struct flush_cmd, llnode); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ret = submit_flush_wait(sbi, cmd->ino); 56962306a36Sopenharmony_ci atomic_inc(&fcc->issued_flush); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci llist_for_each_entry_safe(cmd, next, 57262306a36Sopenharmony_ci fcc->dispatch_list, llnode) { 57362306a36Sopenharmony_ci cmd->ret = ret; 57462306a36Sopenharmony_ci complete(&cmd->wait); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci fcc->dispatch_list = NULL; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci wait_event_interruptible(*q, 58062306a36Sopenharmony_ci kthread_should_stop() || !llist_empty(&fcc->issue_list)); 58162306a36Sopenharmony_ci goto repeat; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ciint f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 58762306a36Sopenharmony_ci struct flush_cmd cmd; 58862306a36Sopenharmony_ci int ret; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (test_opt(sbi, NOBARRIER)) 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!test_opt(sbi, FLUSH_MERGE)) { 59462306a36Sopenharmony_ci atomic_inc(&fcc->queued_flush); 59562306a36Sopenharmony_ci ret = submit_flush_wait(sbi, ino); 59662306a36Sopenharmony_ci atomic_dec(&fcc->queued_flush); 59762306a36Sopenharmony_ci atomic_inc(&fcc->issued_flush); 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (atomic_inc_return(&fcc->queued_flush) == 1 || 60262306a36Sopenharmony_ci f2fs_is_multi_device(sbi)) { 60362306a36Sopenharmony_ci ret = submit_flush_wait(sbi, ino); 60462306a36Sopenharmony_ci atomic_dec(&fcc->queued_flush); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci atomic_inc(&fcc->issued_flush); 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci cmd.ino = ino; 61162306a36Sopenharmony_ci init_completion(&cmd.wait); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci llist_add(&cmd.llnode, &fcc->issue_list); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * update issue_list before we wake up issue_flush thread, this 61762306a36Sopenharmony_ci * smp_mb() pairs with another barrier in ___wait_event(), see 61862306a36Sopenharmony_ci * more details in comments of waitqueue_active(). 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci smp_mb(); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (waitqueue_active(&fcc->flush_wait_queue)) 62362306a36Sopenharmony_ci wake_up(&fcc->flush_wait_queue); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (fcc->f2fs_issue_flush) { 62662306a36Sopenharmony_ci wait_for_completion(&cmd.wait); 62762306a36Sopenharmony_ci atomic_dec(&fcc->queued_flush); 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci struct llist_node *list; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci list = llist_del_all(&fcc->issue_list); 63262306a36Sopenharmony_ci if (!list) { 63362306a36Sopenharmony_ci wait_for_completion(&cmd.wait); 63462306a36Sopenharmony_ci atomic_dec(&fcc->queued_flush); 63562306a36Sopenharmony_ci } else { 63662306a36Sopenharmony_ci struct flush_cmd *tmp, *next; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci ret = submit_flush_wait(sbi, ino); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci llist_for_each_entry_safe(tmp, next, list, llnode) { 64162306a36Sopenharmony_ci if (tmp == &cmd) { 64262306a36Sopenharmony_ci cmd.ret = ret; 64362306a36Sopenharmony_ci atomic_dec(&fcc->queued_flush); 64462306a36Sopenharmony_ci continue; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci tmp->ret = ret; 64762306a36Sopenharmony_ci complete(&tmp->wait); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return cmd.ret; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ciint f2fs_create_flush_cmd_control(struct f2fs_sb_info *sbi) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci dev_t dev = sbi->sb->s_bdev->bd_dev; 65862306a36Sopenharmony_ci struct flush_cmd_control *fcc; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (SM_I(sbi)->fcc_info) { 66162306a36Sopenharmony_ci fcc = SM_I(sbi)->fcc_info; 66262306a36Sopenharmony_ci if (fcc->f2fs_issue_flush) 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci goto init_thread; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci fcc = f2fs_kzalloc(sbi, sizeof(struct flush_cmd_control), GFP_KERNEL); 66862306a36Sopenharmony_ci if (!fcc) 66962306a36Sopenharmony_ci return -ENOMEM; 67062306a36Sopenharmony_ci atomic_set(&fcc->issued_flush, 0); 67162306a36Sopenharmony_ci atomic_set(&fcc->queued_flush, 0); 67262306a36Sopenharmony_ci init_waitqueue_head(&fcc->flush_wait_queue); 67362306a36Sopenharmony_ci init_llist_head(&fcc->issue_list); 67462306a36Sopenharmony_ci SM_I(sbi)->fcc_info = fcc; 67562306a36Sopenharmony_ci if (!test_opt(sbi, FLUSH_MERGE)) 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ciinit_thread: 67962306a36Sopenharmony_ci fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, 68062306a36Sopenharmony_ci "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); 68162306a36Sopenharmony_ci if (IS_ERR(fcc->f2fs_issue_flush)) { 68262306a36Sopenharmony_ci int err = PTR_ERR(fcc->f2fs_issue_flush); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci fcc->f2fs_issue_flush = NULL; 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_civoid f2fs_destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (fcc && fcc->f2fs_issue_flush) { 69662306a36Sopenharmony_ci struct task_struct *flush_thread = fcc->f2fs_issue_flush; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci fcc->f2fs_issue_flush = NULL; 69962306a36Sopenharmony_ci kthread_stop(flush_thread); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci if (free) { 70262306a36Sopenharmony_ci kfree(fcc); 70362306a36Sopenharmony_ci SM_I(sbi)->fcc_info = NULL; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ciint f2fs_flush_device_cache(struct f2fs_sb_info *sbi) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci int ret = 0, i; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (!f2fs_is_multi_device(sbi)) 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (test_opt(sbi, NOBARRIER)) 71562306a36Sopenharmony_ci return 0; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci for (i = 1; i < sbi->s_ndevs; i++) { 71862306a36Sopenharmony_ci int count = DEFAULT_RETRY_IO_COUNT; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!f2fs_test_bit(i, (char *)&sbi->dirty_device)) 72162306a36Sopenharmony_ci continue; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci do { 72462306a36Sopenharmony_ci ret = __submit_flush_wait(sbi, FDEV(i).bdev); 72562306a36Sopenharmony_ci if (ret) 72662306a36Sopenharmony_ci f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); 72762306a36Sopenharmony_ci } while (ret && --count); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (ret) { 73062306a36Sopenharmony_ci f2fs_stop_checkpoint(sbi, false, 73162306a36Sopenharmony_ci STOP_CP_REASON_FLUSH_FAIL); 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci spin_lock(&sbi->dev_lock); 73662306a36Sopenharmony_ci f2fs_clear_bit(i, (char *)&sbi->dirty_device); 73762306a36Sopenharmony_ci spin_unlock(&sbi->dev_lock); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return ret; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 74462306a36Sopenharmony_ci enum dirty_type dirty_type) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* need not be added */ 74962306a36Sopenharmony_ci if (IS_CURSEG(sbi, segno)) 75062306a36Sopenharmony_ci return; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) 75362306a36Sopenharmony_ci dirty_i->nr_dirty[dirty_type]++; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (dirty_type == DIRTY) { 75662306a36Sopenharmony_ci struct seg_entry *sentry = get_seg_entry(sbi, segno); 75762306a36Sopenharmony_ci enum dirty_type t = sentry->type; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (unlikely(t >= DIRTY)) { 76062306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 76162306a36Sopenharmony_ci return; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci if (!test_and_set_bit(segno, dirty_i->dirty_segmap[t])) 76462306a36Sopenharmony_ci dirty_i->nr_dirty[t]++; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (__is_large_section(sbi)) { 76762306a36Sopenharmony_ci unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 76862306a36Sopenharmony_ci block_t valid_blocks = 76962306a36Sopenharmony_ci get_valid_blocks(sbi, segno, true); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci f2fs_bug_on(sbi, unlikely(!valid_blocks || 77262306a36Sopenharmony_ci valid_blocks == CAP_BLKS_PER_SEC(sbi))); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!IS_CURSEC(sbi, secno)) 77562306a36Sopenharmony_ci set_bit(secno, dirty_i->dirty_secmap); 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 78162306a36Sopenharmony_ci enum dirty_type dirty_type) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 78462306a36Sopenharmony_ci block_t valid_blocks; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type])) 78762306a36Sopenharmony_ci dirty_i->nr_dirty[dirty_type]--; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (dirty_type == DIRTY) { 79062306a36Sopenharmony_ci struct seg_entry *sentry = get_seg_entry(sbi, segno); 79162306a36Sopenharmony_ci enum dirty_type t = sentry->type; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) 79462306a36Sopenharmony_ci dirty_i->nr_dirty[t]--; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci valid_blocks = get_valid_blocks(sbi, segno, true); 79762306a36Sopenharmony_ci if (valid_blocks == 0) { 79862306a36Sopenharmony_ci clear_bit(GET_SEC_FROM_SEG(sbi, segno), 79962306a36Sopenharmony_ci dirty_i->victim_secmap); 80062306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 80162306a36Sopenharmony_ci clear_bit(segno, SIT_I(sbi)->invalid_segmap); 80262306a36Sopenharmony_ci#endif 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci if (__is_large_section(sbi)) { 80562306a36Sopenharmony_ci unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!valid_blocks || 80862306a36Sopenharmony_ci valid_blocks == CAP_BLKS_PER_SEC(sbi)) { 80962306a36Sopenharmony_ci clear_bit(secno, dirty_i->dirty_secmap); 81062306a36Sopenharmony_ci return; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (!IS_CURSEC(sbi, secno)) 81462306a36Sopenharmony_ci set_bit(secno, dirty_i->dirty_secmap); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci/* 82062306a36Sopenharmony_ci * Should not occur error such as -ENOMEM. 82162306a36Sopenharmony_ci * Adding dirty entry into seglist is not critical operation. 82262306a36Sopenharmony_ci * If a given segment is one of current working segments, it won't be added. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_cistatic void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 82762306a36Sopenharmony_ci unsigned short valid_blocks, ckpt_valid_blocks; 82862306a36Sopenharmony_ci unsigned int usable_blocks; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno)) 83162306a36Sopenharmony_ci return; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci usable_blocks = f2fs_usable_blks_in_seg(sbi, segno); 83462306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci valid_blocks = get_valid_blocks(sbi, segno, false); 83762306a36Sopenharmony_ci ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno, false); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (valid_blocks == 0 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) || 84062306a36Sopenharmony_ci ckpt_valid_blocks == usable_blocks)) { 84162306a36Sopenharmony_ci __locate_dirty_segment(sbi, segno, PRE); 84262306a36Sopenharmony_ci __remove_dirty_segment(sbi, segno, DIRTY); 84362306a36Sopenharmony_ci } else if (valid_blocks < usable_blocks) { 84462306a36Sopenharmony_ci __locate_dirty_segment(sbi, segno, DIRTY); 84562306a36Sopenharmony_ci } else { 84662306a36Sopenharmony_ci /* Recovery routine with SSR needs this */ 84762306a36Sopenharmony_ci __remove_dirty_segment(sbi, segno, DIRTY); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci/* This moves currently empty dirty blocks to prefree. Must hold seglist_lock */ 85462306a36Sopenharmony_civoid f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 85762306a36Sopenharmony_ci unsigned int segno; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 86062306a36Sopenharmony_ci for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 86162306a36Sopenharmony_ci if (get_valid_blocks(sbi, segno, false)) 86262306a36Sopenharmony_ci continue; 86362306a36Sopenharmony_ci if (IS_CURSEG(sbi, segno)) 86462306a36Sopenharmony_ci continue; 86562306a36Sopenharmony_ci __locate_dirty_segment(sbi, segno, PRE); 86662306a36Sopenharmony_ci __remove_dirty_segment(sbi, segno, DIRTY); 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ciblock_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci int ovp_hole_segs = 87462306a36Sopenharmony_ci (overprovision_segments(sbi) - reserved_segments(sbi)); 87562306a36Sopenharmony_ci block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg; 87662306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 87762306a36Sopenharmony_ci block_t holes[2] = {0, 0}; /* DATA and NODE */ 87862306a36Sopenharmony_ci block_t unusable; 87962306a36Sopenharmony_ci struct seg_entry *se; 88062306a36Sopenharmony_ci unsigned int segno; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 88362306a36Sopenharmony_ci for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 88462306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 88562306a36Sopenharmony_ci if (IS_NODESEG(se->type)) 88662306a36Sopenharmony_ci holes[NODE] += f2fs_usable_blks_in_seg(sbi, segno) - 88762306a36Sopenharmony_ci se->valid_blocks; 88862306a36Sopenharmony_ci else 88962306a36Sopenharmony_ci holes[DATA] += f2fs_usable_blks_in_seg(sbi, segno) - 89062306a36Sopenharmony_ci se->valid_blocks; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci unusable = max(holes[DATA], holes[NODE]); 89562306a36Sopenharmony_ci if (unusable > ovp_holes) 89662306a36Sopenharmony_ci return unusable - ovp_holes; 89762306a36Sopenharmony_ci return 0; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ciint f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci int ovp_hole_segs = 90362306a36Sopenharmony_ci (overprovision_segments(sbi) - reserved_segments(sbi)); 90462306a36Sopenharmony_ci if (unusable > F2FS_OPTION(sbi).unusable_cap) 90562306a36Sopenharmony_ci return -EAGAIN; 90662306a36Sopenharmony_ci if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) && 90762306a36Sopenharmony_ci dirty_segments(sbi) > ovp_hole_segs) 90862306a36Sopenharmony_ci return -EAGAIN; 90962306a36Sopenharmony_ci return 0; 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci/* This is only used by SBI_CP_DISABLED */ 91362306a36Sopenharmony_cistatic unsigned int get_free_segment(struct f2fs_sb_info *sbi) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 91662306a36Sopenharmony_ci unsigned int segno = 0; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 91962306a36Sopenharmony_ci for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) { 92062306a36Sopenharmony_ci if (get_valid_blocks(sbi, segno, false)) 92162306a36Sopenharmony_ci continue; 92262306a36Sopenharmony_ci if (get_ckpt_valid_blocks(sbi, segno, false)) 92362306a36Sopenharmony_ci continue; 92462306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 92562306a36Sopenharmony_ci return segno; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 92862306a36Sopenharmony_ci return NULL_SEGNO; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi, 93262306a36Sopenharmony_ci struct block_device *bdev, block_t lstart, 93362306a36Sopenharmony_ci block_t start, block_t len) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 93662306a36Sopenharmony_ci struct list_head *pend_list; 93762306a36Sopenharmony_ci struct discard_cmd *dc; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci f2fs_bug_on(sbi, !len); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci pend_list = &dcc->pend_list[plist_idx(len)]; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS, true, NULL); 94462306a36Sopenharmony_ci INIT_LIST_HEAD(&dc->list); 94562306a36Sopenharmony_ci dc->bdev = bdev; 94662306a36Sopenharmony_ci dc->di.lstart = lstart; 94762306a36Sopenharmony_ci dc->di.start = start; 94862306a36Sopenharmony_ci dc->di.len = len; 94962306a36Sopenharmony_ci dc->ref = 0; 95062306a36Sopenharmony_ci dc->state = D_PREP; 95162306a36Sopenharmony_ci dc->queued = 0; 95262306a36Sopenharmony_ci dc->error = 0; 95362306a36Sopenharmony_ci init_completion(&dc->wait); 95462306a36Sopenharmony_ci list_add_tail(&dc->list, pend_list); 95562306a36Sopenharmony_ci spin_lock_init(&dc->lock); 95662306a36Sopenharmony_ci dc->bio_ref = 0; 95762306a36Sopenharmony_ci atomic_inc(&dcc->discard_cmd_cnt); 95862306a36Sopenharmony_ci dcc->undiscard_blks += len; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return dc; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic bool f2fs_check_discard_tree(struct f2fs_sb_info *sbi) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 96662306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 96762306a36Sopenharmony_ci struct rb_node *cur = rb_first_cached(&dcc->root), *next; 96862306a36Sopenharmony_ci struct discard_cmd *cur_dc, *next_dc; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci while (cur) { 97162306a36Sopenharmony_ci next = rb_next(cur); 97262306a36Sopenharmony_ci if (!next) 97362306a36Sopenharmony_ci return true; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci cur_dc = rb_entry(cur, struct discard_cmd, rb_node); 97662306a36Sopenharmony_ci next_dc = rb_entry(next, struct discard_cmd, rb_node); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (cur_dc->di.lstart + cur_dc->di.len > next_dc->di.lstart) { 97962306a36Sopenharmony_ci f2fs_info(sbi, "broken discard_rbtree, " 98062306a36Sopenharmony_ci "cur(%u, %u) next(%u, %u)", 98162306a36Sopenharmony_ci cur_dc->di.lstart, cur_dc->di.len, 98262306a36Sopenharmony_ci next_dc->di.lstart, next_dc->di.len); 98362306a36Sopenharmony_ci return false; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci cur = next; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci#endif 98862306a36Sopenharmony_ci return true; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic struct discard_cmd *__lookup_discard_cmd(struct f2fs_sb_info *sbi, 99262306a36Sopenharmony_ci block_t blkaddr) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 99562306a36Sopenharmony_ci struct rb_node *node = dcc->root.rb_root.rb_node; 99662306a36Sopenharmony_ci struct discard_cmd *dc; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci while (node) { 99962306a36Sopenharmony_ci dc = rb_entry(node, struct discard_cmd, rb_node); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (blkaddr < dc->di.lstart) 100262306a36Sopenharmony_ci node = node->rb_left; 100362306a36Sopenharmony_ci else if (blkaddr >= dc->di.lstart + dc->di.len) 100462306a36Sopenharmony_ci node = node->rb_right; 100562306a36Sopenharmony_ci else 100662306a36Sopenharmony_ci return dc; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci return NULL; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic struct discard_cmd *__lookup_discard_cmd_ret(struct rb_root_cached *root, 101262306a36Sopenharmony_ci block_t blkaddr, 101362306a36Sopenharmony_ci struct discard_cmd **prev_entry, 101462306a36Sopenharmony_ci struct discard_cmd **next_entry, 101562306a36Sopenharmony_ci struct rb_node ***insert_p, 101662306a36Sopenharmony_ci struct rb_node **insert_parent) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci struct rb_node **pnode = &root->rb_root.rb_node; 101962306a36Sopenharmony_ci struct rb_node *parent = NULL, *tmp_node; 102062306a36Sopenharmony_ci struct discard_cmd *dc; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci *insert_p = NULL; 102362306a36Sopenharmony_ci *insert_parent = NULL; 102462306a36Sopenharmony_ci *prev_entry = NULL; 102562306a36Sopenharmony_ci *next_entry = NULL; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (RB_EMPTY_ROOT(&root->rb_root)) 102862306a36Sopenharmony_ci return NULL; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci while (*pnode) { 103162306a36Sopenharmony_ci parent = *pnode; 103262306a36Sopenharmony_ci dc = rb_entry(*pnode, struct discard_cmd, rb_node); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (blkaddr < dc->di.lstart) 103562306a36Sopenharmony_ci pnode = &(*pnode)->rb_left; 103662306a36Sopenharmony_ci else if (blkaddr >= dc->di.lstart + dc->di.len) 103762306a36Sopenharmony_ci pnode = &(*pnode)->rb_right; 103862306a36Sopenharmony_ci else 103962306a36Sopenharmony_ci goto lookup_neighbors; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci *insert_p = pnode; 104362306a36Sopenharmony_ci *insert_parent = parent; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci dc = rb_entry(parent, struct discard_cmd, rb_node); 104662306a36Sopenharmony_ci tmp_node = parent; 104762306a36Sopenharmony_ci if (parent && blkaddr > dc->di.lstart) 104862306a36Sopenharmony_ci tmp_node = rb_next(parent); 104962306a36Sopenharmony_ci *next_entry = rb_entry_safe(tmp_node, struct discard_cmd, rb_node); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci tmp_node = parent; 105262306a36Sopenharmony_ci if (parent && blkaddr < dc->di.lstart) 105362306a36Sopenharmony_ci tmp_node = rb_prev(parent); 105462306a36Sopenharmony_ci *prev_entry = rb_entry_safe(tmp_node, struct discard_cmd, rb_node); 105562306a36Sopenharmony_ci return NULL; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cilookup_neighbors: 105862306a36Sopenharmony_ci /* lookup prev node for merging backward later */ 105962306a36Sopenharmony_ci tmp_node = rb_prev(&dc->rb_node); 106062306a36Sopenharmony_ci *prev_entry = rb_entry_safe(tmp_node, struct discard_cmd, rb_node); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* lookup next node for merging frontward later */ 106362306a36Sopenharmony_ci tmp_node = rb_next(&dc->rb_node); 106462306a36Sopenharmony_ci *next_entry = rb_entry_safe(tmp_node, struct discard_cmd, rb_node); 106562306a36Sopenharmony_ci return dc; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic void __detach_discard_cmd(struct discard_cmd_control *dcc, 106962306a36Sopenharmony_ci struct discard_cmd *dc) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci if (dc->state == D_DONE) 107262306a36Sopenharmony_ci atomic_sub(dc->queued, &dcc->queued_discard); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci list_del(&dc->list); 107562306a36Sopenharmony_ci rb_erase_cached(&dc->rb_node, &dcc->root); 107662306a36Sopenharmony_ci dcc->undiscard_blks -= dc->di.len; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci kmem_cache_free(discard_cmd_slab, dc); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci atomic_dec(&dcc->discard_cmd_cnt); 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic void __remove_discard_cmd(struct f2fs_sb_info *sbi, 108462306a36Sopenharmony_ci struct discard_cmd *dc) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 108762306a36Sopenharmony_ci unsigned long flags; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci trace_f2fs_remove_discard(dc->bdev, dc->di.start, dc->di.len); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci spin_lock_irqsave(&dc->lock, flags); 109262306a36Sopenharmony_ci if (dc->bio_ref) { 109362306a36Sopenharmony_ci spin_unlock_irqrestore(&dc->lock, flags); 109462306a36Sopenharmony_ci return; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci spin_unlock_irqrestore(&dc->lock, flags); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci f2fs_bug_on(sbi, dc->ref); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (dc->error == -EOPNOTSUPP) 110162306a36Sopenharmony_ci dc->error = 0; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (dc->error) 110462306a36Sopenharmony_ci printk_ratelimited( 110562306a36Sopenharmony_ci "%sF2FS-fs (%s): Issue discard(%u, %u, %u) failed, ret: %d", 110662306a36Sopenharmony_ci KERN_INFO, sbi->sb->s_id, 110762306a36Sopenharmony_ci dc->di.lstart, dc->di.start, dc->di.len, dc->error); 110862306a36Sopenharmony_ci __detach_discard_cmd(dcc, dc); 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic void f2fs_submit_discard_endio(struct bio *bio) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private; 111462306a36Sopenharmony_ci unsigned long flags; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci spin_lock_irqsave(&dc->lock, flags); 111762306a36Sopenharmony_ci if (!dc->error) 111862306a36Sopenharmony_ci dc->error = blk_status_to_errno(bio->bi_status); 111962306a36Sopenharmony_ci dc->bio_ref--; 112062306a36Sopenharmony_ci if (!dc->bio_ref && dc->state == D_SUBMIT) { 112162306a36Sopenharmony_ci dc->state = D_DONE; 112262306a36Sopenharmony_ci complete_all(&dc->wait); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci spin_unlock_irqrestore(&dc->lock, flags); 112562306a36Sopenharmony_ci bio_put(bio); 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic void __check_sit_bitmap(struct f2fs_sb_info *sbi, 112962306a36Sopenharmony_ci block_t start, block_t end) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 113262306a36Sopenharmony_ci struct seg_entry *sentry; 113362306a36Sopenharmony_ci unsigned int segno; 113462306a36Sopenharmony_ci block_t blk = start; 113562306a36Sopenharmony_ci unsigned long offset, size, max_blocks = sbi->blocks_per_seg; 113662306a36Sopenharmony_ci unsigned long *map; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci while (blk < end) { 113962306a36Sopenharmony_ci segno = GET_SEGNO(sbi, blk); 114062306a36Sopenharmony_ci sentry = get_seg_entry(sbi, segno); 114162306a36Sopenharmony_ci offset = GET_BLKOFF_FROM_SEG0(sbi, blk); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (end < START_BLOCK(sbi, segno + 1)) 114462306a36Sopenharmony_ci size = GET_BLKOFF_FROM_SEG0(sbi, end); 114562306a36Sopenharmony_ci else 114662306a36Sopenharmony_ci size = max_blocks; 114762306a36Sopenharmony_ci map = (unsigned long *)(sentry->cur_valid_map); 114862306a36Sopenharmony_ci offset = __find_rev_next_bit(map, size, offset); 114962306a36Sopenharmony_ci f2fs_bug_on(sbi, offset != size); 115062306a36Sopenharmony_ci blk = START_BLOCK(sbi, segno + 1); 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci#endif 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_cistatic void __init_discard_policy(struct f2fs_sb_info *sbi, 115662306a36Sopenharmony_ci struct discard_policy *dpolicy, 115762306a36Sopenharmony_ci int discard_type, unsigned int granularity) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* common policy */ 116262306a36Sopenharmony_ci dpolicy->type = discard_type; 116362306a36Sopenharmony_ci dpolicy->sync = true; 116462306a36Sopenharmony_ci dpolicy->ordered = false; 116562306a36Sopenharmony_ci dpolicy->granularity = granularity; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci dpolicy->max_requests = dcc->max_discard_request; 116862306a36Sopenharmony_ci dpolicy->io_aware_gran = dcc->discard_io_aware_gran; 116962306a36Sopenharmony_ci dpolicy->timeout = false; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (discard_type == DPOLICY_BG) { 117262306a36Sopenharmony_ci dpolicy->min_interval = dcc->min_discard_issue_time; 117362306a36Sopenharmony_ci dpolicy->mid_interval = dcc->mid_discard_issue_time; 117462306a36Sopenharmony_ci dpolicy->max_interval = dcc->max_discard_issue_time; 117562306a36Sopenharmony_ci dpolicy->io_aware = true; 117662306a36Sopenharmony_ci dpolicy->sync = false; 117762306a36Sopenharmony_ci dpolicy->ordered = true; 117862306a36Sopenharmony_ci if (utilization(sbi) > dcc->discard_urgent_util) { 117962306a36Sopenharmony_ci dpolicy->granularity = MIN_DISCARD_GRANULARITY; 118062306a36Sopenharmony_ci if (atomic_read(&dcc->discard_cmd_cnt)) 118162306a36Sopenharmony_ci dpolicy->max_interval = 118262306a36Sopenharmony_ci dcc->min_discard_issue_time; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci } else if (discard_type == DPOLICY_FORCE) { 118562306a36Sopenharmony_ci dpolicy->min_interval = dcc->min_discard_issue_time; 118662306a36Sopenharmony_ci dpolicy->mid_interval = dcc->mid_discard_issue_time; 118762306a36Sopenharmony_ci dpolicy->max_interval = dcc->max_discard_issue_time; 118862306a36Sopenharmony_ci dpolicy->io_aware = false; 118962306a36Sopenharmony_ci } else if (discard_type == DPOLICY_FSTRIM) { 119062306a36Sopenharmony_ci dpolicy->io_aware = false; 119162306a36Sopenharmony_ci } else if (discard_type == DPOLICY_UMOUNT) { 119262306a36Sopenharmony_ci dpolicy->io_aware = false; 119362306a36Sopenharmony_ci /* we need to issue all to keep CP_TRIMMED_FLAG */ 119462306a36Sopenharmony_ci dpolicy->granularity = MIN_DISCARD_GRANULARITY; 119562306a36Sopenharmony_ci dpolicy->timeout = true; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_cistatic void __update_discard_tree_range(struct f2fs_sb_info *sbi, 120062306a36Sopenharmony_ci struct block_device *bdev, block_t lstart, 120162306a36Sopenharmony_ci block_t start, block_t len); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 120462306a36Sopenharmony_cistatic void __submit_zone_reset_cmd(struct f2fs_sb_info *sbi, 120562306a36Sopenharmony_ci struct discard_cmd *dc, blk_opf_t flag, 120662306a36Sopenharmony_ci struct list_head *wait_list, 120762306a36Sopenharmony_ci unsigned int *issued) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 121062306a36Sopenharmony_ci struct block_device *bdev = dc->bdev; 121162306a36Sopenharmony_ci struct bio *bio = bio_alloc(bdev, 0, REQ_OP_ZONE_RESET | flag, GFP_NOFS); 121262306a36Sopenharmony_ci unsigned long flags; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci trace_f2fs_issue_reset_zone(bdev, dc->di.start); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci spin_lock_irqsave(&dc->lock, flags); 121762306a36Sopenharmony_ci dc->state = D_SUBMIT; 121862306a36Sopenharmony_ci dc->bio_ref++; 121962306a36Sopenharmony_ci spin_unlock_irqrestore(&dc->lock, flags); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (issued) 122262306a36Sopenharmony_ci (*issued)++; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci atomic_inc(&dcc->queued_discard); 122562306a36Sopenharmony_ci dc->queued++; 122662306a36Sopenharmony_ci list_move_tail(&dc->list, wait_list); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* sanity check on discard range */ 122962306a36Sopenharmony_ci __check_sit_bitmap(sbi, dc->di.lstart, dc->di.lstart + dc->di.len); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(dc->di.start); 123262306a36Sopenharmony_ci bio->bi_private = dc; 123362306a36Sopenharmony_ci bio->bi_end_io = f2fs_submit_discard_endio; 123462306a36Sopenharmony_ci submit_bio(bio); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci atomic_inc(&dcc->issued_discard); 123762306a36Sopenharmony_ci f2fs_update_iostat(sbi, NULL, FS_ZONE_RESET_IO, dc->di.len * F2FS_BLKSIZE); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci#endif 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ 124262306a36Sopenharmony_cistatic int __submit_discard_cmd(struct f2fs_sb_info *sbi, 124362306a36Sopenharmony_ci struct discard_policy *dpolicy, 124462306a36Sopenharmony_ci struct discard_cmd *dc, int *issued) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct block_device *bdev = dc->bdev; 124762306a36Sopenharmony_ci unsigned int max_discard_blocks = 124862306a36Sopenharmony_ci SECTOR_TO_BLOCK(bdev_max_discard_sectors(bdev)); 124962306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 125062306a36Sopenharmony_ci struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? 125162306a36Sopenharmony_ci &(dcc->fstrim_list) : &(dcc->wait_list); 125262306a36Sopenharmony_ci blk_opf_t flag = dpolicy->sync ? REQ_SYNC : 0; 125362306a36Sopenharmony_ci block_t lstart, start, len, total_len; 125462306a36Sopenharmony_ci int err = 0; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (dc->state != D_PREP) 125762306a36Sopenharmony_ci return 0; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) 126062306a36Sopenharmony_ci return 0; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 126362306a36Sopenharmony_ci if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) { 126462306a36Sopenharmony_ci int devi = f2fs_bdev_index(sbi, bdev); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (devi < 0) 126762306a36Sopenharmony_ci return -EINVAL; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci if (f2fs_blkz_is_seq(sbi, devi, dc->di.start)) { 127062306a36Sopenharmony_ci __submit_zone_reset_cmd(sbi, dc, flag, 127162306a36Sopenharmony_ci wait_list, issued); 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci#endif 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci trace_f2fs_issue_discard(bdev, dc->di.start, dc->di.len); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci lstart = dc->di.lstart; 128062306a36Sopenharmony_ci start = dc->di.start; 128162306a36Sopenharmony_ci len = dc->di.len; 128262306a36Sopenharmony_ci total_len = len; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci dc->di.len = 0; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci while (total_len && *issued < dpolicy->max_requests && !err) { 128762306a36Sopenharmony_ci struct bio *bio = NULL; 128862306a36Sopenharmony_ci unsigned long flags; 128962306a36Sopenharmony_ci bool last = true; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (len > max_discard_blocks) { 129262306a36Sopenharmony_ci len = max_discard_blocks; 129362306a36Sopenharmony_ci last = false; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci (*issued)++; 129762306a36Sopenharmony_ci if (*issued == dpolicy->max_requests) 129862306a36Sopenharmony_ci last = true; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci dc->di.len += len; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (time_to_inject(sbi, FAULT_DISCARD)) { 130362306a36Sopenharmony_ci err = -EIO; 130462306a36Sopenharmony_ci } else { 130562306a36Sopenharmony_ci err = __blkdev_issue_discard(bdev, 130662306a36Sopenharmony_ci SECTOR_FROM_BLOCK(start), 130762306a36Sopenharmony_ci SECTOR_FROM_BLOCK(len), 130862306a36Sopenharmony_ci GFP_NOFS, &bio); 130962306a36Sopenharmony_ci } 131062306a36Sopenharmony_ci if (err) { 131162306a36Sopenharmony_ci spin_lock_irqsave(&dc->lock, flags); 131262306a36Sopenharmony_ci if (dc->state == D_PARTIAL) 131362306a36Sopenharmony_ci dc->state = D_SUBMIT; 131462306a36Sopenharmony_ci spin_unlock_irqrestore(&dc->lock, flags); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci break; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci f2fs_bug_on(sbi, !bio); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* 132262306a36Sopenharmony_ci * should keep before submission to avoid D_DONE 132362306a36Sopenharmony_ci * right away 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ci spin_lock_irqsave(&dc->lock, flags); 132662306a36Sopenharmony_ci if (last) 132762306a36Sopenharmony_ci dc->state = D_SUBMIT; 132862306a36Sopenharmony_ci else 132962306a36Sopenharmony_ci dc->state = D_PARTIAL; 133062306a36Sopenharmony_ci dc->bio_ref++; 133162306a36Sopenharmony_ci spin_unlock_irqrestore(&dc->lock, flags); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci atomic_inc(&dcc->queued_discard); 133462306a36Sopenharmony_ci dc->queued++; 133562306a36Sopenharmony_ci list_move_tail(&dc->list, wait_list); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* sanity check on discard range */ 133862306a36Sopenharmony_ci __check_sit_bitmap(sbi, lstart, lstart + len); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci bio->bi_private = dc; 134162306a36Sopenharmony_ci bio->bi_end_io = f2fs_submit_discard_endio; 134262306a36Sopenharmony_ci bio->bi_opf |= flag; 134362306a36Sopenharmony_ci submit_bio(bio); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci atomic_inc(&dcc->issued_discard); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci f2fs_update_iostat(sbi, NULL, FS_DISCARD_IO, len * F2FS_BLKSIZE); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci lstart += len; 135062306a36Sopenharmony_ci start += len; 135162306a36Sopenharmony_ci total_len -= len; 135262306a36Sopenharmony_ci len = total_len; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (!err && len) { 135662306a36Sopenharmony_ci dcc->undiscard_blks -= len; 135762306a36Sopenharmony_ci __update_discard_tree_range(sbi, bdev, lstart, start, len); 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci return err; 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic void __insert_discard_cmd(struct f2fs_sb_info *sbi, 136362306a36Sopenharmony_ci struct block_device *bdev, block_t lstart, 136462306a36Sopenharmony_ci block_t start, block_t len) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 136762306a36Sopenharmony_ci struct rb_node **p = &dcc->root.rb_root.rb_node; 136862306a36Sopenharmony_ci struct rb_node *parent = NULL; 136962306a36Sopenharmony_ci struct discard_cmd *dc; 137062306a36Sopenharmony_ci bool leftmost = true; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* look up rb tree to find parent node */ 137362306a36Sopenharmony_ci while (*p) { 137462306a36Sopenharmony_ci parent = *p; 137562306a36Sopenharmony_ci dc = rb_entry(parent, struct discard_cmd, rb_node); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (lstart < dc->di.lstart) { 137862306a36Sopenharmony_ci p = &(*p)->rb_left; 137962306a36Sopenharmony_ci } else if (lstart >= dc->di.lstart + dc->di.len) { 138062306a36Sopenharmony_ci p = &(*p)->rb_right; 138162306a36Sopenharmony_ci leftmost = false; 138262306a36Sopenharmony_ci } else { 138362306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci dc = __create_discard_cmd(sbi, bdev, lstart, start, len); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci rb_link_node(&dc->rb_node, parent, p); 139062306a36Sopenharmony_ci rb_insert_color_cached(&dc->rb_node, &dcc->root, leftmost); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic void __relocate_discard_cmd(struct discard_cmd_control *dcc, 139462306a36Sopenharmony_ci struct discard_cmd *dc) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci list_move_tail(&dc->list, &dcc->pend_list[plist_idx(dc->di.len)]); 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic void __punch_discard_cmd(struct f2fs_sb_info *sbi, 140062306a36Sopenharmony_ci struct discard_cmd *dc, block_t blkaddr) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 140362306a36Sopenharmony_ci struct discard_info di = dc->di; 140462306a36Sopenharmony_ci bool modified = false; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (dc->state == D_DONE || dc->di.len == 1) { 140762306a36Sopenharmony_ci __remove_discard_cmd(sbi, dc); 140862306a36Sopenharmony_ci return; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci dcc->undiscard_blks -= di.len; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (blkaddr > di.lstart) { 141462306a36Sopenharmony_ci dc->di.len = blkaddr - dc->di.lstart; 141562306a36Sopenharmony_ci dcc->undiscard_blks += dc->di.len; 141662306a36Sopenharmony_ci __relocate_discard_cmd(dcc, dc); 141762306a36Sopenharmony_ci modified = true; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (blkaddr < di.lstart + di.len - 1) { 142162306a36Sopenharmony_ci if (modified) { 142262306a36Sopenharmony_ci __insert_discard_cmd(sbi, dc->bdev, blkaddr + 1, 142362306a36Sopenharmony_ci di.start + blkaddr + 1 - di.lstart, 142462306a36Sopenharmony_ci di.lstart + di.len - 1 - blkaddr); 142562306a36Sopenharmony_ci } else { 142662306a36Sopenharmony_ci dc->di.lstart++; 142762306a36Sopenharmony_ci dc->di.len--; 142862306a36Sopenharmony_ci dc->di.start++; 142962306a36Sopenharmony_ci dcc->undiscard_blks += dc->di.len; 143062306a36Sopenharmony_ci __relocate_discard_cmd(dcc, dc); 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic void __update_discard_tree_range(struct f2fs_sb_info *sbi, 143662306a36Sopenharmony_ci struct block_device *bdev, block_t lstart, 143762306a36Sopenharmony_ci block_t start, block_t len) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 144062306a36Sopenharmony_ci struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 144162306a36Sopenharmony_ci struct discard_cmd *dc; 144262306a36Sopenharmony_ci struct discard_info di = {0}; 144362306a36Sopenharmony_ci struct rb_node **insert_p = NULL, *insert_parent = NULL; 144462306a36Sopenharmony_ci unsigned int max_discard_blocks = 144562306a36Sopenharmony_ci SECTOR_TO_BLOCK(bdev_max_discard_sectors(bdev)); 144662306a36Sopenharmony_ci block_t end = lstart + len; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci dc = __lookup_discard_cmd_ret(&dcc->root, lstart, 144962306a36Sopenharmony_ci &prev_dc, &next_dc, &insert_p, &insert_parent); 145062306a36Sopenharmony_ci if (dc) 145162306a36Sopenharmony_ci prev_dc = dc; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (!prev_dc) { 145462306a36Sopenharmony_ci di.lstart = lstart; 145562306a36Sopenharmony_ci di.len = next_dc ? next_dc->di.lstart - lstart : len; 145662306a36Sopenharmony_ci di.len = min(di.len, len); 145762306a36Sopenharmony_ci di.start = start; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci while (1) { 146162306a36Sopenharmony_ci struct rb_node *node; 146262306a36Sopenharmony_ci bool merged = false; 146362306a36Sopenharmony_ci struct discard_cmd *tdc = NULL; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (prev_dc) { 146662306a36Sopenharmony_ci di.lstart = prev_dc->di.lstart + prev_dc->di.len; 146762306a36Sopenharmony_ci if (di.lstart < lstart) 146862306a36Sopenharmony_ci di.lstart = lstart; 146962306a36Sopenharmony_ci if (di.lstart >= end) 147062306a36Sopenharmony_ci break; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (!next_dc || next_dc->di.lstart > end) 147362306a36Sopenharmony_ci di.len = end - di.lstart; 147462306a36Sopenharmony_ci else 147562306a36Sopenharmony_ci di.len = next_dc->di.lstart - di.lstart; 147662306a36Sopenharmony_ci di.start = start + di.lstart - lstart; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (!di.len) 148062306a36Sopenharmony_ci goto next; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (prev_dc && prev_dc->state == D_PREP && 148362306a36Sopenharmony_ci prev_dc->bdev == bdev && 148462306a36Sopenharmony_ci __is_discard_back_mergeable(&di, &prev_dc->di, 148562306a36Sopenharmony_ci max_discard_blocks)) { 148662306a36Sopenharmony_ci prev_dc->di.len += di.len; 148762306a36Sopenharmony_ci dcc->undiscard_blks += di.len; 148862306a36Sopenharmony_ci __relocate_discard_cmd(dcc, prev_dc); 148962306a36Sopenharmony_ci di = prev_dc->di; 149062306a36Sopenharmony_ci tdc = prev_dc; 149162306a36Sopenharmony_ci merged = true; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (next_dc && next_dc->state == D_PREP && 149562306a36Sopenharmony_ci next_dc->bdev == bdev && 149662306a36Sopenharmony_ci __is_discard_front_mergeable(&di, &next_dc->di, 149762306a36Sopenharmony_ci max_discard_blocks)) { 149862306a36Sopenharmony_ci next_dc->di.lstart = di.lstart; 149962306a36Sopenharmony_ci next_dc->di.len += di.len; 150062306a36Sopenharmony_ci next_dc->di.start = di.start; 150162306a36Sopenharmony_ci dcc->undiscard_blks += di.len; 150262306a36Sopenharmony_ci __relocate_discard_cmd(dcc, next_dc); 150362306a36Sopenharmony_ci if (tdc) 150462306a36Sopenharmony_ci __remove_discard_cmd(sbi, tdc); 150562306a36Sopenharmony_ci merged = true; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (!merged) 150962306a36Sopenharmony_ci __insert_discard_cmd(sbi, bdev, 151062306a36Sopenharmony_ci di.lstart, di.start, di.len); 151162306a36Sopenharmony_ci next: 151262306a36Sopenharmony_ci prev_dc = next_dc; 151362306a36Sopenharmony_ci if (!prev_dc) 151462306a36Sopenharmony_ci break; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci node = rb_next(&prev_dc->rb_node); 151762306a36Sopenharmony_ci next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 152262306a36Sopenharmony_cistatic void __queue_zone_reset_cmd(struct f2fs_sb_info *sbi, 152362306a36Sopenharmony_ci struct block_device *bdev, block_t blkstart, block_t lblkstart, 152462306a36Sopenharmony_ci block_t blklen) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci trace_f2fs_queue_reset_zone(bdev, blkstart); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock); 152962306a36Sopenharmony_ci __insert_discard_cmd(sbi, bdev, lblkstart, blkstart, blklen); 153062306a36Sopenharmony_ci mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock); 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci#endif 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_cistatic void __queue_discard_cmd(struct f2fs_sb_info *sbi, 153562306a36Sopenharmony_ci struct block_device *bdev, block_t blkstart, block_t blklen) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci block_t lblkstart = blkstart; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (!f2fs_bdev_support_discard(bdev)) 154062306a36Sopenharmony_ci return; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci trace_f2fs_queue_discard(bdev, blkstart, blklen); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci if (f2fs_is_multi_device(sbi)) { 154562306a36Sopenharmony_ci int devi = f2fs_target_device_index(sbi, blkstart); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci blkstart -= FDEV(devi).start_blk; 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock); 155062306a36Sopenharmony_ci __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen); 155162306a36Sopenharmony_ci mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock); 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic void __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, 155562306a36Sopenharmony_ci struct discard_policy *dpolicy, int *issued) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 155862306a36Sopenharmony_ci struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 155962306a36Sopenharmony_ci struct rb_node **insert_p = NULL, *insert_parent = NULL; 156062306a36Sopenharmony_ci struct discard_cmd *dc; 156162306a36Sopenharmony_ci struct blk_plug plug; 156262306a36Sopenharmony_ci bool io_interrupted = false; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 156562306a36Sopenharmony_ci dc = __lookup_discard_cmd_ret(&dcc->root, dcc->next_pos, 156662306a36Sopenharmony_ci &prev_dc, &next_dc, &insert_p, &insert_parent); 156762306a36Sopenharmony_ci if (!dc) 156862306a36Sopenharmony_ci dc = next_dc; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci blk_start_plug(&plug); 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci while (dc) { 157362306a36Sopenharmony_ci struct rb_node *node; 157462306a36Sopenharmony_ci int err = 0; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (dc->state != D_PREP) 157762306a36Sopenharmony_ci goto next; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (dpolicy->io_aware && !is_idle(sbi, DISCARD_TIME)) { 158062306a36Sopenharmony_ci io_interrupted = true; 158162306a36Sopenharmony_ci break; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci dcc->next_pos = dc->di.lstart + dc->di.len; 158562306a36Sopenharmony_ci err = __submit_discard_cmd(sbi, dpolicy, dc, issued); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (*issued >= dpolicy->max_requests) 158862306a36Sopenharmony_ci break; 158962306a36Sopenharmony_cinext: 159062306a36Sopenharmony_ci node = rb_next(&dc->rb_node); 159162306a36Sopenharmony_ci if (err) 159262306a36Sopenharmony_ci __remove_discard_cmd(sbi, dc); 159362306a36Sopenharmony_ci dc = rb_entry_safe(node, struct discard_cmd, rb_node); 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci blk_finish_plug(&plug); 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci if (!dc) 159962306a36Sopenharmony_ci dcc->next_pos = 0; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (!(*issued) && io_interrupted) 160462306a36Sopenharmony_ci *issued = -1; 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_cistatic unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi, 160762306a36Sopenharmony_ci struct discard_policy *dpolicy); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_cistatic int __issue_discard_cmd(struct f2fs_sb_info *sbi, 161062306a36Sopenharmony_ci struct discard_policy *dpolicy) 161162306a36Sopenharmony_ci{ 161262306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 161362306a36Sopenharmony_ci struct list_head *pend_list; 161462306a36Sopenharmony_ci struct discard_cmd *dc, *tmp; 161562306a36Sopenharmony_ci struct blk_plug plug; 161662306a36Sopenharmony_ci int i, issued; 161762306a36Sopenharmony_ci bool io_interrupted = false; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (dpolicy->timeout) 162062306a36Sopenharmony_ci f2fs_update_time(sbi, UMOUNT_DISCARD_TIMEOUT); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ciretry: 162362306a36Sopenharmony_ci issued = 0; 162462306a36Sopenharmony_ci for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { 162562306a36Sopenharmony_ci if (dpolicy->timeout && 162662306a36Sopenharmony_ci f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) 162762306a36Sopenharmony_ci break; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (i + 1 < dpolicy->granularity) 163062306a36Sopenharmony_ci break; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (i + 1 < dcc->max_ordered_discard && dpolicy->ordered) { 163362306a36Sopenharmony_ci __issue_discard_cmd_orderly(sbi, dpolicy, &issued); 163462306a36Sopenharmony_ci return issued; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci pend_list = &dcc->pend_list[i]; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 164062306a36Sopenharmony_ci if (list_empty(pend_list)) 164162306a36Sopenharmony_ci goto next; 164262306a36Sopenharmony_ci if (unlikely(dcc->rbtree_check)) 164362306a36Sopenharmony_ci f2fs_bug_on(sbi, !f2fs_check_discard_tree(sbi)); 164462306a36Sopenharmony_ci blk_start_plug(&plug); 164562306a36Sopenharmony_ci list_for_each_entry_safe(dc, tmp, pend_list, list) { 164662306a36Sopenharmony_ci f2fs_bug_on(sbi, dc->state != D_PREP); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (dpolicy->timeout && 164962306a36Sopenharmony_ci f2fs_time_over(sbi, UMOUNT_DISCARD_TIMEOUT)) 165062306a36Sopenharmony_ci break; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (dpolicy->io_aware && i < dpolicy->io_aware_gran && 165362306a36Sopenharmony_ci !is_idle(sbi, DISCARD_TIME)) { 165462306a36Sopenharmony_ci io_interrupted = true; 165562306a36Sopenharmony_ci break; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci __submit_discard_cmd(sbi, dpolicy, dc, &issued); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci if (issued >= dpolicy->max_requests) 166162306a36Sopenharmony_ci break; 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci blk_finish_plug(&plug); 166462306a36Sopenharmony_cinext: 166562306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (issued >= dpolicy->max_requests || io_interrupted) 166862306a36Sopenharmony_ci break; 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (dpolicy->type == DPOLICY_UMOUNT && issued) { 167262306a36Sopenharmony_ci __wait_all_discard_cmd(sbi, dpolicy); 167362306a36Sopenharmony_ci goto retry; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci if (!issued && io_interrupted) 167762306a36Sopenharmony_ci issued = -1; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci return issued; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_cistatic bool __drop_discard_cmd(struct f2fs_sb_info *sbi) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 168562306a36Sopenharmony_ci struct list_head *pend_list; 168662306a36Sopenharmony_ci struct discard_cmd *dc, *tmp; 168762306a36Sopenharmony_ci int i; 168862306a36Sopenharmony_ci bool dropped = false; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 169162306a36Sopenharmony_ci for (i = MAX_PLIST_NUM - 1; i >= 0; i--) { 169262306a36Sopenharmony_ci pend_list = &dcc->pend_list[i]; 169362306a36Sopenharmony_ci list_for_each_entry_safe(dc, tmp, pend_list, list) { 169462306a36Sopenharmony_ci f2fs_bug_on(sbi, dc->state != D_PREP); 169562306a36Sopenharmony_ci __remove_discard_cmd(sbi, dc); 169662306a36Sopenharmony_ci dropped = true; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci return dropped; 170262306a36Sopenharmony_ci} 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_civoid f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi) 170562306a36Sopenharmony_ci{ 170662306a36Sopenharmony_ci __drop_discard_cmd(sbi); 170762306a36Sopenharmony_ci} 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_cistatic unsigned int __wait_one_discard_bio(struct f2fs_sb_info *sbi, 171062306a36Sopenharmony_ci struct discard_cmd *dc) 171162306a36Sopenharmony_ci{ 171262306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 171362306a36Sopenharmony_ci unsigned int len = 0; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci wait_for_completion_io(&dc->wait); 171662306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 171762306a36Sopenharmony_ci f2fs_bug_on(sbi, dc->state != D_DONE); 171862306a36Sopenharmony_ci dc->ref--; 171962306a36Sopenharmony_ci if (!dc->ref) { 172062306a36Sopenharmony_ci if (!dc->error) 172162306a36Sopenharmony_ci len = dc->di.len; 172262306a36Sopenharmony_ci __remove_discard_cmd(sbi, dc); 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci return len; 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_cistatic unsigned int __wait_discard_cmd_range(struct f2fs_sb_info *sbi, 173062306a36Sopenharmony_ci struct discard_policy *dpolicy, 173162306a36Sopenharmony_ci block_t start, block_t end) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 173462306a36Sopenharmony_ci struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ? 173562306a36Sopenharmony_ci &(dcc->fstrim_list) : &(dcc->wait_list); 173662306a36Sopenharmony_ci struct discard_cmd *dc = NULL, *iter, *tmp; 173762306a36Sopenharmony_ci unsigned int trimmed = 0; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_cinext: 174062306a36Sopenharmony_ci dc = NULL; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 174362306a36Sopenharmony_ci list_for_each_entry_safe(iter, tmp, wait_list, list) { 174462306a36Sopenharmony_ci if (iter->di.lstart + iter->di.len <= start || 174562306a36Sopenharmony_ci end <= iter->di.lstart) 174662306a36Sopenharmony_ci continue; 174762306a36Sopenharmony_ci if (iter->di.len < dpolicy->granularity) 174862306a36Sopenharmony_ci continue; 174962306a36Sopenharmony_ci if (iter->state == D_DONE && !iter->ref) { 175062306a36Sopenharmony_ci wait_for_completion_io(&iter->wait); 175162306a36Sopenharmony_ci if (!iter->error) 175262306a36Sopenharmony_ci trimmed += iter->di.len; 175362306a36Sopenharmony_ci __remove_discard_cmd(sbi, iter); 175462306a36Sopenharmony_ci } else { 175562306a36Sopenharmony_ci iter->ref++; 175662306a36Sopenharmony_ci dc = iter; 175762306a36Sopenharmony_ci break; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (dc) { 176362306a36Sopenharmony_ci trimmed += __wait_one_discard_bio(sbi, dc); 176462306a36Sopenharmony_ci goto next; 176562306a36Sopenharmony_ci } 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci return trimmed; 176862306a36Sopenharmony_ci} 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_cistatic unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi, 177162306a36Sopenharmony_ci struct discard_policy *dpolicy) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci struct discard_policy dp; 177462306a36Sopenharmony_ci unsigned int discard_blks; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci if (dpolicy) 177762306a36Sopenharmony_ci return __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX); 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci /* wait all */ 178062306a36Sopenharmony_ci __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, MIN_DISCARD_GRANULARITY); 178162306a36Sopenharmony_ci discard_blks = __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); 178262306a36Sopenharmony_ci __init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, MIN_DISCARD_GRANULARITY); 178362306a36Sopenharmony_ci discard_blks += __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci return discard_blks; 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci/* This should be covered by global mutex, &sit_i->sentry_lock */ 178962306a36Sopenharmony_cistatic void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) 179062306a36Sopenharmony_ci{ 179162306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 179262306a36Sopenharmony_ci struct discard_cmd *dc; 179362306a36Sopenharmony_ci bool need_wait = false; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 179662306a36Sopenharmony_ci dc = __lookup_discard_cmd(sbi, blkaddr); 179762306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 179862306a36Sopenharmony_ci if (dc && f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(dc->bdev)) { 179962306a36Sopenharmony_ci int devi = f2fs_bdev_index(sbi, dc->bdev); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (devi < 0) { 180262306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 180362306a36Sopenharmony_ci return; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci if (f2fs_blkz_is_seq(sbi, devi, dc->di.start)) { 180762306a36Sopenharmony_ci /* force submit zone reset */ 180862306a36Sopenharmony_ci if (dc->state == D_PREP) 180962306a36Sopenharmony_ci __submit_zone_reset_cmd(sbi, dc, REQ_SYNC, 181062306a36Sopenharmony_ci &dcc->wait_list, NULL); 181162306a36Sopenharmony_ci dc->ref++; 181262306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 181362306a36Sopenharmony_ci /* wait zone reset */ 181462306a36Sopenharmony_ci __wait_one_discard_bio(sbi, dc); 181562306a36Sopenharmony_ci return; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci#endif 181962306a36Sopenharmony_ci if (dc) { 182062306a36Sopenharmony_ci if (dc->state == D_PREP) { 182162306a36Sopenharmony_ci __punch_discard_cmd(sbi, dc, blkaddr); 182262306a36Sopenharmony_ci } else { 182362306a36Sopenharmony_ci dc->ref++; 182462306a36Sopenharmony_ci need_wait = true; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (need_wait) 183062306a36Sopenharmony_ci __wait_one_discard_bio(sbi, dc); 183162306a36Sopenharmony_ci} 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_civoid f2fs_stop_discard_thread(struct f2fs_sb_info *sbi) 183462306a36Sopenharmony_ci{ 183562306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci if (dcc && dcc->f2fs_issue_discard) { 183862306a36Sopenharmony_ci struct task_struct *discard_thread = dcc->f2fs_issue_discard; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci dcc->f2fs_issue_discard = NULL; 184162306a36Sopenharmony_ci kthread_stop(discard_thread); 184262306a36Sopenharmony_ci } 184362306a36Sopenharmony_ci} 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci/** 184662306a36Sopenharmony_ci * f2fs_issue_discard_timeout() - Issue all discard cmd within UMOUNT_DISCARD_TIMEOUT 184762306a36Sopenharmony_ci * @sbi: the f2fs_sb_info data for discard cmd to issue 184862306a36Sopenharmony_ci * 184962306a36Sopenharmony_ci * When UMOUNT_DISCARD_TIMEOUT is exceeded, all remaining discard commands will be dropped 185062306a36Sopenharmony_ci * 185162306a36Sopenharmony_ci * Return true if issued all discard cmd or no discard cmd need issue, otherwise return false. 185262306a36Sopenharmony_ci */ 185362306a36Sopenharmony_cibool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) 185462306a36Sopenharmony_ci{ 185562306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 185662306a36Sopenharmony_ci struct discard_policy dpolicy; 185762306a36Sopenharmony_ci bool dropped; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci if (!atomic_read(&dcc->discard_cmd_cnt)) 186062306a36Sopenharmony_ci return true; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT, 186362306a36Sopenharmony_ci dcc->discard_granularity); 186462306a36Sopenharmony_ci __issue_discard_cmd(sbi, &dpolicy); 186562306a36Sopenharmony_ci dropped = __drop_discard_cmd(sbi); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci /* just to make sure there is no pending discard commands */ 186862306a36Sopenharmony_ci __wait_all_discard_cmd(sbi, NULL); 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt)); 187162306a36Sopenharmony_ci return !dropped; 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_cistatic int issue_discard_thread(void *data) 187562306a36Sopenharmony_ci{ 187662306a36Sopenharmony_ci struct f2fs_sb_info *sbi = data; 187762306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 187862306a36Sopenharmony_ci wait_queue_head_t *q = &dcc->discard_wait_queue; 187962306a36Sopenharmony_ci struct discard_policy dpolicy; 188062306a36Sopenharmony_ci unsigned int wait_ms = dcc->min_discard_issue_time; 188162306a36Sopenharmony_ci int issued; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci set_freezable(); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci do { 188662306a36Sopenharmony_ci wait_event_interruptible_timeout(*q, 188762306a36Sopenharmony_ci kthread_should_stop() || freezing(current) || 188862306a36Sopenharmony_ci dcc->discard_wake, 188962306a36Sopenharmony_ci msecs_to_jiffies(wait_ms)); 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (sbi->gc_mode == GC_URGENT_HIGH || 189262306a36Sopenharmony_ci !f2fs_available_free_memory(sbi, DISCARD_CACHE)) 189362306a36Sopenharmony_ci __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 189462306a36Sopenharmony_ci MIN_DISCARD_GRANULARITY); 189562306a36Sopenharmony_ci else 189662306a36Sopenharmony_ci __init_discard_policy(sbi, &dpolicy, DPOLICY_BG, 189762306a36Sopenharmony_ci dcc->discard_granularity); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci if (dcc->discard_wake) 190062306a36Sopenharmony_ci dcc->discard_wake = false; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci /* clean up pending candidates before going to sleep */ 190362306a36Sopenharmony_ci if (atomic_read(&dcc->queued_discard)) 190462306a36Sopenharmony_ci __wait_all_discard_cmd(sbi, NULL); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci if (try_to_freeze()) 190762306a36Sopenharmony_ci continue; 190862306a36Sopenharmony_ci if (f2fs_readonly(sbi->sb)) 190962306a36Sopenharmony_ci continue; 191062306a36Sopenharmony_ci if (kthread_should_stop()) 191162306a36Sopenharmony_ci return 0; 191262306a36Sopenharmony_ci if (is_sbi_flag_set(sbi, SBI_NEED_FSCK) || 191362306a36Sopenharmony_ci !atomic_read(&dcc->discard_cmd_cnt)) { 191462306a36Sopenharmony_ci wait_ms = dpolicy.max_interval; 191562306a36Sopenharmony_ci continue; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci sb_start_intwrite(sbi->sb); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci issued = __issue_discard_cmd(sbi, &dpolicy); 192162306a36Sopenharmony_ci if (issued > 0) { 192262306a36Sopenharmony_ci __wait_all_discard_cmd(sbi, &dpolicy); 192362306a36Sopenharmony_ci wait_ms = dpolicy.min_interval; 192462306a36Sopenharmony_ci } else if (issued == -1) { 192562306a36Sopenharmony_ci wait_ms = f2fs_time_to_wait(sbi, DISCARD_TIME); 192662306a36Sopenharmony_ci if (!wait_ms) 192762306a36Sopenharmony_ci wait_ms = dpolicy.mid_interval; 192862306a36Sopenharmony_ci } else { 192962306a36Sopenharmony_ci wait_ms = dpolicy.max_interval; 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci if (!atomic_read(&dcc->discard_cmd_cnt)) 193262306a36Sopenharmony_ci wait_ms = dpolicy.max_interval; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci sb_end_intwrite(sbi->sb); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci } while (!kthread_should_stop()); 193762306a36Sopenharmony_ci return 0; 193862306a36Sopenharmony_ci} 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 194162306a36Sopenharmony_cistatic int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, 194262306a36Sopenharmony_ci struct block_device *bdev, block_t blkstart, block_t blklen) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci sector_t sector, nr_sects; 194562306a36Sopenharmony_ci block_t lblkstart = blkstart; 194662306a36Sopenharmony_ci int devi = 0; 194762306a36Sopenharmony_ci u64 remainder = 0; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (f2fs_is_multi_device(sbi)) { 195062306a36Sopenharmony_ci devi = f2fs_target_device_index(sbi, blkstart); 195162306a36Sopenharmony_ci if (blkstart < FDEV(devi).start_blk || 195262306a36Sopenharmony_ci blkstart > FDEV(devi).end_blk) { 195362306a36Sopenharmony_ci f2fs_err(sbi, "Invalid block %x", blkstart); 195462306a36Sopenharmony_ci return -EIO; 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci blkstart -= FDEV(devi).start_blk; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci /* For sequential zones, reset the zone write pointer */ 196062306a36Sopenharmony_ci if (f2fs_blkz_is_seq(sbi, devi, blkstart)) { 196162306a36Sopenharmony_ci sector = SECTOR_FROM_BLOCK(blkstart); 196262306a36Sopenharmony_ci nr_sects = SECTOR_FROM_BLOCK(blklen); 196362306a36Sopenharmony_ci div64_u64_rem(sector, bdev_zone_sectors(bdev), &remainder); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci if (remainder || nr_sects != bdev_zone_sectors(bdev)) { 196662306a36Sopenharmony_ci f2fs_err(sbi, "(%d) %s: Unaligned zone reset attempted (block %x + %x)", 196762306a36Sopenharmony_ci devi, sbi->s_ndevs ? FDEV(devi).path : "", 196862306a36Sopenharmony_ci blkstart, blklen); 196962306a36Sopenharmony_ci return -EIO; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) { 197362306a36Sopenharmony_ci trace_f2fs_issue_reset_zone(bdev, blkstart); 197462306a36Sopenharmony_ci return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET, 197562306a36Sopenharmony_ci sector, nr_sects, GFP_NOFS); 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci __queue_zone_reset_cmd(sbi, bdev, blkstart, lblkstart, blklen); 197962306a36Sopenharmony_ci return 0; 198062306a36Sopenharmony_ci } 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci /* For conventional zones, use regular discard if supported */ 198362306a36Sopenharmony_ci __queue_discard_cmd(sbi, bdev, lblkstart, blklen); 198462306a36Sopenharmony_ci return 0; 198562306a36Sopenharmony_ci} 198662306a36Sopenharmony_ci#endif 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_cistatic int __issue_discard_async(struct f2fs_sb_info *sbi, 198962306a36Sopenharmony_ci struct block_device *bdev, block_t blkstart, block_t blklen) 199062306a36Sopenharmony_ci{ 199162306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 199262306a36Sopenharmony_ci if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) 199362306a36Sopenharmony_ci return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); 199462306a36Sopenharmony_ci#endif 199562306a36Sopenharmony_ci __queue_discard_cmd(sbi, bdev, blkstart, blklen); 199662306a36Sopenharmony_ci return 0; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_cistatic int f2fs_issue_discard(struct f2fs_sb_info *sbi, 200062306a36Sopenharmony_ci block_t blkstart, block_t blklen) 200162306a36Sopenharmony_ci{ 200262306a36Sopenharmony_ci sector_t start = blkstart, len = 0; 200362306a36Sopenharmony_ci struct block_device *bdev; 200462306a36Sopenharmony_ci struct seg_entry *se; 200562306a36Sopenharmony_ci unsigned int offset; 200662306a36Sopenharmony_ci block_t i; 200762306a36Sopenharmony_ci int err = 0; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci bdev = f2fs_target_device(sbi, blkstart, NULL); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci for (i = blkstart; i < blkstart + blklen; i++, len++) { 201262306a36Sopenharmony_ci if (i != start) { 201362306a36Sopenharmony_ci struct block_device *bdev2 = 201462306a36Sopenharmony_ci f2fs_target_device(sbi, i, NULL); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci if (bdev2 != bdev) { 201762306a36Sopenharmony_ci err = __issue_discard_async(sbi, bdev, 201862306a36Sopenharmony_ci start, len); 201962306a36Sopenharmony_ci if (err) 202062306a36Sopenharmony_ci return err; 202162306a36Sopenharmony_ci bdev = bdev2; 202262306a36Sopenharmony_ci start = i; 202362306a36Sopenharmony_ci len = 0; 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci se = get_seg_entry(sbi, GET_SEGNO(sbi, i)); 202862306a36Sopenharmony_ci offset = GET_BLKOFF_FROM_SEG0(sbi, i); 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (f2fs_block_unit_discard(sbi) && 203162306a36Sopenharmony_ci !f2fs_test_and_set_bit(offset, se->discard_map)) 203262306a36Sopenharmony_ci sbi->discard_blks--; 203362306a36Sopenharmony_ci } 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (len) 203662306a36Sopenharmony_ci err = __issue_discard_async(sbi, bdev, start, len); 203762306a36Sopenharmony_ci return err; 203862306a36Sopenharmony_ci} 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_cistatic bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, 204162306a36Sopenharmony_ci bool check_only) 204262306a36Sopenharmony_ci{ 204362306a36Sopenharmony_ci int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 204462306a36Sopenharmony_ci int max_blocks = sbi->blocks_per_seg; 204562306a36Sopenharmony_ci struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); 204662306a36Sopenharmony_ci unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 204762306a36Sopenharmony_ci unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 204862306a36Sopenharmony_ci unsigned long *discard_map = (unsigned long *)se->discard_map; 204962306a36Sopenharmony_ci unsigned long *dmap = SIT_I(sbi)->tmp_map; 205062306a36Sopenharmony_ci unsigned int start = 0, end = -1; 205162306a36Sopenharmony_ci bool force = (cpc->reason & CP_DISCARD); 205262306a36Sopenharmony_ci struct discard_entry *de = NULL; 205362306a36Sopenharmony_ci struct list_head *head = &SM_I(sbi)->dcc_info->entry_list; 205462306a36Sopenharmony_ci int i; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci if (se->valid_blocks == max_blocks || !f2fs_hw_support_discard(sbi) || 205762306a36Sopenharmony_ci !f2fs_block_unit_discard(sbi)) 205862306a36Sopenharmony_ci return false; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci if (!force) { 206162306a36Sopenharmony_ci if (!f2fs_realtime_discard_enable(sbi) || !se->valid_blocks || 206262306a36Sopenharmony_ci SM_I(sbi)->dcc_info->nr_discards >= 206362306a36Sopenharmony_ci SM_I(sbi)->dcc_info->max_discards) 206462306a36Sopenharmony_ci return false; 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ 206862306a36Sopenharmony_ci for (i = 0; i < entries; i++) 206962306a36Sopenharmony_ci dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : 207062306a36Sopenharmony_ci (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci while (force || SM_I(sbi)->dcc_info->nr_discards <= 207362306a36Sopenharmony_ci SM_I(sbi)->dcc_info->max_discards) { 207462306a36Sopenharmony_ci start = __find_rev_next_bit(dmap, max_blocks, end + 1); 207562306a36Sopenharmony_ci if (start >= max_blocks) 207662306a36Sopenharmony_ci break; 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci end = __find_rev_next_zero_bit(dmap, max_blocks, start + 1); 207962306a36Sopenharmony_ci if (force && start && end != max_blocks 208062306a36Sopenharmony_ci && (end - start) < cpc->trim_minlen) 208162306a36Sopenharmony_ci continue; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci if (check_only) 208462306a36Sopenharmony_ci return true; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci if (!de) { 208762306a36Sopenharmony_ci de = f2fs_kmem_cache_alloc(discard_entry_slab, 208862306a36Sopenharmony_ci GFP_F2FS_ZERO, true, NULL); 208962306a36Sopenharmony_ci de->start_blkaddr = START_BLOCK(sbi, cpc->trim_start); 209062306a36Sopenharmony_ci list_add_tail(&de->list, head); 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci for (i = start; i < end; i++) 209462306a36Sopenharmony_ci __set_bit_le(i, (void *)de->discard_map); 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci SM_I(sbi)->dcc_info->nr_discards += end - start; 209762306a36Sopenharmony_ci } 209862306a36Sopenharmony_ci return false; 209962306a36Sopenharmony_ci} 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_cistatic void release_discard_addr(struct discard_entry *entry) 210262306a36Sopenharmony_ci{ 210362306a36Sopenharmony_ci list_del(&entry->list); 210462306a36Sopenharmony_ci kmem_cache_free(discard_entry_slab, entry); 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_civoid f2fs_release_discard_addrs(struct f2fs_sb_info *sbi) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list); 211062306a36Sopenharmony_ci struct discard_entry *entry, *this; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci /* drop caches */ 211362306a36Sopenharmony_ci list_for_each_entry_safe(entry, this, head, list) 211462306a36Sopenharmony_ci release_discard_addr(entry); 211562306a36Sopenharmony_ci} 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci/* 211862306a36Sopenharmony_ci * Should call f2fs_clear_prefree_segments after checkpoint is done. 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_cistatic void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) 212162306a36Sopenharmony_ci{ 212262306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 212362306a36Sopenharmony_ci unsigned int segno; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 212662306a36Sopenharmony_ci for_each_set_bit(segno, dirty_i->dirty_segmap[PRE], MAIN_SEGS(sbi)) 212762306a36Sopenharmony_ci __set_test_and_free(sbi, segno, false); 212862306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 212962306a36Sopenharmony_ci} 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_civoid f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, 213262306a36Sopenharmony_ci struct cp_control *cpc) 213362306a36Sopenharmony_ci{ 213462306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 213562306a36Sopenharmony_ci struct list_head *head = &dcc->entry_list; 213662306a36Sopenharmony_ci struct discard_entry *entry, *this; 213762306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 213862306a36Sopenharmony_ci unsigned long *prefree_map = dirty_i->dirty_segmap[PRE]; 213962306a36Sopenharmony_ci unsigned int start = 0, end = -1; 214062306a36Sopenharmony_ci unsigned int secno, start_segno; 214162306a36Sopenharmony_ci bool force = (cpc->reason & CP_DISCARD); 214262306a36Sopenharmony_ci bool section_alignment = F2FS_OPTION(sbi).discard_unit == 214362306a36Sopenharmony_ci DISCARD_UNIT_SECTION; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci if (f2fs_lfs_mode(sbi) && __is_large_section(sbi)) 214662306a36Sopenharmony_ci section_alignment = true; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci while (1) { 215162306a36Sopenharmony_ci int i; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci if (section_alignment && end != -1) 215462306a36Sopenharmony_ci end--; 215562306a36Sopenharmony_ci start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1); 215662306a36Sopenharmony_ci if (start >= MAIN_SEGS(sbi)) 215762306a36Sopenharmony_ci break; 215862306a36Sopenharmony_ci end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi), 215962306a36Sopenharmony_ci start + 1); 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci if (section_alignment) { 216262306a36Sopenharmony_ci start = rounddown(start, sbi->segs_per_sec); 216362306a36Sopenharmony_ci end = roundup(end, sbi->segs_per_sec); 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci for (i = start; i < end; i++) { 216762306a36Sopenharmony_ci if (test_and_clear_bit(i, prefree_map)) 216862306a36Sopenharmony_ci dirty_i->nr_dirty[PRE]--; 216962306a36Sopenharmony_ci } 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if (!f2fs_realtime_discard_enable(sbi)) 217262306a36Sopenharmony_ci continue; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (force && start >= cpc->trim_start && 217562306a36Sopenharmony_ci (end - 1) <= cpc->trim_end) 217662306a36Sopenharmony_ci continue; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci /* Should cover 2MB zoned device for zone-based reset */ 217962306a36Sopenharmony_ci if (!f2fs_sb_has_blkzoned(sbi) && 218062306a36Sopenharmony_ci (!f2fs_lfs_mode(sbi) || !__is_large_section(sbi))) { 218162306a36Sopenharmony_ci f2fs_issue_discard(sbi, START_BLOCK(sbi, start), 218262306a36Sopenharmony_ci (end - start) << sbi->log_blocks_per_seg); 218362306a36Sopenharmony_ci continue; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_cinext: 218662306a36Sopenharmony_ci secno = GET_SEC_FROM_SEG(sbi, start); 218762306a36Sopenharmony_ci start_segno = GET_SEG_FROM_SEC(sbi, secno); 218862306a36Sopenharmony_ci if (!IS_CURSEC(sbi, secno) && 218962306a36Sopenharmony_ci !get_valid_blocks(sbi, start, true)) 219062306a36Sopenharmony_ci f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno), 219162306a36Sopenharmony_ci sbi->segs_per_sec << sbi->log_blocks_per_seg); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci start = start_segno + sbi->segs_per_sec; 219462306a36Sopenharmony_ci if (start < end) 219562306a36Sopenharmony_ci goto next; 219662306a36Sopenharmony_ci else 219762306a36Sopenharmony_ci end = start - 1; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci if (!f2fs_block_unit_discard(sbi)) 220262306a36Sopenharmony_ci goto wakeup; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* send small discards */ 220562306a36Sopenharmony_ci list_for_each_entry_safe(entry, this, head, list) { 220662306a36Sopenharmony_ci unsigned int cur_pos = 0, next_pos, len, total_len = 0; 220762306a36Sopenharmony_ci bool is_valid = test_bit_le(0, entry->discard_map); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_cifind_next: 221062306a36Sopenharmony_ci if (is_valid) { 221162306a36Sopenharmony_ci next_pos = find_next_zero_bit_le(entry->discard_map, 221262306a36Sopenharmony_ci sbi->blocks_per_seg, cur_pos); 221362306a36Sopenharmony_ci len = next_pos - cur_pos; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci if (f2fs_sb_has_blkzoned(sbi) || 221662306a36Sopenharmony_ci (force && len < cpc->trim_minlen)) 221762306a36Sopenharmony_ci goto skip; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, 222062306a36Sopenharmony_ci len); 222162306a36Sopenharmony_ci total_len += len; 222262306a36Sopenharmony_ci } else { 222362306a36Sopenharmony_ci next_pos = find_next_bit_le(entry->discard_map, 222462306a36Sopenharmony_ci sbi->blocks_per_seg, cur_pos); 222562306a36Sopenharmony_ci } 222662306a36Sopenharmony_ciskip: 222762306a36Sopenharmony_ci cur_pos = next_pos; 222862306a36Sopenharmony_ci is_valid = !is_valid; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci if (cur_pos < sbi->blocks_per_seg) 223162306a36Sopenharmony_ci goto find_next; 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci release_discard_addr(entry); 223462306a36Sopenharmony_ci dcc->nr_discards -= total_len; 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ciwakeup: 223862306a36Sopenharmony_ci wake_up_discard_thread(sbi, false); 223962306a36Sopenharmony_ci} 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ciint f2fs_start_discard_thread(struct f2fs_sb_info *sbi) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci dev_t dev = sbi->sb->s_bdev->bd_dev; 224462306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 224562306a36Sopenharmony_ci int err = 0; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci if (!f2fs_realtime_discard_enable(sbi)) 224862306a36Sopenharmony_ci return 0; 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci dcc->f2fs_issue_discard = kthread_run(issue_discard_thread, sbi, 225162306a36Sopenharmony_ci "f2fs_discard-%u:%u", MAJOR(dev), MINOR(dev)); 225262306a36Sopenharmony_ci if (IS_ERR(dcc->f2fs_issue_discard)) { 225362306a36Sopenharmony_ci err = PTR_ERR(dcc->f2fs_issue_discard); 225462306a36Sopenharmony_ci dcc->f2fs_issue_discard = NULL; 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci return err; 225862306a36Sopenharmony_ci} 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_cistatic int create_discard_cmd_control(struct f2fs_sb_info *sbi) 226162306a36Sopenharmony_ci{ 226262306a36Sopenharmony_ci struct discard_cmd_control *dcc; 226362306a36Sopenharmony_ci int err = 0, i; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci if (SM_I(sbi)->dcc_info) { 226662306a36Sopenharmony_ci dcc = SM_I(sbi)->dcc_info; 226762306a36Sopenharmony_ci goto init_thread; 226862306a36Sopenharmony_ci } 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci dcc = f2fs_kzalloc(sbi, sizeof(struct discard_cmd_control), GFP_KERNEL); 227162306a36Sopenharmony_ci if (!dcc) 227262306a36Sopenharmony_ci return -ENOMEM; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci dcc->discard_io_aware_gran = MAX_PLIST_NUM; 227562306a36Sopenharmony_ci dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY; 227662306a36Sopenharmony_ci dcc->max_ordered_discard = DEFAULT_MAX_ORDERED_DISCARD_GRANULARITY; 227762306a36Sopenharmony_ci if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SEGMENT) 227862306a36Sopenharmony_ci dcc->discard_granularity = sbi->blocks_per_seg; 227962306a36Sopenharmony_ci else if (F2FS_OPTION(sbi).discard_unit == DISCARD_UNIT_SECTION) 228062306a36Sopenharmony_ci dcc->discard_granularity = BLKS_PER_SEC(sbi); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci INIT_LIST_HEAD(&dcc->entry_list); 228362306a36Sopenharmony_ci for (i = 0; i < MAX_PLIST_NUM; i++) 228462306a36Sopenharmony_ci INIT_LIST_HEAD(&dcc->pend_list[i]); 228562306a36Sopenharmony_ci INIT_LIST_HEAD(&dcc->wait_list); 228662306a36Sopenharmony_ci INIT_LIST_HEAD(&dcc->fstrim_list); 228762306a36Sopenharmony_ci mutex_init(&dcc->cmd_lock); 228862306a36Sopenharmony_ci atomic_set(&dcc->issued_discard, 0); 228962306a36Sopenharmony_ci atomic_set(&dcc->queued_discard, 0); 229062306a36Sopenharmony_ci atomic_set(&dcc->discard_cmd_cnt, 0); 229162306a36Sopenharmony_ci dcc->nr_discards = 0; 229262306a36Sopenharmony_ci dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg; 229362306a36Sopenharmony_ci dcc->max_discard_request = DEF_MAX_DISCARD_REQUEST; 229462306a36Sopenharmony_ci dcc->min_discard_issue_time = DEF_MIN_DISCARD_ISSUE_TIME; 229562306a36Sopenharmony_ci dcc->mid_discard_issue_time = DEF_MID_DISCARD_ISSUE_TIME; 229662306a36Sopenharmony_ci dcc->max_discard_issue_time = DEF_MAX_DISCARD_ISSUE_TIME; 229762306a36Sopenharmony_ci dcc->discard_urgent_util = DEF_DISCARD_URGENT_UTIL; 229862306a36Sopenharmony_ci dcc->undiscard_blks = 0; 229962306a36Sopenharmony_ci dcc->next_pos = 0; 230062306a36Sopenharmony_ci dcc->root = RB_ROOT_CACHED; 230162306a36Sopenharmony_ci dcc->rbtree_check = false; 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci init_waitqueue_head(&dcc->discard_wait_queue); 230462306a36Sopenharmony_ci SM_I(sbi)->dcc_info = dcc; 230562306a36Sopenharmony_ciinit_thread: 230662306a36Sopenharmony_ci err = f2fs_start_discard_thread(sbi); 230762306a36Sopenharmony_ci if (err) { 230862306a36Sopenharmony_ci kfree(dcc); 230962306a36Sopenharmony_ci SM_I(sbi)->dcc_info = NULL; 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci return err; 231362306a36Sopenharmony_ci} 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic void destroy_discard_cmd_control(struct f2fs_sb_info *sbi) 231662306a36Sopenharmony_ci{ 231762306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci if (!dcc) 232062306a36Sopenharmony_ci return; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci f2fs_stop_discard_thread(sbi); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci /* 232562306a36Sopenharmony_ci * Recovery can cache discard commands, so in error path of 232662306a36Sopenharmony_ci * fill_super(), it needs to give a chance to handle them. 232762306a36Sopenharmony_ci */ 232862306a36Sopenharmony_ci f2fs_issue_discard_timeout(sbi); 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci kfree(dcc); 233162306a36Sopenharmony_ci SM_I(sbi)->dcc_info = NULL; 233262306a36Sopenharmony_ci} 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_cistatic bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) 233562306a36Sopenharmony_ci{ 233662306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap)) { 233962306a36Sopenharmony_ci sit_i->dirty_sentries++; 234062306a36Sopenharmony_ci return false; 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci return true; 234462306a36Sopenharmony_ci} 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_cistatic void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type, 234762306a36Sopenharmony_ci unsigned int segno, int modified) 234862306a36Sopenharmony_ci{ 234962306a36Sopenharmony_ci struct seg_entry *se = get_seg_entry(sbi, segno); 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci se->type = type; 235262306a36Sopenharmony_ci if (modified) 235362306a36Sopenharmony_ci __mark_sit_entry_dirty(sbi, segno); 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_cistatic inline unsigned long long get_segment_mtime(struct f2fs_sb_info *sbi, 235762306a36Sopenharmony_ci block_t blkaddr) 235862306a36Sopenharmony_ci{ 235962306a36Sopenharmony_ci unsigned int segno = GET_SEGNO(sbi, blkaddr); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci if (segno == NULL_SEGNO) 236262306a36Sopenharmony_ci return 0; 236362306a36Sopenharmony_ci return get_seg_entry(sbi, segno)->mtime; 236462306a36Sopenharmony_ci} 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_cistatic void update_segment_mtime(struct f2fs_sb_info *sbi, block_t blkaddr, 236762306a36Sopenharmony_ci unsigned long long old_mtime) 236862306a36Sopenharmony_ci{ 236962306a36Sopenharmony_ci struct seg_entry *se; 237062306a36Sopenharmony_ci unsigned int segno = GET_SEGNO(sbi, blkaddr); 237162306a36Sopenharmony_ci unsigned long long ctime = get_mtime(sbi, false); 237262306a36Sopenharmony_ci unsigned long long mtime = old_mtime ? old_mtime : ctime; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci if (segno == NULL_SEGNO) 237562306a36Sopenharmony_ci return; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci if (!se->mtime) 238062306a36Sopenharmony_ci se->mtime = mtime; 238162306a36Sopenharmony_ci else 238262306a36Sopenharmony_ci se->mtime = div_u64(se->mtime * se->valid_blocks + mtime, 238362306a36Sopenharmony_ci se->valid_blocks + 1); 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci if (ctime > SIT_I(sbi)->max_mtime) 238662306a36Sopenharmony_ci SIT_I(sbi)->max_mtime = ctime; 238762306a36Sopenharmony_ci} 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_cistatic void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) 239062306a36Sopenharmony_ci{ 239162306a36Sopenharmony_ci struct seg_entry *se; 239262306a36Sopenharmony_ci unsigned int segno, offset; 239362306a36Sopenharmony_ci long int new_vblocks; 239462306a36Sopenharmony_ci bool exist; 239562306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 239662306a36Sopenharmony_ci bool mir_exist; 239762306a36Sopenharmony_ci#endif 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci segno = GET_SEGNO(sbi, blkaddr); 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 240262306a36Sopenharmony_ci new_vblocks = se->valid_blocks + del; 240362306a36Sopenharmony_ci offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci f2fs_bug_on(sbi, (new_vblocks < 0 || 240662306a36Sopenharmony_ci (new_vblocks > f2fs_usable_blks_in_seg(sbi, segno)))); 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci se->valid_blocks = new_vblocks; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci /* Update valid block bitmap */ 241162306a36Sopenharmony_ci if (del > 0) { 241262306a36Sopenharmony_ci exist = f2fs_test_and_set_bit(offset, se->cur_valid_map); 241362306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 241462306a36Sopenharmony_ci mir_exist = f2fs_test_and_set_bit(offset, 241562306a36Sopenharmony_ci se->cur_valid_map_mir); 241662306a36Sopenharmony_ci if (unlikely(exist != mir_exist)) { 241762306a36Sopenharmony_ci f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d", 241862306a36Sopenharmony_ci blkaddr, exist); 241962306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 242062306a36Sopenharmony_ci } 242162306a36Sopenharmony_ci#endif 242262306a36Sopenharmony_ci if (unlikely(exist)) { 242362306a36Sopenharmony_ci f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", 242462306a36Sopenharmony_ci blkaddr); 242562306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 242662306a36Sopenharmony_ci se->valid_blocks--; 242762306a36Sopenharmony_ci del = 0; 242862306a36Sopenharmony_ci } 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (f2fs_block_unit_discard(sbi) && 243162306a36Sopenharmony_ci !f2fs_test_and_set_bit(offset, se->discard_map)) 243262306a36Sopenharmony_ci sbi->discard_blks--; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci /* 243562306a36Sopenharmony_ci * SSR should never reuse block which is checkpointed 243662306a36Sopenharmony_ci * or newly invalidated. 243762306a36Sopenharmony_ci */ 243862306a36Sopenharmony_ci if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) { 243962306a36Sopenharmony_ci if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) 244062306a36Sopenharmony_ci se->ckpt_valid_blocks++; 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci } else { 244362306a36Sopenharmony_ci exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map); 244462306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 244562306a36Sopenharmony_ci mir_exist = f2fs_test_and_clear_bit(offset, 244662306a36Sopenharmony_ci se->cur_valid_map_mir); 244762306a36Sopenharmony_ci if (unlikely(exist != mir_exist)) { 244862306a36Sopenharmony_ci f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d", 244962306a36Sopenharmony_ci blkaddr, exist); 245062306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci#endif 245362306a36Sopenharmony_ci if (unlikely(!exist)) { 245462306a36Sopenharmony_ci f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", 245562306a36Sopenharmony_ci blkaddr); 245662306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 245762306a36Sopenharmony_ci se->valid_blocks++; 245862306a36Sopenharmony_ci del = 0; 245962306a36Sopenharmony_ci } else if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { 246062306a36Sopenharmony_ci /* 246162306a36Sopenharmony_ci * If checkpoints are off, we must not reuse data that 246262306a36Sopenharmony_ci * was used in the previous checkpoint. If it was used 246362306a36Sopenharmony_ci * before, we must track that to know how much space we 246462306a36Sopenharmony_ci * really have. 246562306a36Sopenharmony_ci */ 246662306a36Sopenharmony_ci if (f2fs_test_bit(offset, se->ckpt_valid_map)) { 246762306a36Sopenharmony_ci spin_lock(&sbi->stat_lock); 246862306a36Sopenharmony_ci sbi->unusable_block_count++; 246962306a36Sopenharmony_ci spin_unlock(&sbi->stat_lock); 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci } 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci if (f2fs_block_unit_discard(sbi) && 247462306a36Sopenharmony_ci f2fs_test_and_clear_bit(offset, se->discard_map)) 247562306a36Sopenharmony_ci sbi->discard_blks++; 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci if (!f2fs_test_bit(offset, se->ckpt_valid_map)) 247862306a36Sopenharmony_ci se->ckpt_valid_blocks += del; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci __mark_sit_entry_dirty(sbi, segno); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci /* update total number of valid blocks to be written in ckpt area */ 248362306a36Sopenharmony_ci SIT_I(sbi)->written_valid_blocks += del; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci if (__is_large_section(sbi)) 248662306a36Sopenharmony_ci get_sec_entry(sbi, segno)->valid_blocks += del; 248762306a36Sopenharmony_ci} 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_civoid f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) 249062306a36Sopenharmony_ci{ 249162306a36Sopenharmony_ci unsigned int segno = GET_SEGNO(sbi, addr); 249262306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci f2fs_bug_on(sbi, addr == NULL_ADDR); 249562306a36Sopenharmony_ci if (addr == NEW_ADDR || addr == COMPRESS_ADDR) 249662306a36Sopenharmony_ci return; 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci f2fs_invalidate_internal_cache(sbi, addr); 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_ci /* add it into sit main buffer */ 250162306a36Sopenharmony_ci down_write(&sit_i->sentry_lock); 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci update_segment_mtime(sbi, addr, 0); 250462306a36Sopenharmony_ci update_sit_entry(sbi, addr, -1); 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci /* add it into dirty seglist */ 250762306a36Sopenharmony_ci locate_dirty_segment(sbi, segno); 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci up_write(&sit_i->sentry_lock); 251062306a36Sopenharmony_ci} 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_cibool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) 251362306a36Sopenharmony_ci{ 251462306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 251562306a36Sopenharmony_ci unsigned int segno, offset; 251662306a36Sopenharmony_ci struct seg_entry *se; 251762306a36Sopenharmony_ci bool is_cp = false; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (!__is_valid_data_blkaddr(blkaddr)) 252062306a36Sopenharmony_ci return true; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci down_read(&sit_i->sentry_lock); 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci segno = GET_SEGNO(sbi, blkaddr); 252562306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 252662306a36Sopenharmony_ci offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr); 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci if (f2fs_test_bit(offset, se->ckpt_valid_map)) 252962306a36Sopenharmony_ci is_cp = true; 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci up_read(&sit_i->sentry_lock); 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci return is_cp; 253462306a36Sopenharmony_ci} 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_cistatic unsigned short f2fs_curseg_valid_blocks(struct f2fs_sb_info *sbi, int type) 253762306a36Sopenharmony_ci{ 253862306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_ci if (sbi->ckpt->alloc_type[type] == SSR) 254162306a36Sopenharmony_ci return sbi->blocks_per_seg; 254262306a36Sopenharmony_ci return curseg->next_blkoff; 254362306a36Sopenharmony_ci} 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci/* 254662306a36Sopenharmony_ci * Calculate the number of current summary pages for writing 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ciint f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) 254962306a36Sopenharmony_ci{ 255062306a36Sopenharmony_ci int valid_sum_count = 0; 255162306a36Sopenharmony_ci int i, sum_in_page; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 255462306a36Sopenharmony_ci if (sbi->ckpt->alloc_type[i] != SSR && for_ra) 255562306a36Sopenharmony_ci valid_sum_count += 255662306a36Sopenharmony_ci le16_to_cpu(F2FS_CKPT(sbi)->cur_data_blkoff[i]); 255762306a36Sopenharmony_ci else 255862306a36Sopenharmony_ci valid_sum_count += f2fs_curseg_valid_blocks(sbi, i); 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE - 256262306a36Sopenharmony_ci SUM_FOOTER_SIZE) / SUMMARY_SIZE; 256362306a36Sopenharmony_ci if (valid_sum_count <= sum_in_page) 256462306a36Sopenharmony_ci return 1; 256562306a36Sopenharmony_ci else if ((valid_sum_count - sum_in_page) <= 256662306a36Sopenharmony_ci (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) 256762306a36Sopenharmony_ci return 2; 256862306a36Sopenharmony_ci return 3; 256962306a36Sopenharmony_ci} 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci/* 257262306a36Sopenharmony_ci * Caller should put this summary page 257362306a36Sopenharmony_ci */ 257462306a36Sopenharmony_cistruct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) 257562306a36Sopenharmony_ci{ 257662306a36Sopenharmony_ci if (unlikely(f2fs_cp_error(sbi))) 257762306a36Sopenharmony_ci return ERR_PTR(-EIO); 257862306a36Sopenharmony_ci return f2fs_get_meta_page_retry(sbi, GET_SUM_BLOCK(sbi, segno)); 257962306a36Sopenharmony_ci} 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_civoid f2fs_update_meta_page(struct f2fs_sb_info *sbi, 258262306a36Sopenharmony_ci void *src, block_t blk_addr) 258362306a36Sopenharmony_ci{ 258462306a36Sopenharmony_ci struct page *page = f2fs_grab_meta_page(sbi, blk_addr); 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci memcpy(page_address(page), src, PAGE_SIZE); 258762306a36Sopenharmony_ci set_page_dirty(page); 258862306a36Sopenharmony_ci f2fs_put_page(page, 1); 258962306a36Sopenharmony_ci} 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_cistatic void write_sum_page(struct f2fs_sb_info *sbi, 259262306a36Sopenharmony_ci struct f2fs_summary_block *sum_blk, block_t blk_addr) 259362306a36Sopenharmony_ci{ 259462306a36Sopenharmony_ci f2fs_update_meta_page(sbi, (void *)sum_blk, blk_addr); 259562306a36Sopenharmony_ci} 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_cistatic void write_current_sum_page(struct f2fs_sb_info *sbi, 259862306a36Sopenharmony_ci int type, block_t blk_addr) 259962306a36Sopenharmony_ci{ 260062306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 260162306a36Sopenharmony_ci struct page *page = f2fs_grab_meta_page(sbi, blk_addr); 260262306a36Sopenharmony_ci struct f2fs_summary_block *src = curseg->sum_blk; 260362306a36Sopenharmony_ci struct f2fs_summary_block *dst; 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci dst = (struct f2fs_summary_block *)page_address(page); 260662306a36Sopenharmony_ci memset(dst, 0, PAGE_SIZE); 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci down_read(&curseg->journal_rwsem); 261162306a36Sopenharmony_ci memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE); 261262306a36Sopenharmony_ci up_read(&curseg->journal_rwsem); 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE); 261562306a36Sopenharmony_ci memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE); 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci set_page_dirty(page); 262062306a36Sopenharmony_ci f2fs_put_page(page, 1); 262162306a36Sopenharmony_ci} 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_cistatic int is_next_segment_free(struct f2fs_sb_info *sbi, 262462306a36Sopenharmony_ci struct curseg_info *curseg, int type) 262562306a36Sopenharmony_ci{ 262662306a36Sopenharmony_ci unsigned int segno = curseg->segno + 1; 262762306a36Sopenharmony_ci struct free_segmap_info *free_i = FREE_I(sbi); 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci if (segno < MAIN_SEGS(sbi) && segno % sbi->segs_per_sec) 263062306a36Sopenharmony_ci return !test_bit(segno, free_i->free_segmap); 263162306a36Sopenharmony_ci return 0; 263262306a36Sopenharmony_ci} 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci/* 263562306a36Sopenharmony_ci * Find a new segment from the free segments bitmap to right order 263662306a36Sopenharmony_ci * This function should be returned with success, otherwise BUG 263762306a36Sopenharmony_ci */ 263862306a36Sopenharmony_cistatic void get_new_segment(struct f2fs_sb_info *sbi, 263962306a36Sopenharmony_ci unsigned int *newseg, bool new_sec, int dir) 264062306a36Sopenharmony_ci{ 264162306a36Sopenharmony_ci struct free_segmap_info *free_i = FREE_I(sbi); 264262306a36Sopenharmony_ci unsigned int segno, secno, zoneno; 264362306a36Sopenharmony_ci unsigned int total_zones = MAIN_SECS(sbi) / sbi->secs_per_zone; 264462306a36Sopenharmony_ci unsigned int hint = GET_SEC_FROM_SEG(sbi, *newseg); 264562306a36Sopenharmony_ci unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg); 264662306a36Sopenharmony_ci unsigned int left_start = hint; 264762306a36Sopenharmony_ci bool init = true; 264862306a36Sopenharmony_ci int go_left = 0; 264962306a36Sopenharmony_ci int i; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci spin_lock(&free_i->segmap_lock); 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { 265462306a36Sopenharmony_ci segno = find_next_zero_bit(free_i->free_segmap, 265562306a36Sopenharmony_ci GET_SEG_FROM_SEC(sbi, hint + 1), *newseg + 1); 265662306a36Sopenharmony_ci if (segno < GET_SEG_FROM_SEC(sbi, hint + 1)) 265762306a36Sopenharmony_ci goto got_it; 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_cifind_other_zone: 266062306a36Sopenharmony_ci secno = find_next_zero_bit(free_i->free_secmap, MAIN_SECS(sbi), hint); 266162306a36Sopenharmony_ci if (secno >= MAIN_SECS(sbi)) { 266262306a36Sopenharmony_ci if (dir == ALLOC_RIGHT) { 266362306a36Sopenharmony_ci secno = find_first_zero_bit(free_i->free_secmap, 266462306a36Sopenharmony_ci MAIN_SECS(sbi)); 266562306a36Sopenharmony_ci f2fs_bug_on(sbi, secno >= MAIN_SECS(sbi)); 266662306a36Sopenharmony_ci } else { 266762306a36Sopenharmony_ci go_left = 1; 266862306a36Sopenharmony_ci left_start = hint - 1; 266962306a36Sopenharmony_ci } 267062306a36Sopenharmony_ci } 267162306a36Sopenharmony_ci if (go_left == 0) 267262306a36Sopenharmony_ci goto skip_left; 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci while (test_bit(left_start, free_i->free_secmap)) { 267562306a36Sopenharmony_ci if (left_start > 0) { 267662306a36Sopenharmony_ci left_start--; 267762306a36Sopenharmony_ci continue; 267862306a36Sopenharmony_ci } 267962306a36Sopenharmony_ci left_start = find_first_zero_bit(free_i->free_secmap, 268062306a36Sopenharmony_ci MAIN_SECS(sbi)); 268162306a36Sopenharmony_ci f2fs_bug_on(sbi, left_start >= MAIN_SECS(sbi)); 268262306a36Sopenharmony_ci break; 268362306a36Sopenharmony_ci } 268462306a36Sopenharmony_ci secno = left_start; 268562306a36Sopenharmony_ciskip_left: 268662306a36Sopenharmony_ci segno = GET_SEG_FROM_SEC(sbi, secno); 268762306a36Sopenharmony_ci zoneno = GET_ZONE_FROM_SEC(sbi, secno); 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci /* give up on finding another zone */ 269062306a36Sopenharmony_ci if (!init) 269162306a36Sopenharmony_ci goto got_it; 269262306a36Sopenharmony_ci if (sbi->secs_per_zone == 1) 269362306a36Sopenharmony_ci goto got_it; 269462306a36Sopenharmony_ci if (zoneno == old_zoneno) 269562306a36Sopenharmony_ci goto got_it; 269662306a36Sopenharmony_ci if (dir == ALLOC_LEFT) { 269762306a36Sopenharmony_ci if (!go_left && zoneno + 1 >= total_zones) 269862306a36Sopenharmony_ci goto got_it; 269962306a36Sopenharmony_ci if (go_left && zoneno == 0) 270062306a36Sopenharmony_ci goto got_it; 270162306a36Sopenharmony_ci } 270262306a36Sopenharmony_ci for (i = 0; i < NR_CURSEG_TYPE; i++) 270362306a36Sopenharmony_ci if (CURSEG_I(sbi, i)->zone == zoneno) 270462306a36Sopenharmony_ci break; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci if (i < NR_CURSEG_TYPE) { 270762306a36Sopenharmony_ci /* zone is in user, try another */ 270862306a36Sopenharmony_ci if (go_left) 270962306a36Sopenharmony_ci hint = zoneno * sbi->secs_per_zone - 1; 271062306a36Sopenharmony_ci else if (zoneno + 1 >= total_zones) 271162306a36Sopenharmony_ci hint = 0; 271262306a36Sopenharmony_ci else 271362306a36Sopenharmony_ci hint = (zoneno + 1) * sbi->secs_per_zone; 271462306a36Sopenharmony_ci init = false; 271562306a36Sopenharmony_ci goto find_other_zone; 271662306a36Sopenharmony_ci } 271762306a36Sopenharmony_cigot_it: 271862306a36Sopenharmony_ci /* set it as dirty segment in free segmap */ 271962306a36Sopenharmony_ci f2fs_bug_on(sbi, test_bit(segno, free_i->free_segmap)); 272062306a36Sopenharmony_ci __set_inuse(sbi, segno); 272162306a36Sopenharmony_ci *newseg = segno; 272262306a36Sopenharmony_ci spin_unlock(&free_i->segmap_lock); 272362306a36Sopenharmony_ci} 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_cistatic void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) 272662306a36Sopenharmony_ci{ 272762306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 272862306a36Sopenharmony_ci struct summary_footer *sum_footer; 272962306a36Sopenharmony_ci unsigned short seg_type = curseg->seg_type; 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci curseg->inited = true; 273262306a36Sopenharmony_ci curseg->segno = curseg->next_segno; 273362306a36Sopenharmony_ci curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno); 273462306a36Sopenharmony_ci curseg->next_blkoff = 0; 273562306a36Sopenharmony_ci curseg->next_segno = NULL_SEGNO; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci sum_footer = &(curseg->sum_blk->footer); 273862306a36Sopenharmony_ci memset(sum_footer, 0, sizeof(struct summary_footer)); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci sanity_check_seg_type(sbi, seg_type); 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci if (IS_DATASEG(seg_type)) 274362306a36Sopenharmony_ci SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); 274462306a36Sopenharmony_ci if (IS_NODESEG(seg_type)) 274562306a36Sopenharmony_ci SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); 274662306a36Sopenharmony_ci __set_sit_entry_type(sbi, seg_type, curseg->segno, modified); 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type) 275062306a36Sopenharmony_ci{ 275162306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 275262306a36Sopenharmony_ci unsigned short seg_type = curseg->seg_type; 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci sanity_check_seg_type(sbi, seg_type); 275562306a36Sopenharmony_ci if (f2fs_need_rand_seg(sbi)) 275662306a36Sopenharmony_ci return get_random_u32_below(MAIN_SECS(sbi) * sbi->segs_per_sec); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci /* if segs_per_sec is large than 1, we need to keep original policy. */ 275962306a36Sopenharmony_ci if (__is_large_section(sbi)) 276062306a36Sopenharmony_ci return curseg->segno; 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci /* inmem log may not locate on any segment after mount */ 276362306a36Sopenharmony_ci if (!curseg->inited) 276462306a36Sopenharmony_ci return 0; 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 276762306a36Sopenharmony_ci return 0; 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci if (test_opt(sbi, NOHEAP) && 277062306a36Sopenharmony_ci (seg_type == CURSEG_HOT_DATA || IS_NODESEG(seg_type))) 277162306a36Sopenharmony_ci return 0; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (SIT_I(sbi)->last_victim[ALLOC_NEXT]) 277462306a36Sopenharmony_ci return SIT_I(sbi)->last_victim[ALLOC_NEXT]; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci /* find segments from 0 to reuse freed segments */ 277762306a36Sopenharmony_ci if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE) 277862306a36Sopenharmony_ci return 0; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci return curseg->segno; 278162306a36Sopenharmony_ci} 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci/* 278462306a36Sopenharmony_ci * Allocate a current working segment. 278562306a36Sopenharmony_ci * This function always allocates a free segment in LFS manner. 278662306a36Sopenharmony_ci */ 278762306a36Sopenharmony_cistatic void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) 278862306a36Sopenharmony_ci{ 278962306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 279062306a36Sopenharmony_ci unsigned short seg_type = curseg->seg_type; 279162306a36Sopenharmony_ci unsigned int segno = curseg->segno; 279262306a36Sopenharmony_ci int dir = ALLOC_LEFT; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci if (curseg->inited) 279562306a36Sopenharmony_ci write_sum_page(sbi, curseg->sum_blk, 279662306a36Sopenharmony_ci GET_SUM_BLOCK(sbi, segno)); 279762306a36Sopenharmony_ci if (seg_type == CURSEG_WARM_DATA || seg_type == CURSEG_COLD_DATA) 279862306a36Sopenharmony_ci dir = ALLOC_RIGHT; 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci if (test_opt(sbi, NOHEAP)) 280162306a36Sopenharmony_ci dir = ALLOC_RIGHT; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci segno = __get_next_segno(sbi, type); 280462306a36Sopenharmony_ci get_new_segment(sbi, &segno, new_sec, dir); 280562306a36Sopenharmony_ci curseg->next_segno = segno; 280662306a36Sopenharmony_ci reset_curseg(sbi, type, 1); 280762306a36Sopenharmony_ci curseg->alloc_type = LFS; 280862306a36Sopenharmony_ci if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK) 280962306a36Sopenharmony_ci curseg->fragment_remained_chunk = 281062306a36Sopenharmony_ci get_random_u32_inclusive(1, sbi->max_fragment_chunk); 281162306a36Sopenharmony_ci} 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_cistatic int __next_free_blkoff(struct f2fs_sb_info *sbi, 281462306a36Sopenharmony_ci int segno, block_t start) 281562306a36Sopenharmony_ci{ 281662306a36Sopenharmony_ci struct seg_entry *se = get_seg_entry(sbi, segno); 281762306a36Sopenharmony_ci int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); 281862306a36Sopenharmony_ci unsigned long *target_map = SIT_I(sbi)->tmp_map; 281962306a36Sopenharmony_ci unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map; 282062306a36Sopenharmony_ci unsigned long *cur_map = (unsigned long *)se->cur_valid_map; 282162306a36Sopenharmony_ci int i; 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci for (i = 0; i < entries; i++) 282462306a36Sopenharmony_ci target_map[i] = ckpt_map[i] | cur_map[i]; 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci return __find_rev_next_zero_bit(target_map, sbi->blocks_per_seg, start); 282762306a36Sopenharmony_ci} 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_cistatic int f2fs_find_next_ssr_block(struct f2fs_sb_info *sbi, 283062306a36Sopenharmony_ci struct curseg_info *seg) 283162306a36Sopenharmony_ci{ 283262306a36Sopenharmony_ci return __next_free_blkoff(sbi, seg->segno, seg->next_blkoff + 1); 283362306a36Sopenharmony_ci} 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_cibool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno) 283662306a36Sopenharmony_ci{ 283762306a36Sopenharmony_ci return __next_free_blkoff(sbi, segno, 0) < sbi->blocks_per_seg; 283862306a36Sopenharmony_ci} 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci/* 284162306a36Sopenharmony_ci * This function always allocates a used segment(from dirty seglist) by SSR 284262306a36Sopenharmony_ci * manner, so it should recover the existing segment information of valid blocks 284362306a36Sopenharmony_ci */ 284462306a36Sopenharmony_cistatic void change_curseg(struct f2fs_sb_info *sbi, int type) 284562306a36Sopenharmony_ci{ 284662306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 284762306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 284862306a36Sopenharmony_ci unsigned int new_segno = curseg->next_segno; 284962306a36Sopenharmony_ci struct f2fs_summary_block *sum_node; 285062306a36Sopenharmony_ci struct page *sum_page; 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci write_sum_page(sbi, curseg->sum_blk, GET_SUM_BLOCK(sbi, curseg->segno)); 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci __set_test_and_inuse(sbi, new_segno); 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 285762306a36Sopenharmony_ci __remove_dirty_segment(sbi, new_segno, PRE); 285862306a36Sopenharmony_ci __remove_dirty_segment(sbi, new_segno, DIRTY); 285962306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci reset_curseg(sbi, type, 1); 286262306a36Sopenharmony_ci curseg->alloc_type = SSR; 286362306a36Sopenharmony_ci curseg->next_blkoff = __next_free_blkoff(sbi, curseg->segno, 0); 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci sum_page = f2fs_get_sum_page(sbi, new_segno); 286662306a36Sopenharmony_ci if (IS_ERR(sum_page)) { 286762306a36Sopenharmony_ci /* GC won't be able to use stale summary pages by cp_error */ 286862306a36Sopenharmony_ci memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE); 286962306a36Sopenharmony_ci return; 287062306a36Sopenharmony_ci } 287162306a36Sopenharmony_ci sum_node = (struct f2fs_summary_block *)page_address(sum_page); 287262306a36Sopenharmony_ci memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); 287362306a36Sopenharmony_ci f2fs_put_page(sum_page, 1); 287462306a36Sopenharmony_ci} 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_cistatic int get_ssr_segment(struct f2fs_sb_info *sbi, int type, 287762306a36Sopenharmony_ci int alloc_mode, unsigned long long age); 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_cistatic void get_atssr_segment(struct f2fs_sb_info *sbi, int type, 288062306a36Sopenharmony_ci int target_type, int alloc_mode, 288162306a36Sopenharmony_ci unsigned long long age) 288262306a36Sopenharmony_ci{ 288362306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci curseg->seg_type = target_type; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci if (get_ssr_segment(sbi, type, alloc_mode, age)) { 288862306a36Sopenharmony_ci struct seg_entry *se = get_seg_entry(sbi, curseg->next_segno); 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci curseg->seg_type = se->type; 289162306a36Sopenharmony_ci change_curseg(sbi, type); 289262306a36Sopenharmony_ci } else { 289362306a36Sopenharmony_ci /* allocate cold segment by default */ 289462306a36Sopenharmony_ci curseg->seg_type = CURSEG_COLD_DATA; 289562306a36Sopenharmony_ci new_curseg(sbi, type, true); 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci stat_inc_seg_type(sbi, curseg); 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_cistatic void __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi) 290162306a36Sopenharmony_ci{ 290262306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_ALL_DATA_ATGC); 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci if (!sbi->am.atgc_enabled) 290562306a36Sopenharmony_ci return; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci f2fs_down_read(&SM_I(sbi)->curseg_lock); 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 291062306a36Sopenharmony_ci down_write(&SIT_I(sbi)->sentry_lock); 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci get_atssr_segment(sbi, CURSEG_ALL_DATA_ATGC, CURSEG_COLD_DATA, SSR, 0); 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci up_write(&SIT_I(sbi)->sentry_lock); 291562306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci f2fs_up_read(&SM_I(sbi)->curseg_lock); 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci} 292062306a36Sopenharmony_civoid f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi) 292162306a36Sopenharmony_ci{ 292262306a36Sopenharmony_ci __f2fs_init_atgc_curseg(sbi); 292362306a36Sopenharmony_ci} 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_cistatic void __f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi, int type) 292662306a36Sopenharmony_ci{ 292762306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 293062306a36Sopenharmony_ci if (!curseg->inited) 293162306a36Sopenharmony_ci goto out; 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci if (get_valid_blocks(sbi, curseg->segno, false)) { 293462306a36Sopenharmony_ci write_sum_page(sbi, curseg->sum_blk, 293562306a36Sopenharmony_ci GET_SUM_BLOCK(sbi, curseg->segno)); 293662306a36Sopenharmony_ci } else { 293762306a36Sopenharmony_ci mutex_lock(&DIRTY_I(sbi)->seglist_lock); 293862306a36Sopenharmony_ci __set_test_and_free(sbi, curseg->segno, true); 293962306a36Sopenharmony_ci mutex_unlock(&DIRTY_I(sbi)->seglist_lock); 294062306a36Sopenharmony_ci } 294162306a36Sopenharmony_ciout: 294262306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 294362306a36Sopenharmony_ci} 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_civoid f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci __f2fs_save_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED); 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci if (sbi->am.atgc_enabled) 295062306a36Sopenharmony_ci __f2fs_save_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC); 295162306a36Sopenharmony_ci} 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_cistatic void __f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi, int type) 295462306a36Sopenharmony_ci{ 295562306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 295862306a36Sopenharmony_ci if (!curseg->inited) 295962306a36Sopenharmony_ci goto out; 296062306a36Sopenharmony_ci if (get_valid_blocks(sbi, curseg->segno, false)) 296162306a36Sopenharmony_ci goto out; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci mutex_lock(&DIRTY_I(sbi)->seglist_lock); 296462306a36Sopenharmony_ci __set_test_and_inuse(sbi, curseg->segno); 296562306a36Sopenharmony_ci mutex_unlock(&DIRTY_I(sbi)->seglist_lock); 296662306a36Sopenharmony_ciout: 296762306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 296862306a36Sopenharmony_ci} 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_civoid f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi) 297162306a36Sopenharmony_ci{ 297262306a36Sopenharmony_ci __f2fs_restore_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED); 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci if (sbi->am.atgc_enabled) 297562306a36Sopenharmony_ci __f2fs_restore_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC); 297662306a36Sopenharmony_ci} 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_cistatic int get_ssr_segment(struct f2fs_sb_info *sbi, int type, 297962306a36Sopenharmony_ci int alloc_mode, unsigned long long age) 298062306a36Sopenharmony_ci{ 298162306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 298262306a36Sopenharmony_ci unsigned segno = NULL_SEGNO; 298362306a36Sopenharmony_ci unsigned short seg_type = curseg->seg_type; 298462306a36Sopenharmony_ci int i, cnt; 298562306a36Sopenharmony_ci bool reversed = false; 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci sanity_check_seg_type(sbi, seg_type); 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci /* f2fs_need_SSR() already forces to do this */ 299062306a36Sopenharmony_ci if (!f2fs_get_victim(sbi, &segno, BG_GC, seg_type, alloc_mode, age)) { 299162306a36Sopenharmony_ci curseg->next_segno = segno; 299262306a36Sopenharmony_ci return 1; 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci /* For node segments, let's do SSR more intensively */ 299662306a36Sopenharmony_ci if (IS_NODESEG(seg_type)) { 299762306a36Sopenharmony_ci if (seg_type >= CURSEG_WARM_NODE) { 299862306a36Sopenharmony_ci reversed = true; 299962306a36Sopenharmony_ci i = CURSEG_COLD_NODE; 300062306a36Sopenharmony_ci } else { 300162306a36Sopenharmony_ci i = CURSEG_HOT_NODE; 300262306a36Sopenharmony_ci } 300362306a36Sopenharmony_ci cnt = NR_CURSEG_NODE_TYPE; 300462306a36Sopenharmony_ci } else { 300562306a36Sopenharmony_ci if (seg_type >= CURSEG_WARM_DATA) { 300662306a36Sopenharmony_ci reversed = true; 300762306a36Sopenharmony_ci i = CURSEG_COLD_DATA; 300862306a36Sopenharmony_ci } else { 300962306a36Sopenharmony_ci i = CURSEG_HOT_DATA; 301062306a36Sopenharmony_ci } 301162306a36Sopenharmony_ci cnt = NR_CURSEG_DATA_TYPE; 301262306a36Sopenharmony_ci } 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci for (; cnt-- > 0; reversed ? i-- : i++) { 301562306a36Sopenharmony_ci if (i == seg_type) 301662306a36Sopenharmony_ci continue; 301762306a36Sopenharmony_ci if (!f2fs_get_victim(sbi, &segno, BG_GC, i, alloc_mode, age)) { 301862306a36Sopenharmony_ci curseg->next_segno = segno; 301962306a36Sopenharmony_ci return 1; 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ci } 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_ci /* find valid_blocks=0 in dirty list */ 302462306a36Sopenharmony_ci if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) { 302562306a36Sopenharmony_ci segno = get_free_segment(sbi); 302662306a36Sopenharmony_ci if (segno != NULL_SEGNO) { 302762306a36Sopenharmony_ci curseg->next_segno = segno; 302862306a36Sopenharmony_ci return 1; 302962306a36Sopenharmony_ci } 303062306a36Sopenharmony_ci } 303162306a36Sopenharmony_ci return 0; 303262306a36Sopenharmony_ci} 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_cistatic bool need_new_seg(struct f2fs_sb_info *sbi, int type) 303562306a36Sopenharmony_ci{ 303662306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) && 303962306a36Sopenharmony_ci curseg->seg_type == CURSEG_WARM_NODE) 304062306a36Sopenharmony_ci return true; 304162306a36Sopenharmony_ci if (curseg->alloc_type == LFS && 304262306a36Sopenharmony_ci is_next_segment_free(sbi, curseg, type) && 304362306a36Sopenharmony_ci likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED))) 304462306a36Sopenharmony_ci return true; 304562306a36Sopenharmony_ci if (!f2fs_need_SSR(sbi) || !get_ssr_segment(sbi, type, SSR, 0)) 304662306a36Sopenharmony_ci return true; 304762306a36Sopenharmony_ci return false; 304862306a36Sopenharmony_ci} 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_civoid f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type, 305162306a36Sopenharmony_ci unsigned int start, unsigned int end) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 305462306a36Sopenharmony_ci unsigned int segno; 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci f2fs_down_read(&SM_I(sbi)->curseg_lock); 305762306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 305862306a36Sopenharmony_ci down_write(&SIT_I(sbi)->sentry_lock); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci segno = CURSEG_I(sbi, type)->segno; 306162306a36Sopenharmony_ci if (segno < start || segno > end) 306262306a36Sopenharmony_ci goto unlock; 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type, SSR, 0)) 306562306a36Sopenharmony_ci change_curseg(sbi, type); 306662306a36Sopenharmony_ci else 306762306a36Sopenharmony_ci new_curseg(sbi, type, true); 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci stat_inc_seg_type(sbi, curseg); 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ci locate_dirty_segment(sbi, segno); 307262306a36Sopenharmony_ciunlock: 307362306a36Sopenharmony_ci up_write(&SIT_I(sbi)->sentry_lock); 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci if (segno != curseg->segno) 307662306a36Sopenharmony_ci f2fs_notice(sbi, "For resize: curseg of type %d: %u ==> %u", 307762306a36Sopenharmony_ci type, segno, curseg->segno); 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 308062306a36Sopenharmony_ci f2fs_up_read(&SM_I(sbi)->curseg_lock); 308162306a36Sopenharmony_ci} 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_cistatic void __allocate_new_segment(struct f2fs_sb_info *sbi, int type, 308462306a36Sopenharmony_ci bool new_sec, bool force) 308562306a36Sopenharmony_ci{ 308662306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 308762306a36Sopenharmony_ci unsigned int old_segno; 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci if (!force && curseg->inited && 309062306a36Sopenharmony_ci !curseg->next_blkoff && 309162306a36Sopenharmony_ci !get_valid_blocks(sbi, curseg->segno, new_sec) && 309262306a36Sopenharmony_ci !get_ckpt_valid_blocks(sbi, curseg->segno, new_sec)) 309362306a36Sopenharmony_ci return; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci old_segno = curseg->segno; 309662306a36Sopenharmony_ci new_curseg(sbi, type, true); 309762306a36Sopenharmony_ci stat_inc_seg_type(sbi, curseg); 309862306a36Sopenharmony_ci locate_dirty_segment(sbi, old_segno); 309962306a36Sopenharmony_ci} 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_civoid f2fs_allocate_new_section(struct f2fs_sb_info *sbi, int type, bool force) 310262306a36Sopenharmony_ci{ 310362306a36Sopenharmony_ci f2fs_down_read(&SM_I(sbi)->curseg_lock); 310462306a36Sopenharmony_ci down_write(&SIT_I(sbi)->sentry_lock); 310562306a36Sopenharmony_ci __allocate_new_segment(sbi, type, true, force); 310662306a36Sopenharmony_ci up_write(&SIT_I(sbi)->sentry_lock); 310762306a36Sopenharmony_ci f2fs_up_read(&SM_I(sbi)->curseg_lock); 310862306a36Sopenharmony_ci} 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_civoid f2fs_allocate_new_segments(struct f2fs_sb_info *sbi) 311162306a36Sopenharmony_ci{ 311262306a36Sopenharmony_ci int i; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci f2fs_down_read(&SM_I(sbi)->curseg_lock); 311562306a36Sopenharmony_ci down_write(&SIT_I(sbi)->sentry_lock); 311662306a36Sopenharmony_ci for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) 311762306a36Sopenharmony_ci __allocate_new_segment(sbi, i, false, false); 311862306a36Sopenharmony_ci up_write(&SIT_I(sbi)->sentry_lock); 311962306a36Sopenharmony_ci f2fs_up_read(&SM_I(sbi)->curseg_lock); 312062306a36Sopenharmony_ci} 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_cibool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, 312362306a36Sopenharmony_ci struct cp_control *cpc) 312462306a36Sopenharmony_ci{ 312562306a36Sopenharmony_ci __u64 trim_start = cpc->trim_start; 312662306a36Sopenharmony_ci bool has_candidate = false; 312762306a36Sopenharmony_ci 312862306a36Sopenharmony_ci down_write(&SIT_I(sbi)->sentry_lock); 312962306a36Sopenharmony_ci for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { 313062306a36Sopenharmony_ci if (add_discard_addrs(sbi, cpc, true)) { 313162306a36Sopenharmony_ci has_candidate = true; 313262306a36Sopenharmony_ci break; 313362306a36Sopenharmony_ci } 313462306a36Sopenharmony_ci } 313562306a36Sopenharmony_ci up_write(&SIT_I(sbi)->sentry_lock); 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci cpc->trim_start = trim_start; 313862306a36Sopenharmony_ci return has_candidate; 313962306a36Sopenharmony_ci} 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_cistatic unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, 314262306a36Sopenharmony_ci struct discard_policy *dpolicy, 314362306a36Sopenharmony_ci unsigned int start, unsigned int end) 314462306a36Sopenharmony_ci{ 314562306a36Sopenharmony_ci struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; 314662306a36Sopenharmony_ci struct discard_cmd *prev_dc = NULL, *next_dc = NULL; 314762306a36Sopenharmony_ci struct rb_node **insert_p = NULL, *insert_parent = NULL; 314862306a36Sopenharmony_ci struct discard_cmd *dc; 314962306a36Sopenharmony_ci struct blk_plug plug; 315062306a36Sopenharmony_ci int issued; 315162306a36Sopenharmony_ci unsigned int trimmed = 0; 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_cinext: 315462306a36Sopenharmony_ci issued = 0; 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci mutex_lock(&dcc->cmd_lock); 315762306a36Sopenharmony_ci if (unlikely(dcc->rbtree_check)) 315862306a36Sopenharmony_ci f2fs_bug_on(sbi, !f2fs_check_discard_tree(sbi)); 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci dc = __lookup_discard_cmd_ret(&dcc->root, start, 316162306a36Sopenharmony_ci &prev_dc, &next_dc, &insert_p, &insert_parent); 316262306a36Sopenharmony_ci if (!dc) 316362306a36Sopenharmony_ci dc = next_dc; 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci blk_start_plug(&plug); 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci while (dc && dc->di.lstart <= end) { 316862306a36Sopenharmony_ci struct rb_node *node; 316962306a36Sopenharmony_ci int err = 0; 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_ci if (dc->di.len < dpolicy->granularity) 317262306a36Sopenharmony_ci goto skip; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci if (dc->state != D_PREP) { 317562306a36Sopenharmony_ci list_move_tail(&dc->list, &dcc->fstrim_list); 317662306a36Sopenharmony_ci goto skip; 317762306a36Sopenharmony_ci } 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci err = __submit_discard_cmd(sbi, dpolicy, dc, &issued); 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci if (issued >= dpolicy->max_requests) { 318262306a36Sopenharmony_ci start = dc->di.lstart + dc->di.len; 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci if (err) 318562306a36Sopenharmony_ci __remove_discard_cmd(sbi, dc); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci blk_finish_plug(&plug); 318862306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 318962306a36Sopenharmony_ci trimmed += __wait_all_discard_cmd(sbi, NULL); 319062306a36Sopenharmony_ci f2fs_io_schedule_timeout(DEFAULT_IO_TIMEOUT); 319162306a36Sopenharmony_ci goto next; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ciskip: 319462306a36Sopenharmony_ci node = rb_next(&dc->rb_node); 319562306a36Sopenharmony_ci if (err) 319662306a36Sopenharmony_ci __remove_discard_cmd(sbi, dc); 319762306a36Sopenharmony_ci dc = rb_entry_safe(node, struct discard_cmd, rb_node); 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci if (fatal_signal_pending(current)) 320062306a36Sopenharmony_ci break; 320162306a36Sopenharmony_ci } 320262306a36Sopenharmony_ci 320362306a36Sopenharmony_ci blk_finish_plug(&plug); 320462306a36Sopenharmony_ci mutex_unlock(&dcc->cmd_lock); 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_ci return trimmed; 320762306a36Sopenharmony_ci} 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_ciint f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) 321062306a36Sopenharmony_ci{ 321162306a36Sopenharmony_ci __u64 start = F2FS_BYTES_TO_BLK(range->start); 321262306a36Sopenharmony_ci __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1; 321362306a36Sopenharmony_ci unsigned int start_segno, end_segno; 321462306a36Sopenharmony_ci block_t start_block, end_block; 321562306a36Sopenharmony_ci struct cp_control cpc; 321662306a36Sopenharmony_ci struct discard_policy dpolicy; 321762306a36Sopenharmony_ci unsigned long long trimmed = 0; 321862306a36Sopenharmony_ci int err = 0; 321962306a36Sopenharmony_ci bool need_align = f2fs_lfs_mode(sbi) && __is_large_section(sbi); 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize) 322262306a36Sopenharmony_ci return -EINVAL; 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci if (end < MAIN_BLKADDR(sbi)) 322562306a36Sopenharmony_ci goto out; 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) { 322862306a36Sopenharmony_ci f2fs_warn(sbi, "Found FS corruption, run fsck to fix."); 322962306a36Sopenharmony_ci return -EFSCORRUPTED; 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci /* start/end segment number in main_area */ 323362306a36Sopenharmony_ci start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start); 323462306a36Sopenharmony_ci end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 : 323562306a36Sopenharmony_ci GET_SEGNO(sbi, end); 323662306a36Sopenharmony_ci if (need_align) { 323762306a36Sopenharmony_ci start_segno = rounddown(start_segno, sbi->segs_per_sec); 323862306a36Sopenharmony_ci end_segno = roundup(end_segno + 1, sbi->segs_per_sec) - 1; 323962306a36Sopenharmony_ci } 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci cpc.reason = CP_DISCARD; 324262306a36Sopenharmony_ci cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen)); 324362306a36Sopenharmony_ci cpc.trim_start = start_segno; 324462306a36Sopenharmony_ci cpc.trim_end = end_segno; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci if (sbi->discard_blks == 0) 324762306a36Sopenharmony_ci goto out; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci f2fs_down_write(&sbi->gc_lock); 325062306a36Sopenharmony_ci stat_inc_cp_call_count(sbi, TOTAL_CALL); 325162306a36Sopenharmony_ci err = f2fs_write_checkpoint(sbi, &cpc); 325262306a36Sopenharmony_ci f2fs_up_write(&sbi->gc_lock); 325362306a36Sopenharmony_ci if (err) 325462306a36Sopenharmony_ci goto out; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci /* 325762306a36Sopenharmony_ci * We filed discard candidates, but actually we don't need to wait for 325862306a36Sopenharmony_ci * all of them, since they'll be issued in idle time along with runtime 325962306a36Sopenharmony_ci * discard option. User configuration looks like using runtime discard 326062306a36Sopenharmony_ci * or periodic fstrim instead of it. 326162306a36Sopenharmony_ci */ 326262306a36Sopenharmony_ci if (f2fs_realtime_discard_enable(sbi)) 326362306a36Sopenharmony_ci goto out; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci start_block = START_BLOCK(sbi, start_segno); 326662306a36Sopenharmony_ci end_block = START_BLOCK(sbi, end_segno + 1); 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen); 326962306a36Sopenharmony_ci trimmed = __issue_discard_cmd_range(sbi, &dpolicy, 327062306a36Sopenharmony_ci start_block, end_block); 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci trimmed += __wait_discard_cmd_range(sbi, &dpolicy, 327362306a36Sopenharmony_ci start_block, end_block); 327462306a36Sopenharmony_ciout: 327562306a36Sopenharmony_ci if (!err) 327662306a36Sopenharmony_ci range->len = F2FS_BLK_TO_BYTES(trimmed); 327762306a36Sopenharmony_ci return err; 327862306a36Sopenharmony_ci} 327962306a36Sopenharmony_ci 328062306a36Sopenharmony_ciint f2fs_rw_hint_to_seg_type(enum rw_hint hint) 328162306a36Sopenharmony_ci{ 328262306a36Sopenharmony_ci switch (hint) { 328362306a36Sopenharmony_ci case WRITE_LIFE_SHORT: 328462306a36Sopenharmony_ci return CURSEG_HOT_DATA; 328562306a36Sopenharmony_ci case WRITE_LIFE_EXTREME: 328662306a36Sopenharmony_ci return CURSEG_COLD_DATA; 328762306a36Sopenharmony_ci default: 328862306a36Sopenharmony_ci return CURSEG_WARM_DATA; 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci} 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_cistatic int __get_segment_type_2(struct f2fs_io_info *fio) 329362306a36Sopenharmony_ci{ 329462306a36Sopenharmony_ci if (fio->type == DATA) 329562306a36Sopenharmony_ci return CURSEG_HOT_DATA; 329662306a36Sopenharmony_ci else 329762306a36Sopenharmony_ci return CURSEG_HOT_NODE; 329862306a36Sopenharmony_ci} 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_cistatic int __get_segment_type_4(struct f2fs_io_info *fio) 330162306a36Sopenharmony_ci{ 330262306a36Sopenharmony_ci if (fio->type == DATA) { 330362306a36Sopenharmony_ci struct inode *inode = fio->page->mapping->host; 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 330662306a36Sopenharmony_ci return CURSEG_HOT_DATA; 330762306a36Sopenharmony_ci else 330862306a36Sopenharmony_ci return CURSEG_COLD_DATA; 330962306a36Sopenharmony_ci } else { 331062306a36Sopenharmony_ci if (IS_DNODE(fio->page) && is_cold_node(fio->page)) 331162306a36Sopenharmony_ci return CURSEG_WARM_NODE; 331262306a36Sopenharmony_ci else 331362306a36Sopenharmony_ci return CURSEG_COLD_NODE; 331462306a36Sopenharmony_ci } 331562306a36Sopenharmony_ci} 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_cistatic int __get_age_segment_type(struct inode *inode, pgoff_t pgofs) 331862306a36Sopenharmony_ci{ 331962306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 332062306a36Sopenharmony_ci struct extent_info ei = {}; 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci if (f2fs_lookup_age_extent_cache(inode, pgofs, &ei)) { 332362306a36Sopenharmony_ci if (!ei.age) 332462306a36Sopenharmony_ci return NO_CHECK_TYPE; 332562306a36Sopenharmony_ci if (ei.age <= sbi->hot_data_age_threshold) 332662306a36Sopenharmony_ci return CURSEG_HOT_DATA; 332762306a36Sopenharmony_ci if (ei.age <= sbi->warm_data_age_threshold) 332862306a36Sopenharmony_ci return CURSEG_WARM_DATA; 332962306a36Sopenharmony_ci return CURSEG_COLD_DATA; 333062306a36Sopenharmony_ci } 333162306a36Sopenharmony_ci return NO_CHECK_TYPE; 333262306a36Sopenharmony_ci} 333362306a36Sopenharmony_ci 333462306a36Sopenharmony_cistatic int __get_segment_type_6(struct f2fs_io_info *fio) 333562306a36Sopenharmony_ci{ 333662306a36Sopenharmony_ci if (fio->type == DATA) { 333762306a36Sopenharmony_ci struct inode *inode = fio->page->mapping->host; 333862306a36Sopenharmony_ci int type; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci if (is_inode_flag_set(inode, FI_ALIGNED_WRITE)) 334162306a36Sopenharmony_ci return CURSEG_COLD_DATA_PINNED; 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci if (page_private_gcing(fio->page)) { 334462306a36Sopenharmony_ci if (fio->sbi->am.atgc_enabled && 334562306a36Sopenharmony_ci (fio->io_type == FS_DATA_IO) && 334662306a36Sopenharmony_ci (fio->sbi->gc_mode != GC_URGENT_HIGH)) 334762306a36Sopenharmony_ci return CURSEG_ALL_DATA_ATGC; 334862306a36Sopenharmony_ci else 334962306a36Sopenharmony_ci return CURSEG_COLD_DATA; 335062306a36Sopenharmony_ci } 335162306a36Sopenharmony_ci if (file_is_cold(inode) || f2fs_need_compress_data(inode)) 335262306a36Sopenharmony_ci return CURSEG_COLD_DATA; 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci type = __get_age_segment_type(inode, fio->page->index); 335562306a36Sopenharmony_ci if (type != NO_CHECK_TYPE) 335662306a36Sopenharmony_ci return type; 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci if (file_is_hot(inode) || 335962306a36Sopenharmony_ci is_inode_flag_set(inode, FI_HOT_DATA) || 336062306a36Sopenharmony_ci f2fs_is_cow_file(inode)) 336162306a36Sopenharmony_ci return CURSEG_HOT_DATA; 336262306a36Sopenharmony_ci return f2fs_rw_hint_to_seg_type(inode->i_write_hint); 336362306a36Sopenharmony_ci } else { 336462306a36Sopenharmony_ci if (IS_DNODE(fio->page)) 336562306a36Sopenharmony_ci return is_cold_node(fio->page) ? CURSEG_WARM_NODE : 336662306a36Sopenharmony_ci CURSEG_HOT_NODE; 336762306a36Sopenharmony_ci return CURSEG_COLD_NODE; 336862306a36Sopenharmony_ci } 336962306a36Sopenharmony_ci} 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_cistatic int __get_segment_type(struct f2fs_io_info *fio) 337262306a36Sopenharmony_ci{ 337362306a36Sopenharmony_ci int type = 0; 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci switch (F2FS_OPTION(fio->sbi).active_logs) { 337662306a36Sopenharmony_ci case 2: 337762306a36Sopenharmony_ci type = __get_segment_type_2(fio); 337862306a36Sopenharmony_ci break; 337962306a36Sopenharmony_ci case 4: 338062306a36Sopenharmony_ci type = __get_segment_type_4(fio); 338162306a36Sopenharmony_ci break; 338262306a36Sopenharmony_ci case 6: 338362306a36Sopenharmony_ci type = __get_segment_type_6(fio); 338462306a36Sopenharmony_ci break; 338562306a36Sopenharmony_ci default: 338662306a36Sopenharmony_ci f2fs_bug_on(fio->sbi, true); 338762306a36Sopenharmony_ci } 338862306a36Sopenharmony_ci 338962306a36Sopenharmony_ci if (IS_HOT(type)) 339062306a36Sopenharmony_ci fio->temp = HOT; 339162306a36Sopenharmony_ci else if (IS_WARM(type)) 339262306a36Sopenharmony_ci fio->temp = WARM; 339362306a36Sopenharmony_ci else 339462306a36Sopenharmony_ci fio->temp = COLD; 339562306a36Sopenharmony_ci return type; 339662306a36Sopenharmony_ci} 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_cistatic void f2fs_randomize_chunk(struct f2fs_sb_info *sbi, 339962306a36Sopenharmony_ci struct curseg_info *seg) 340062306a36Sopenharmony_ci{ 340162306a36Sopenharmony_ci /* To allocate block chunks in different sizes, use random number */ 340262306a36Sopenharmony_ci if (--seg->fragment_remained_chunk > 0) 340362306a36Sopenharmony_ci return; 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci seg->fragment_remained_chunk = 340662306a36Sopenharmony_ci get_random_u32_inclusive(1, sbi->max_fragment_chunk); 340762306a36Sopenharmony_ci seg->next_blkoff += 340862306a36Sopenharmony_ci get_random_u32_inclusive(1, sbi->max_fragment_hole); 340962306a36Sopenharmony_ci} 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_civoid f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page, 341262306a36Sopenharmony_ci block_t old_blkaddr, block_t *new_blkaddr, 341362306a36Sopenharmony_ci struct f2fs_summary *sum, int type, 341462306a36Sopenharmony_ci struct f2fs_io_info *fio) 341562306a36Sopenharmony_ci{ 341662306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 341762306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, type); 341862306a36Sopenharmony_ci unsigned long long old_mtime; 341962306a36Sopenharmony_ci bool from_gc = (type == CURSEG_ALL_DATA_ATGC); 342062306a36Sopenharmony_ci struct seg_entry *se = NULL; 342162306a36Sopenharmony_ci bool segment_full = false; 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci f2fs_down_read(&SM_I(sbi)->curseg_lock); 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 342662306a36Sopenharmony_ci down_write(&sit_i->sentry_lock); 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_ci if (from_gc) { 342962306a36Sopenharmony_ci f2fs_bug_on(sbi, GET_SEGNO(sbi, old_blkaddr) == NULL_SEGNO); 343062306a36Sopenharmony_ci se = get_seg_entry(sbi, GET_SEGNO(sbi, old_blkaddr)); 343162306a36Sopenharmony_ci sanity_check_seg_type(sbi, se->type); 343262306a36Sopenharmony_ci f2fs_bug_on(sbi, IS_NODESEG(se->type)); 343362306a36Sopenharmony_ci } 343462306a36Sopenharmony_ci *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); 343562306a36Sopenharmony_ci 343662306a36Sopenharmony_ci f2fs_bug_on(sbi, curseg->next_blkoff >= sbi->blocks_per_seg); 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci f2fs_wait_discard_bio(sbi, *new_blkaddr); 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci curseg->sum_blk->entries[curseg->next_blkoff] = *sum; 344162306a36Sopenharmony_ci if (curseg->alloc_type == SSR) { 344262306a36Sopenharmony_ci curseg->next_blkoff = f2fs_find_next_ssr_block(sbi, curseg); 344362306a36Sopenharmony_ci } else { 344462306a36Sopenharmony_ci curseg->next_blkoff++; 344562306a36Sopenharmony_ci if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK) 344662306a36Sopenharmony_ci f2fs_randomize_chunk(sbi, curseg); 344762306a36Sopenharmony_ci } 344862306a36Sopenharmony_ci if (curseg->next_blkoff >= f2fs_usable_blks_in_seg(sbi, curseg->segno)) 344962306a36Sopenharmony_ci segment_full = true; 345062306a36Sopenharmony_ci stat_inc_block_count(sbi, curseg); 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci if (from_gc) { 345362306a36Sopenharmony_ci old_mtime = get_segment_mtime(sbi, old_blkaddr); 345462306a36Sopenharmony_ci } else { 345562306a36Sopenharmony_ci update_segment_mtime(sbi, old_blkaddr, 0); 345662306a36Sopenharmony_ci old_mtime = 0; 345762306a36Sopenharmony_ci } 345862306a36Sopenharmony_ci update_segment_mtime(sbi, *new_blkaddr, old_mtime); 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci /* 346162306a36Sopenharmony_ci * SIT information should be updated before segment allocation, 346262306a36Sopenharmony_ci * since SSR needs latest valid block information. 346362306a36Sopenharmony_ci */ 346462306a36Sopenharmony_ci update_sit_entry(sbi, *new_blkaddr, 1); 346562306a36Sopenharmony_ci if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) 346662306a36Sopenharmony_ci update_sit_entry(sbi, old_blkaddr, -1); 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci /* 346962306a36Sopenharmony_ci * If the current segment is full, flush it out and replace it with a 347062306a36Sopenharmony_ci * new segment. 347162306a36Sopenharmony_ci */ 347262306a36Sopenharmony_ci if (segment_full) { 347362306a36Sopenharmony_ci if (from_gc) { 347462306a36Sopenharmony_ci get_atssr_segment(sbi, type, se->type, 347562306a36Sopenharmony_ci AT_SSR, se->mtime); 347662306a36Sopenharmony_ci } else { 347762306a36Sopenharmony_ci if (need_new_seg(sbi, type)) 347862306a36Sopenharmony_ci new_curseg(sbi, type, false); 347962306a36Sopenharmony_ci else 348062306a36Sopenharmony_ci change_curseg(sbi, type); 348162306a36Sopenharmony_ci stat_inc_seg_type(sbi, curseg); 348262306a36Sopenharmony_ci } 348362306a36Sopenharmony_ci } 348462306a36Sopenharmony_ci /* 348562306a36Sopenharmony_ci * segment dirty status should be updated after segment allocation, 348662306a36Sopenharmony_ci * so we just need to update status only one time after previous 348762306a36Sopenharmony_ci * segment being closed. 348862306a36Sopenharmony_ci */ 348962306a36Sopenharmony_ci locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); 349062306a36Sopenharmony_ci locate_dirty_segment(sbi, GET_SEGNO(sbi, *new_blkaddr)); 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_ci if (IS_DATASEG(curseg->seg_type)) 349362306a36Sopenharmony_ci atomic64_inc(&sbi->allocated_data_blocks); 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci up_write(&sit_i->sentry_lock); 349662306a36Sopenharmony_ci 349762306a36Sopenharmony_ci if (page && IS_NODESEG(curseg->seg_type)) { 349862306a36Sopenharmony_ci fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci f2fs_inode_chksum_set(sbi, page); 350162306a36Sopenharmony_ci } 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci if (fio) { 350462306a36Sopenharmony_ci struct f2fs_bio_info *io; 350562306a36Sopenharmony_ci 350662306a36Sopenharmony_ci if (F2FS_IO_ALIGNED(sbi)) 350762306a36Sopenharmony_ci fio->retry = 0; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci INIT_LIST_HEAD(&fio->list); 351062306a36Sopenharmony_ci fio->in_list = 1; 351162306a36Sopenharmony_ci io = sbi->write_io[fio->type] + fio->temp; 351262306a36Sopenharmony_ci spin_lock(&io->io_lock); 351362306a36Sopenharmony_ci list_add_tail(&fio->list, &io->io_list); 351462306a36Sopenharmony_ci spin_unlock(&io->io_lock); 351562306a36Sopenharmony_ci } 351662306a36Sopenharmony_ci 351762306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_ci f2fs_up_read(&SM_I(sbi)->curseg_lock); 352062306a36Sopenharmony_ci} 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_civoid f2fs_update_device_state(struct f2fs_sb_info *sbi, nid_t ino, 352362306a36Sopenharmony_ci block_t blkaddr, unsigned int blkcnt) 352462306a36Sopenharmony_ci{ 352562306a36Sopenharmony_ci if (!f2fs_is_multi_device(sbi)) 352662306a36Sopenharmony_ci return; 352762306a36Sopenharmony_ci 352862306a36Sopenharmony_ci while (1) { 352962306a36Sopenharmony_ci unsigned int devidx = f2fs_target_device_index(sbi, blkaddr); 353062306a36Sopenharmony_ci unsigned int blks = FDEV(devidx).end_blk - blkaddr + 1; 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci /* update device state for fsync */ 353362306a36Sopenharmony_ci f2fs_set_dirty_device(sbi, ino, devidx, FLUSH_INO); 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci /* update device state for checkpoint */ 353662306a36Sopenharmony_ci if (!f2fs_test_bit(devidx, (char *)&sbi->dirty_device)) { 353762306a36Sopenharmony_ci spin_lock(&sbi->dev_lock); 353862306a36Sopenharmony_ci f2fs_set_bit(devidx, (char *)&sbi->dirty_device); 353962306a36Sopenharmony_ci spin_unlock(&sbi->dev_lock); 354062306a36Sopenharmony_ci } 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci if (blkcnt <= blks) 354362306a36Sopenharmony_ci break; 354462306a36Sopenharmony_ci blkcnt -= blks; 354562306a36Sopenharmony_ci blkaddr += blks; 354662306a36Sopenharmony_ci } 354762306a36Sopenharmony_ci} 354862306a36Sopenharmony_ci 354962306a36Sopenharmony_cistatic void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio) 355062306a36Sopenharmony_ci{ 355162306a36Sopenharmony_ci int type = __get_segment_type(fio); 355262306a36Sopenharmony_ci bool keep_order = (f2fs_lfs_mode(fio->sbi) && type == CURSEG_COLD_DATA); 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci if (keep_order) 355562306a36Sopenharmony_ci f2fs_down_read(&fio->sbi->io_order_lock); 355662306a36Sopenharmony_cireallocate: 355762306a36Sopenharmony_ci f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, 355862306a36Sopenharmony_ci &fio->new_blkaddr, sum, type, fio); 355962306a36Sopenharmony_ci if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) 356062306a36Sopenharmony_ci f2fs_invalidate_internal_cache(fio->sbi, fio->old_blkaddr); 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci /* writeout dirty page into bdev */ 356362306a36Sopenharmony_ci f2fs_submit_page_write(fio); 356462306a36Sopenharmony_ci if (fio->retry) { 356562306a36Sopenharmony_ci fio->old_blkaddr = fio->new_blkaddr; 356662306a36Sopenharmony_ci goto reallocate; 356762306a36Sopenharmony_ci } 356862306a36Sopenharmony_ci 356962306a36Sopenharmony_ci f2fs_update_device_state(fio->sbi, fio->ino, fio->new_blkaddr, 1); 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ci if (keep_order) 357262306a36Sopenharmony_ci f2fs_up_read(&fio->sbi->io_order_lock); 357362306a36Sopenharmony_ci} 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_civoid f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page, 357662306a36Sopenharmony_ci enum iostat_type io_type) 357762306a36Sopenharmony_ci{ 357862306a36Sopenharmony_ci struct f2fs_io_info fio = { 357962306a36Sopenharmony_ci .sbi = sbi, 358062306a36Sopenharmony_ci .type = META, 358162306a36Sopenharmony_ci .temp = HOT, 358262306a36Sopenharmony_ci .op = REQ_OP_WRITE, 358362306a36Sopenharmony_ci .op_flags = REQ_SYNC | REQ_META | REQ_PRIO, 358462306a36Sopenharmony_ci .old_blkaddr = page->index, 358562306a36Sopenharmony_ci .new_blkaddr = page->index, 358662306a36Sopenharmony_ci .page = page, 358762306a36Sopenharmony_ci .encrypted_page = NULL, 358862306a36Sopenharmony_ci .in_list = 0, 358962306a36Sopenharmony_ci }; 359062306a36Sopenharmony_ci 359162306a36Sopenharmony_ci if (unlikely(page->index >= MAIN_BLKADDR(sbi))) 359262306a36Sopenharmony_ci fio.op_flags &= ~REQ_META; 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci set_page_writeback(page); 359562306a36Sopenharmony_ci f2fs_submit_page_write(&fio); 359662306a36Sopenharmony_ci 359762306a36Sopenharmony_ci stat_inc_meta_count(sbi, page->index); 359862306a36Sopenharmony_ci f2fs_update_iostat(sbi, NULL, io_type, F2FS_BLKSIZE); 359962306a36Sopenharmony_ci} 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_civoid f2fs_do_write_node_page(unsigned int nid, struct f2fs_io_info *fio) 360262306a36Sopenharmony_ci{ 360362306a36Sopenharmony_ci struct f2fs_summary sum; 360462306a36Sopenharmony_ci 360562306a36Sopenharmony_ci set_summary(&sum, nid, 0, 0); 360662306a36Sopenharmony_ci do_write_page(&sum, fio); 360762306a36Sopenharmony_ci 360862306a36Sopenharmony_ci f2fs_update_iostat(fio->sbi, NULL, fio->io_type, F2FS_BLKSIZE); 360962306a36Sopenharmony_ci} 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_civoid f2fs_outplace_write_data(struct dnode_of_data *dn, 361262306a36Sopenharmony_ci struct f2fs_io_info *fio) 361362306a36Sopenharmony_ci{ 361462306a36Sopenharmony_ci struct f2fs_sb_info *sbi = fio->sbi; 361562306a36Sopenharmony_ci struct f2fs_summary sum; 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR); 361862306a36Sopenharmony_ci if (fio->io_type == FS_DATA_IO || fio->io_type == FS_CP_DATA_IO) 361962306a36Sopenharmony_ci f2fs_update_age_extent_cache(dn); 362062306a36Sopenharmony_ci set_summary(&sum, dn->nid, dn->ofs_in_node, fio->version); 362162306a36Sopenharmony_ci do_write_page(&sum, fio); 362262306a36Sopenharmony_ci f2fs_update_data_blkaddr(dn, fio->new_blkaddr); 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci f2fs_update_iostat(sbi, dn->inode, fio->io_type, F2FS_BLKSIZE); 362562306a36Sopenharmony_ci} 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ciint f2fs_inplace_write_data(struct f2fs_io_info *fio) 362862306a36Sopenharmony_ci{ 362962306a36Sopenharmony_ci int err; 363062306a36Sopenharmony_ci struct f2fs_sb_info *sbi = fio->sbi; 363162306a36Sopenharmony_ci unsigned int segno; 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci fio->new_blkaddr = fio->old_blkaddr; 363462306a36Sopenharmony_ci /* i/o temperature is needed for passing down write hints */ 363562306a36Sopenharmony_ci __get_segment_type(fio); 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci segno = GET_SEGNO(sbi, fio->new_blkaddr); 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) { 364062306a36Sopenharmony_ci set_sbi_flag(sbi, SBI_NEED_FSCK); 364162306a36Sopenharmony_ci f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.", 364262306a36Sopenharmony_ci __func__, segno); 364362306a36Sopenharmony_ci err = -EFSCORRUPTED; 364462306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUM_TYPE); 364562306a36Sopenharmony_ci goto drop_bio; 364662306a36Sopenharmony_ci } 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci if (f2fs_cp_error(sbi)) { 364962306a36Sopenharmony_ci err = -EIO; 365062306a36Sopenharmony_ci goto drop_bio; 365162306a36Sopenharmony_ci } 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci if (fio->post_read) 365462306a36Sopenharmony_ci f2fs_truncate_meta_inode_pages(sbi, fio->new_blkaddr, 1); 365562306a36Sopenharmony_ci 365662306a36Sopenharmony_ci stat_inc_inplace_blocks(fio->sbi); 365762306a36Sopenharmony_ci 365862306a36Sopenharmony_ci if (fio->bio && !IS_F2FS_IPU_NOCACHE(sbi)) 365962306a36Sopenharmony_ci err = f2fs_merge_page_bio(fio); 366062306a36Sopenharmony_ci else 366162306a36Sopenharmony_ci err = f2fs_submit_page_bio(fio); 366262306a36Sopenharmony_ci if (!err) { 366362306a36Sopenharmony_ci f2fs_update_device_state(fio->sbi, fio->ino, 366462306a36Sopenharmony_ci fio->new_blkaddr, 1); 366562306a36Sopenharmony_ci f2fs_update_iostat(fio->sbi, fio->page->mapping->host, 366662306a36Sopenharmony_ci fio->io_type, F2FS_BLKSIZE); 366762306a36Sopenharmony_ci } 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci return err; 367062306a36Sopenharmony_cidrop_bio: 367162306a36Sopenharmony_ci if (fio->bio && *(fio->bio)) { 367262306a36Sopenharmony_ci struct bio *bio = *(fio->bio); 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci bio->bi_status = BLK_STS_IOERR; 367562306a36Sopenharmony_ci bio_endio(bio); 367662306a36Sopenharmony_ci *(fio->bio) = NULL; 367762306a36Sopenharmony_ci } 367862306a36Sopenharmony_ci return err; 367962306a36Sopenharmony_ci} 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_cistatic inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi, 368262306a36Sopenharmony_ci unsigned int segno) 368362306a36Sopenharmony_ci{ 368462306a36Sopenharmony_ci int i; 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ci for (i = CURSEG_HOT_DATA; i < NO_CHECK_TYPE; i++) { 368762306a36Sopenharmony_ci if (CURSEG_I(sbi, i)->segno == segno) 368862306a36Sopenharmony_ci break; 368962306a36Sopenharmony_ci } 369062306a36Sopenharmony_ci return i; 369162306a36Sopenharmony_ci} 369262306a36Sopenharmony_ci 369362306a36Sopenharmony_civoid f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, 369462306a36Sopenharmony_ci block_t old_blkaddr, block_t new_blkaddr, 369562306a36Sopenharmony_ci bool recover_curseg, bool recover_newaddr, 369662306a36Sopenharmony_ci bool from_gc) 369762306a36Sopenharmony_ci{ 369862306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 369962306a36Sopenharmony_ci struct curseg_info *curseg; 370062306a36Sopenharmony_ci unsigned int segno, old_cursegno; 370162306a36Sopenharmony_ci struct seg_entry *se; 370262306a36Sopenharmony_ci int type; 370362306a36Sopenharmony_ci unsigned short old_blkoff; 370462306a36Sopenharmony_ci unsigned char old_alloc_type; 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_ci segno = GET_SEGNO(sbi, new_blkaddr); 370762306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 370862306a36Sopenharmony_ci type = se->type; 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci f2fs_down_write(&SM_I(sbi)->curseg_lock); 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci if (!recover_curseg) { 371362306a36Sopenharmony_ci /* for recovery flow */ 371462306a36Sopenharmony_ci if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { 371562306a36Sopenharmony_ci if (old_blkaddr == NULL_ADDR) 371662306a36Sopenharmony_ci type = CURSEG_COLD_DATA; 371762306a36Sopenharmony_ci else 371862306a36Sopenharmony_ci type = CURSEG_WARM_DATA; 371962306a36Sopenharmony_ci } 372062306a36Sopenharmony_ci } else { 372162306a36Sopenharmony_ci if (IS_CURSEG(sbi, segno)) { 372262306a36Sopenharmony_ci /* se->type is volatile as SSR allocation */ 372362306a36Sopenharmony_ci type = __f2fs_get_curseg(sbi, segno); 372462306a36Sopenharmony_ci f2fs_bug_on(sbi, type == NO_CHECK_TYPE); 372562306a36Sopenharmony_ci } else { 372662306a36Sopenharmony_ci type = CURSEG_WARM_DATA; 372762306a36Sopenharmony_ci } 372862306a36Sopenharmony_ci } 372962306a36Sopenharmony_ci 373062306a36Sopenharmony_ci f2fs_bug_on(sbi, !IS_DATASEG(type)); 373162306a36Sopenharmony_ci curseg = CURSEG_I(sbi, type); 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 373462306a36Sopenharmony_ci down_write(&sit_i->sentry_lock); 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ci old_cursegno = curseg->segno; 373762306a36Sopenharmony_ci old_blkoff = curseg->next_blkoff; 373862306a36Sopenharmony_ci old_alloc_type = curseg->alloc_type; 373962306a36Sopenharmony_ci 374062306a36Sopenharmony_ci /* change the current segment */ 374162306a36Sopenharmony_ci if (segno != curseg->segno) { 374262306a36Sopenharmony_ci curseg->next_segno = segno; 374362306a36Sopenharmony_ci change_curseg(sbi, type); 374462306a36Sopenharmony_ci } 374562306a36Sopenharmony_ci 374662306a36Sopenharmony_ci curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); 374762306a36Sopenharmony_ci curseg->sum_blk->entries[curseg->next_blkoff] = *sum; 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_ci if (!recover_curseg || recover_newaddr) { 375062306a36Sopenharmony_ci if (!from_gc) 375162306a36Sopenharmony_ci update_segment_mtime(sbi, new_blkaddr, 0); 375262306a36Sopenharmony_ci update_sit_entry(sbi, new_blkaddr, 1); 375362306a36Sopenharmony_ci } 375462306a36Sopenharmony_ci if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) { 375562306a36Sopenharmony_ci f2fs_invalidate_internal_cache(sbi, old_blkaddr); 375662306a36Sopenharmony_ci if (!from_gc) 375762306a36Sopenharmony_ci update_segment_mtime(sbi, old_blkaddr, 0); 375862306a36Sopenharmony_ci update_sit_entry(sbi, old_blkaddr, -1); 375962306a36Sopenharmony_ci } 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); 376262306a36Sopenharmony_ci locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr)); 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci locate_dirty_segment(sbi, old_cursegno); 376562306a36Sopenharmony_ci 376662306a36Sopenharmony_ci if (recover_curseg) { 376762306a36Sopenharmony_ci if (old_cursegno != curseg->segno) { 376862306a36Sopenharmony_ci curseg->next_segno = old_cursegno; 376962306a36Sopenharmony_ci change_curseg(sbi, type); 377062306a36Sopenharmony_ci } 377162306a36Sopenharmony_ci curseg->next_blkoff = old_blkoff; 377262306a36Sopenharmony_ci curseg->alloc_type = old_alloc_type; 377362306a36Sopenharmony_ci } 377462306a36Sopenharmony_ci 377562306a36Sopenharmony_ci up_write(&sit_i->sentry_lock); 377662306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 377762306a36Sopenharmony_ci f2fs_up_write(&SM_I(sbi)->curseg_lock); 377862306a36Sopenharmony_ci} 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_civoid f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn, 378162306a36Sopenharmony_ci block_t old_addr, block_t new_addr, 378262306a36Sopenharmony_ci unsigned char version, bool recover_curseg, 378362306a36Sopenharmony_ci bool recover_newaddr) 378462306a36Sopenharmony_ci{ 378562306a36Sopenharmony_ci struct f2fs_summary sum; 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci set_summary(&sum, dn->nid, dn->ofs_in_node, version); 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ci f2fs_do_replace_block(sbi, &sum, old_addr, new_addr, 379062306a36Sopenharmony_ci recover_curseg, recover_newaddr, false); 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ci f2fs_update_data_blkaddr(dn, new_addr); 379362306a36Sopenharmony_ci} 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_civoid f2fs_wait_on_page_writeback(struct page *page, 379662306a36Sopenharmony_ci enum page_type type, bool ordered, bool locked) 379762306a36Sopenharmony_ci{ 379862306a36Sopenharmony_ci if (PageWriteback(page)) { 379962306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_P_SB(page); 380062306a36Sopenharmony_ci 380162306a36Sopenharmony_ci /* submit cached LFS IO */ 380262306a36Sopenharmony_ci f2fs_submit_merged_write_cond(sbi, NULL, page, 0, type); 380362306a36Sopenharmony_ci /* submit cached IPU IO */ 380462306a36Sopenharmony_ci f2fs_submit_merged_ipu_write(sbi, NULL, page); 380562306a36Sopenharmony_ci if (ordered) { 380662306a36Sopenharmony_ci wait_on_page_writeback(page); 380762306a36Sopenharmony_ci f2fs_bug_on(sbi, locked && PageWriteback(page)); 380862306a36Sopenharmony_ci } else { 380962306a36Sopenharmony_ci wait_for_stable_page(page); 381062306a36Sopenharmony_ci } 381162306a36Sopenharmony_ci } 381262306a36Sopenharmony_ci} 381362306a36Sopenharmony_ci 381462306a36Sopenharmony_civoid f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr) 381562306a36Sopenharmony_ci{ 381662306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 381762306a36Sopenharmony_ci struct page *cpage; 381862306a36Sopenharmony_ci 381962306a36Sopenharmony_ci if (!f2fs_post_read_required(inode)) 382062306a36Sopenharmony_ci return; 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci if (!__is_valid_data_blkaddr(blkaddr)) 382362306a36Sopenharmony_ci return; 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci cpage = find_lock_page(META_MAPPING(sbi), blkaddr); 382662306a36Sopenharmony_ci if (cpage) { 382762306a36Sopenharmony_ci f2fs_wait_on_page_writeback(cpage, DATA, true, true); 382862306a36Sopenharmony_ci f2fs_put_page(cpage, 1); 382962306a36Sopenharmony_ci } 383062306a36Sopenharmony_ci} 383162306a36Sopenharmony_ci 383262306a36Sopenharmony_civoid f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, 383362306a36Sopenharmony_ci block_t len) 383462306a36Sopenharmony_ci{ 383562306a36Sopenharmony_ci struct f2fs_sb_info *sbi = F2FS_I_SB(inode); 383662306a36Sopenharmony_ci block_t i; 383762306a36Sopenharmony_ci 383862306a36Sopenharmony_ci if (!f2fs_post_read_required(inode)) 383962306a36Sopenharmony_ci return; 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_ci for (i = 0; i < len; i++) 384262306a36Sopenharmony_ci f2fs_wait_on_block_writeback(inode, blkaddr + i); 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci f2fs_truncate_meta_inode_pages(sbi, blkaddr, len); 384562306a36Sopenharmony_ci} 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_cistatic int read_compacted_summaries(struct f2fs_sb_info *sbi) 384862306a36Sopenharmony_ci{ 384962306a36Sopenharmony_ci struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 385062306a36Sopenharmony_ci struct curseg_info *seg_i; 385162306a36Sopenharmony_ci unsigned char *kaddr; 385262306a36Sopenharmony_ci struct page *page; 385362306a36Sopenharmony_ci block_t start; 385462306a36Sopenharmony_ci int i, j, offset; 385562306a36Sopenharmony_ci 385662306a36Sopenharmony_ci start = start_sum_block(sbi); 385762306a36Sopenharmony_ci 385862306a36Sopenharmony_ci page = f2fs_get_meta_page(sbi, start++); 385962306a36Sopenharmony_ci if (IS_ERR(page)) 386062306a36Sopenharmony_ci return PTR_ERR(page); 386162306a36Sopenharmony_ci kaddr = (unsigned char *)page_address(page); 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ci /* Step 1: restore nat cache */ 386462306a36Sopenharmony_ci seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); 386562306a36Sopenharmony_ci memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE); 386662306a36Sopenharmony_ci 386762306a36Sopenharmony_ci /* Step 2: restore sit cache */ 386862306a36Sopenharmony_ci seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); 386962306a36Sopenharmony_ci memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); 387062306a36Sopenharmony_ci offset = 2 * SUM_JOURNAL_SIZE; 387162306a36Sopenharmony_ci 387262306a36Sopenharmony_ci /* Step 3: restore summary entries */ 387362306a36Sopenharmony_ci for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 387462306a36Sopenharmony_ci unsigned short blk_off; 387562306a36Sopenharmony_ci unsigned int segno; 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci seg_i = CURSEG_I(sbi, i); 387862306a36Sopenharmony_ci segno = le32_to_cpu(ckpt->cur_data_segno[i]); 387962306a36Sopenharmony_ci blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); 388062306a36Sopenharmony_ci seg_i->next_segno = segno; 388162306a36Sopenharmony_ci reset_curseg(sbi, i, 0); 388262306a36Sopenharmony_ci seg_i->alloc_type = ckpt->alloc_type[i]; 388362306a36Sopenharmony_ci seg_i->next_blkoff = blk_off; 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_ci if (seg_i->alloc_type == SSR) 388662306a36Sopenharmony_ci blk_off = sbi->blocks_per_seg; 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci for (j = 0; j < blk_off; j++) { 388962306a36Sopenharmony_ci struct f2fs_summary *s; 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_ci s = (struct f2fs_summary *)(kaddr + offset); 389262306a36Sopenharmony_ci seg_i->sum_blk->entries[j] = *s; 389362306a36Sopenharmony_ci offset += SUMMARY_SIZE; 389462306a36Sopenharmony_ci if (offset + SUMMARY_SIZE <= PAGE_SIZE - 389562306a36Sopenharmony_ci SUM_FOOTER_SIZE) 389662306a36Sopenharmony_ci continue; 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci f2fs_put_page(page, 1); 389962306a36Sopenharmony_ci page = NULL; 390062306a36Sopenharmony_ci 390162306a36Sopenharmony_ci page = f2fs_get_meta_page(sbi, start++); 390262306a36Sopenharmony_ci if (IS_ERR(page)) 390362306a36Sopenharmony_ci return PTR_ERR(page); 390462306a36Sopenharmony_ci kaddr = (unsigned char *)page_address(page); 390562306a36Sopenharmony_ci offset = 0; 390662306a36Sopenharmony_ci } 390762306a36Sopenharmony_ci } 390862306a36Sopenharmony_ci f2fs_put_page(page, 1); 390962306a36Sopenharmony_ci return 0; 391062306a36Sopenharmony_ci} 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_cistatic int read_normal_summaries(struct f2fs_sb_info *sbi, int type) 391362306a36Sopenharmony_ci{ 391462306a36Sopenharmony_ci struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 391562306a36Sopenharmony_ci struct f2fs_summary_block *sum; 391662306a36Sopenharmony_ci struct curseg_info *curseg; 391762306a36Sopenharmony_ci struct page *new; 391862306a36Sopenharmony_ci unsigned short blk_off; 391962306a36Sopenharmony_ci unsigned int segno = 0; 392062306a36Sopenharmony_ci block_t blk_addr = 0; 392162306a36Sopenharmony_ci int err = 0; 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci /* get segment number and block addr */ 392462306a36Sopenharmony_ci if (IS_DATASEG(type)) { 392562306a36Sopenharmony_ci segno = le32_to_cpu(ckpt->cur_data_segno[type]); 392662306a36Sopenharmony_ci blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - 392762306a36Sopenharmony_ci CURSEG_HOT_DATA]); 392862306a36Sopenharmony_ci if (__exist_node_summaries(sbi)) 392962306a36Sopenharmony_ci blk_addr = sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type); 393062306a36Sopenharmony_ci else 393162306a36Sopenharmony_ci blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); 393262306a36Sopenharmony_ci } else { 393362306a36Sopenharmony_ci segno = le32_to_cpu(ckpt->cur_node_segno[type - 393462306a36Sopenharmony_ci CURSEG_HOT_NODE]); 393562306a36Sopenharmony_ci blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - 393662306a36Sopenharmony_ci CURSEG_HOT_NODE]); 393762306a36Sopenharmony_ci if (__exist_node_summaries(sbi)) 393862306a36Sopenharmony_ci blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, 393962306a36Sopenharmony_ci type - CURSEG_HOT_NODE); 394062306a36Sopenharmony_ci else 394162306a36Sopenharmony_ci blk_addr = GET_SUM_BLOCK(sbi, segno); 394262306a36Sopenharmony_ci } 394362306a36Sopenharmony_ci 394462306a36Sopenharmony_ci new = f2fs_get_meta_page(sbi, blk_addr); 394562306a36Sopenharmony_ci if (IS_ERR(new)) 394662306a36Sopenharmony_ci return PTR_ERR(new); 394762306a36Sopenharmony_ci sum = (struct f2fs_summary_block *)page_address(new); 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci if (IS_NODESEG(type)) { 395062306a36Sopenharmony_ci if (__exist_node_summaries(sbi)) { 395162306a36Sopenharmony_ci struct f2fs_summary *ns = &sum->entries[0]; 395262306a36Sopenharmony_ci int i; 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci for (i = 0; i < sbi->blocks_per_seg; i++, ns++) { 395562306a36Sopenharmony_ci ns->version = 0; 395662306a36Sopenharmony_ci ns->ofs_in_node = 0; 395762306a36Sopenharmony_ci } 395862306a36Sopenharmony_ci } else { 395962306a36Sopenharmony_ci err = f2fs_restore_node_summary(sbi, segno, sum); 396062306a36Sopenharmony_ci if (err) 396162306a36Sopenharmony_ci goto out; 396262306a36Sopenharmony_ci } 396362306a36Sopenharmony_ci } 396462306a36Sopenharmony_ci 396562306a36Sopenharmony_ci /* set uncompleted segment to curseg */ 396662306a36Sopenharmony_ci curseg = CURSEG_I(sbi, type); 396762306a36Sopenharmony_ci mutex_lock(&curseg->curseg_mutex); 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci /* update journal info */ 397062306a36Sopenharmony_ci down_write(&curseg->journal_rwsem); 397162306a36Sopenharmony_ci memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE); 397262306a36Sopenharmony_ci up_write(&curseg->journal_rwsem); 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE); 397562306a36Sopenharmony_ci memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE); 397662306a36Sopenharmony_ci curseg->next_segno = segno; 397762306a36Sopenharmony_ci reset_curseg(sbi, type, 0); 397862306a36Sopenharmony_ci curseg->alloc_type = ckpt->alloc_type[type]; 397962306a36Sopenharmony_ci curseg->next_blkoff = blk_off; 398062306a36Sopenharmony_ci mutex_unlock(&curseg->curseg_mutex); 398162306a36Sopenharmony_ciout: 398262306a36Sopenharmony_ci f2fs_put_page(new, 1); 398362306a36Sopenharmony_ci return err; 398462306a36Sopenharmony_ci} 398562306a36Sopenharmony_ci 398662306a36Sopenharmony_cistatic int restore_curseg_summaries(struct f2fs_sb_info *sbi) 398762306a36Sopenharmony_ci{ 398862306a36Sopenharmony_ci struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal; 398962306a36Sopenharmony_ci struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal; 399062306a36Sopenharmony_ci int type = CURSEG_HOT_DATA; 399162306a36Sopenharmony_ci int err; 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) { 399462306a36Sopenharmony_ci int npages = f2fs_npages_for_summary_flush(sbi, true); 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_ci if (npages >= 2) 399762306a36Sopenharmony_ci f2fs_ra_meta_pages(sbi, start_sum_block(sbi), npages, 399862306a36Sopenharmony_ci META_CP, true); 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci /* restore for compacted data summary */ 400162306a36Sopenharmony_ci err = read_compacted_summaries(sbi); 400262306a36Sopenharmony_ci if (err) 400362306a36Sopenharmony_ci return err; 400462306a36Sopenharmony_ci type = CURSEG_HOT_NODE; 400562306a36Sopenharmony_ci } 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci if (__exist_node_summaries(sbi)) 400862306a36Sopenharmony_ci f2fs_ra_meta_pages(sbi, 400962306a36Sopenharmony_ci sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type), 401062306a36Sopenharmony_ci NR_CURSEG_PERSIST_TYPE - type, META_CP, true); 401162306a36Sopenharmony_ci 401262306a36Sopenharmony_ci for (; type <= CURSEG_COLD_NODE; type++) { 401362306a36Sopenharmony_ci err = read_normal_summaries(sbi, type); 401462306a36Sopenharmony_ci if (err) 401562306a36Sopenharmony_ci return err; 401662306a36Sopenharmony_ci } 401762306a36Sopenharmony_ci 401862306a36Sopenharmony_ci /* sanity check for summary blocks */ 401962306a36Sopenharmony_ci if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || 402062306a36Sopenharmony_ci sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) { 402162306a36Sopenharmony_ci f2fs_err(sbi, "invalid journal entries nats %u sits %u", 402262306a36Sopenharmony_ci nats_in_cursum(nat_j), sits_in_cursum(sit_j)); 402362306a36Sopenharmony_ci return -EINVAL; 402462306a36Sopenharmony_ci } 402562306a36Sopenharmony_ci 402662306a36Sopenharmony_ci return 0; 402762306a36Sopenharmony_ci} 402862306a36Sopenharmony_ci 402962306a36Sopenharmony_cistatic void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) 403062306a36Sopenharmony_ci{ 403162306a36Sopenharmony_ci struct page *page; 403262306a36Sopenharmony_ci unsigned char *kaddr; 403362306a36Sopenharmony_ci struct f2fs_summary *summary; 403462306a36Sopenharmony_ci struct curseg_info *seg_i; 403562306a36Sopenharmony_ci int written_size = 0; 403662306a36Sopenharmony_ci int i, j; 403762306a36Sopenharmony_ci 403862306a36Sopenharmony_ci page = f2fs_grab_meta_page(sbi, blkaddr++); 403962306a36Sopenharmony_ci kaddr = (unsigned char *)page_address(page); 404062306a36Sopenharmony_ci memset(kaddr, 0, PAGE_SIZE); 404162306a36Sopenharmony_ci 404262306a36Sopenharmony_ci /* Step 1: write nat cache */ 404362306a36Sopenharmony_ci seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); 404462306a36Sopenharmony_ci memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE); 404562306a36Sopenharmony_ci written_size += SUM_JOURNAL_SIZE; 404662306a36Sopenharmony_ci 404762306a36Sopenharmony_ci /* Step 2: write sit cache */ 404862306a36Sopenharmony_ci seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); 404962306a36Sopenharmony_ci memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE); 405062306a36Sopenharmony_ci written_size += SUM_JOURNAL_SIZE; 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci /* Step 3: write summary entries */ 405362306a36Sopenharmony_ci for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { 405462306a36Sopenharmony_ci seg_i = CURSEG_I(sbi, i); 405562306a36Sopenharmony_ci for (j = 0; j < f2fs_curseg_valid_blocks(sbi, i); j++) { 405662306a36Sopenharmony_ci if (!page) { 405762306a36Sopenharmony_ci page = f2fs_grab_meta_page(sbi, blkaddr++); 405862306a36Sopenharmony_ci kaddr = (unsigned char *)page_address(page); 405962306a36Sopenharmony_ci memset(kaddr, 0, PAGE_SIZE); 406062306a36Sopenharmony_ci written_size = 0; 406162306a36Sopenharmony_ci } 406262306a36Sopenharmony_ci summary = (struct f2fs_summary *)(kaddr + written_size); 406362306a36Sopenharmony_ci *summary = seg_i->sum_blk->entries[j]; 406462306a36Sopenharmony_ci written_size += SUMMARY_SIZE; 406562306a36Sopenharmony_ci 406662306a36Sopenharmony_ci if (written_size + SUMMARY_SIZE <= PAGE_SIZE - 406762306a36Sopenharmony_ci SUM_FOOTER_SIZE) 406862306a36Sopenharmony_ci continue; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci set_page_dirty(page); 407162306a36Sopenharmony_ci f2fs_put_page(page, 1); 407262306a36Sopenharmony_ci page = NULL; 407362306a36Sopenharmony_ci } 407462306a36Sopenharmony_ci } 407562306a36Sopenharmony_ci if (page) { 407662306a36Sopenharmony_ci set_page_dirty(page); 407762306a36Sopenharmony_ci f2fs_put_page(page, 1); 407862306a36Sopenharmony_ci } 407962306a36Sopenharmony_ci} 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_cistatic void write_normal_summaries(struct f2fs_sb_info *sbi, 408262306a36Sopenharmony_ci block_t blkaddr, int type) 408362306a36Sopenharmony_ci{ 408462306a36Sopenharmony_ci int i, end; 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_ci if (IS_DATASEG(type)) 408762306a36Sopenharmony_ci end = type + NR_CURSEG_DATA_TYPE; 408862306a36Sopenharmony_ci else 408962306a36Sopenharmony_ci end = type + NR_CURSEG_NODE_TYPE; 409062306a36Sopenharmony_ci 409162306a36Sopenharmony_ci for (i = type; i < end; i++) 409262306a36Sopenharmony_ci write_current_sum_page(sbi, i, blkaddr + (i - type)); 409362306a36Sopenharmony_ci} 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_civoid f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) 409662306a36Sopenharmony_ci{ 409762306a36Sopenharmony_ci if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) 409862306a36Sopenharmony_ci write_compacted_summaries(sbi, start_blk); 409962306a36Sopenharmony_ci else 410062306a36Sopenharmony_ci write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); 410162306a36Sopenharmony_ci} 410262306a36Sopenharmony_ci 410362306a36Sopenharmony_civoid f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) 410462306a36Sopenharmony_ci{ 410562306a36Sopenharmony_ci write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); 410662306a36Sopenharmony_ci} 410762306a36Sopenharmony_ci 410862306a36Sopenharmony_ciint f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, 410962306a36Sopenharmony_ci unsigned int val, int alloc) 411062306a36Sopenharmony_ci{ 411162306a36Sopenharmony_ci int i; 411262306a36Sopenharmony_ci 411362306a36Sopenharmony_ci if (type == NAT_JOURNAL) { 411462306a36Sopenharmony_ci for (i = 0; i < nats_in_cursum(journal); i++) { 411562306a36Sopenharmony_ci if (le32_to_cpu(nid_in_journal(journal, i)) == val) 411662306a36Sopenharmony_ci return i; 411762306a36Sopenharmony_ci } 411862306a36Sopenharmony_ci if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL)) 411962306a36Sopenharmony_ci return update_nats_in_cursum(journal, 1); 412062306a36Sopenharmony_ci } else if (type == SIT_JOURNAL) { 412162306a36Sopenharmony_ci for (i = 0; i < sits_in_cursum(journal); i++) 412262306a36Sopenharmony_ci if (le32_to_cpu(segno_in_journal(journal, i)) == val) 412362306a36Sopenharmony_ci return i; 412462306a36Sopenharmony_ci if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL)) 412562306a36Sopenharmony_ci return update_sits_in_cursum(journal, 1); 412662306a36Sopenharmony_ci } 412762306a36Sopenharmony_ci return -1; 412862306a36Sopenharmony_ci} 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_cistatic struct page *get_current_sit_page(struct f2fs_sb_info *sbi, 413162306a36Sopenharmony_ci unsigned int segno) 413262306a36Sopenharmony_ci{ 413362306a36Sopenharmony_ci return f2fs_get_meta_page(sbi, current_sit_addr(sbi, segno)); 413462306a36Sopenharmony_ci} 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_cistatic struct page *get_next_sit_page(struct f2fs_sb_info *sbi, 413762306a36Sopenharmony_ci unsigned int start) 413862306a36Sopenharmony_ci{ 413962306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 414062306a36Sopenharmony_ci struct page *page; 414162306a36Sopenharmony_ci pgoff_t src_off, dst_off; 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ci src_off = current_sit_addr(sbi, start); 414462306a36Sopenharmony_ci dst_off = next_sit_addr(sbi, src_off); 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_ci page = f2fs_grab_meta_page(sbi, dst_off); 414762306a36Sopenharmony_ci seg_info_to_sit_page(sbi, page, start); 414862306a36Sopenharmony_ci 414962306a36Sopenharmony_ci set_page_dirty(page); 415062306a36Sopenharmony_ci set_to_next_sit(sit_i, start); 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci return page; 415362306a36Sopenharmony_ci} 415462306a36Sopenharmony_ci 415562306a36Sopenharmony_cistatic struct sit_entry_set *grab_sit_entry_set(void) 415662306a36Sopenharmony_ci{ 415762306a36Sopenharmony_ci struct sit_entry_set *ses = 415862306a36Sopenharmony_ci f2fs_kmem_cache_alloc(sit_entry_set_slab, 415962306a36Sopenharmony_ci GFP_NOFS, true, NULL); 416062306a36Sopenharmony_ci 416162306a36Sopenharmony_ci ses->entry_cnt = 0; 416262306a36Sopenharmony_ci INIT_LIST_HEAD(&ses->set_list); 416362306a36Sopenharmony_ci return ses; 416462306a36Sopenharmony_ci} 416562306a36Sopenharmony_ci 416662306a36Sopenharmony_cistatic void release_sit_entry_set(struct sit_entry_set *ses) 416762306a36Sopenharmony_ci{ 416862306a36Sopenharmony_ci list_del(&ses->set_list); 416962306a36Sopenharmony_ci kmem_cache_free(sit_entry_set_slab, ses); 417062306a36Sopenharmony_ci} 417162306a36Sopenharmony_ci 417262306a36Sopenharmony_cistatic void adjust_sit_entry_set(struct sit_entry_set *ses, 417362306a36Sopenharmony_ci struct list_head *head) 417462306a36Sopenharmony_ci{ 417562306a36Sopenharmony_ci struct sit_entry_set *next = ses; 417662306a36Sopenharmony_ci 417762306a36Sopenharmony_ci if (list_is_last(&ses->set_list, head)) 417862306a36Sopenharmony_ci return; 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci list_for_each_entry_continue(next, head, set_list) 418162306a36Sopenharmony_ci if (ses->entry_cnt <= next->entry_cnt) { 418262306a36Sopenharmony_ci list_move_tail(&ses->set_list, &next->set_list); 418362306a36Sopenharmony_ci return; 418462306a36Sopenharmony_ci } 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci list_move_tail(&ses->set_list, head); 418762306a36Sopenharmony_ci} 418862306a36Sopenharmony_ci 418962306a36Sopenharmony_cistatic void add_sit_entry(unsigned int segno, struct list_head *head) 419062306a36Sopenharmony_ci{ 419162306a36Sopenharmony_ci struct sit_entry_set *ses; 419262306a36Sopenharmony_ci unsigned int start_segno = START_SEGNO(segno); 419362306a36Sopenharmony_ci 419462306a36Sopenharmony_ci list_for_each_entry(ses, head, set_list) { 419562306a36Sopenharmony_ci if (ses->start_segno == start_segno) { 419662306a36Sopenharmony_ci ses->entry_cnt++; 419762306a36Sopenharmony_ci adjust_sit_entry_set(ses, head); 419862306a36Sopenharmony_ci return; 419962306a36Sopenharmony_ci } 420062306a36Sopenharmony_ci } 420162306a36Sopenharmony_ci 420262306a36Sopenharmony_ci ses = grab_sit_entry_set(); 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci ses->start_segno = start_segno; 420562306a36Sopenharmony_ci ses->entry_cnt++; 420662306a36Sopenharmony_ci list_add(&ses->set_list, head); 420762306a36Sopenharmony_ci} 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_cistatic void add_sits_in_set(struct f2fs_sb_info *sbi) 421062306a36Sopenharmony_ci{ 421162306a36Sopenharmony_ci struct f2fs_sm_info *sm_info = SM_I(sbi); 421262306a36Sopenharmony_ci struct list_head *set_list = &sm_info->sit_entry_set; 421362306a36Sopenharmony_ci unsigned long *bitmap = SIT_I(sbi)->dirty_sentries_bitmap; 421462306a36Sopenharmony_ci unsigned int segno; 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_ci for_each_set_bit(segno, bitmap, MAIN_SEGS(sbi)) 421762306a36Sopenharmony_ci add_sit_entry(segno, set_list); 421862306a36Sopenharmony_ci} 421962306a36Sopenharmony_ci 422062306a36Sopenharmony_cistatic void remove_sits_in_journal(struct f2fs_sb_info *sbi) 422162306a36Sopenharmony_ci{ 422262306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 422362306a36Sopenharmony_ci struct f2fs_journal *journal = curseg->journal; 422462306a36Sopenharmony_ci int i; 422562306a36Sopenharmony_ci 422662306a36Sopenharmony_ci down_write(&curseg->journal_rwsem); 422762306a36Sopenharmony_ci for (i = 0; i < sits_in_cursum(journal); i++) { 422862306a36Sopenharmony_ci unsigned int segno; 422962306a36Sopenharmony_ci bool dirtied; 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci segno = le32_to_cpu(segno_in_journal(journal, i)); 423262306a36Sopenharmony_ci dirtied = __mark_sit_entry_dirty(sbi, segno); 423362306a36Sopenharmony_ci 423462306a36Sopenharmony_ci if (!dirtied) 423562306a36Sopenharmony_ci add_sit_entry(segno, &SM_I(sbi)->sit_entry_set); 423662306a36Sopenharmony_ci } 423762306a36Sopenharmony_ci update_sits_in_cursum(journal, -i); 423862306a36Sopenharmony_ci up_write(&curseg->journal_rwsem); 423962306a36Sopenharmony_ci} 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci/* 424262306a36Sopenharmony_ci * CP calls this function, which flushes SIT entries including sit_journal, 424362306a36Sopenharmony_ci * and moves prefree segs to free segs. 424462306a36Sopenharmony_ci */ 424562306a36Sopenharmony_civoid f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) 424662306a36Sopenharmony_ci{ 424762306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 424862306a36Sopenharmony_ci unsigned long *bitmap = sit_i->dirty_sentries_bitmap; 424962306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 425062306a36Sopenharmony_ci struct f2fs_journal *journal = curseg->journal; 425162306a36Sopenharmony_ci struct sit_entry_set *ses, *tmp; 425262306a36Sopenharmony_ci struct list_head *head = &SM_I(sbi)->sit_entry_set; 425362306a36Sopenharmony_ci bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); 425462306a36Sopenharmony_ci struct seg_entry *se; 425562306a36Sopenharmony_ci 425662306a36Sopenharmony_ci down_write(&sit_i->sentry_lock); 425762306a36Sopenharmony_ci 425862306a36Sopenharmony_ci if (!sit_i->dirty_sentries) 425962306a36Sopenharmony_ci goto out; 426062306a36Sopenharmony_ci 426162306a36Sopenharmony_ci /* 426262306a36Sopenharmony_ci * add and account sit entries of dirty bitmap in sit entry 426362306a36Sopenharmony_ci * set temporarily 426462306a36Sopenharmony_ci */ 426562306a36Sopenharmony_ci add_sits_in_set(sbi); 426662306a36Sopenharmony_ci 426762306a36Sopenharmony_ci /* 426862306a36Sopenharmony_ci * if there are no enough space in journal to store dirty sit 426962306a36Sopenharmony_ci * entries, remove all entries from journal and add and account 427062306a36Sopenharmony_ci * them in sit entry set. 427162306a36Sopenharmony_ci */ 427262306a36Sopenharmony_ci if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) || 427362306a36Sopenharmony_ci !to_journal) 427462306a36Sopenharmony_ci remove_sits_in_journal(sbi); 427562306a36Sopenharmony_ci 427662306a36Sopenharmony_ci /* 427762306a36Sopenharmony_ci * there are two steps to flush sit entries: 427862306a36Sopenharmony_ci * #1, flush sit entries to journal in current cold data summary block. 427962306a36Sopenharmony_ci * #2, flush sit entries to sit page. 428062306a36Sopenharmony_ci */ 428162306a36Sopenharmony_ci list_for_each_entry_safe(ses, tmp, head, set_list) { 428262306a36Sopenharmony_ci struct page *page = NULL; 428362306a36Sopenharmony_ci struct f2fs_sit_block *raw_sit = NULL; 428462306a36Sopenharmony_ci unsigned int start_segno = ses->start_segno; 428562306a36Sopenharmony_ci unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, 428662306a36Sopenharmony_ci (unsigned long)MAIN_SEGS(sbi)); 428762306a36Sopenharmony_ci unsigned int segno = start_segno; 428862306a36Sopenharmony_ci 428962306a36Sopenharmony_ci if (to_journal && 429062306a36Sopenharmony_ci !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) 429162306a36Sopenharmony_ci to_journal = false; 429262306a36Sopenharmony_ci 429362306a36Sopenharmony_ci if (to_journal) { 429462306a36Sopenharmony_ci down_write(&curseg->journal_rwsem); 429562306a36Sopenharmony_ci } else { 429662306a36Sopenharmony_ci page = get_next_sit_page(sbi, start_segno); 429762306a36Sopenharmony_ci raw_sit = page_address(page); 429862306a36Sopenharmony_ci } 429962306a36Sopenharmony_ci 430062306a36Sopenharmony_ci /* flush dirty sit entries in region of current sit set */ 430162306a36Sopenharmony_ci for_each_set_bit_from(segno, bitmap, end) { 430262306a36Sopenharmony_ci int offset, sit_offset; 430362306a36Sopenharmony_ci 430462306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 430562306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 430662306a36Sopenharmony_ci if (memcmp(se->cur_valid_map, se->cur_valid_map_mir, 430762306a36Sopenharmony_ci SIT_VBLOCK_MAP_SIZE)) 430862306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 430962306a36Sopenharmony_ci#endif 431062306a36Sopenharmony_ci 431162306a36Sopenharmony_ci /* add discard candidates */ 431262306a36Sopenharmony_ci if (!(cpc->reason & CP_DISCARD)) { 431362306a36Sopenharmony_ci cpc->trim_start = segno; 431462306a36Sopenharmony_ci add_discard_addrs(sbi, cpc, false); 431562306a36Sopenharmony_ci } 431662306a36Sopenharmony_ci 431762306a36Sopenharmony_ci if (to_journal) { 431862306a36Sopenharmony_ci offset = f2fs_lookup_journal_in_cursum(journal, 431962306a36Sopenharmony_ci SIT_JOURNAL, segno, 1); 432062306a36Sopenharmony_ci f2fs_bug_on(sbi, offset < 0); 432162306a36Sopenharmony_ci segno_in_journal(journal, offset) = 432262306a36Sopenharmony_ci cpu_to_le32(segno); 432362306a36Sopenharmony_ci seg_info_to_raw_sit(se, 432462306a36Sopenharmony_ci &sit_in_journal(journal, offset)); 432562306a36Sopenharmony_ci check_block_count(sbi, segno, 432662306a36Sopenharmony_ci &sit_in_journal(journal, offset)); 432762306a36Sopenharmony_ci } else { 432862306a36Sopenharmony_ci sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); 432962306a36Sopenharmony_ci seg_info_to_raw_sit(se, 433062306a36Sopenharmony_ci &raw_sit->entries[sit_offset]); 433162306a36Sopenharmony_ci check_block_count(sbi, segno, 433262306a36Sopenharmony_ci &raw_sit->entries[sit_offset]); 433362306a36Sopenharmony_ci } 433462306a36Sopenharmony_ci 433562306a36Sopenharmony_ci __clear_bit(segno, bitmap); 433662306a36Sopenharmony_ci sit_i->dirty_sentries--; 433762306a36Sopenharmony_ci ses->entry_cnt--; 433862306a36Sopenharmony_ci } 433962306a36Sopenharmony_ci 434062306a36Sopenharmony_ci if (to_journal) 434162306a36Sopenharmony_ci up_write(&curseg->journal_rwsem); 434262306a36Sopenharmony_ci else 434362306a36Sopenharmony_ci f2fs_put_page(page, 1); 434462306a36Sopenharmony_ci 434562306a36Sopenharmony_ci f2fs_bug_on(sbi, ses->entry_cnt); 434662306a36Sopenharmony_ci release_sit_entry_set(ses); 434762306a36Sopenharmony_ci } 434862306a36Sopenharmony_ci 434962306a36Sopenharmony_ci f2fs_bug_on(sbi, !list_empty(head)); 435062306a36Sopenharmony_ci f2fs_bug_on(sbi, sit_i->dirty_sentries); 435162306a36Sopenharmony_ciout: 435262306a36Sopenharmony_ci if (cpc->reason & CP_DISCARD) { 435362306a36Sopenharmony_ci __u64 trim_start = cpc->trim_start; 435462306a36Sopenharmony_ci 435562306a36Sopenharmony_ci for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) 435662306a36Sopenharmony_ci add_discard_addrs(sbi, cpc, false); 435762306a36Sopenharmony_ci 435862306a36Sopenharmony_ci cpc->trim_start = trim_start; 435962306a36Sopenharmony_ci } 436062306a36Sopenharmony_ci up_write(&sit_i->sentry_lock); 436162306a36Sopenharmony_ci 436262306a36Sopenharmony_ci set_prefree_as_free_segments(sbi); 436362306a36Sopenharmony_ci} 436462306a36Sopenharmony_ci 436562306a36Sopenharmony_cistatic int build_sit_info(struct f2fs_sb_info *sbi) 436662306a36Sopenharmony_ci{ 436762306a36Sopenharmony_ci struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); 436862306a36Sopenharmony_ci struct sit_info *sit_i; 436962306a36Sopenharmony_ci unsigned int sit_segs, start; 437062306a36Sopenharmony_ci char *src_bitmap, *bitmap; 437162306a36Sopenharmony_ci unsigned int bitmap_size, main_bitmap_size, sit_bitmap_size; 437262306a36Sopenharmony_ci unsigned int discard_map = f2fs_block_unit_discard(sbi) ? 1 : 0; 437362306a36Sopenharmony_ci 437462306a36Sopenharmony_ci /* allocate memory for SIT information */ 437562306a36Sopenharmony_ci sit_i = f2fs_kzalloc(sbi, sizeof(struct sit_info), GFP_KERNEL); 437662306a36Sopenharmony_ci if (!sit_i) 437762306a36Sopenharmony_ci return -ENOMEM; 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci SM_I(sbi)->sit_info = sit_i; 438062306a36Sopenharmony_ci 438162306a36Sopenharmony_ci sit_i->sentries = 438262306a36Sopenharmony_ci f2fs_kvzalloc(sbi, array_size(sizeof(struct seg_entry), 438362306a36Sopenharmony_ci MAIN_SEGS(sbi)), 438462306a36Sopenharmony_ci GFP_KERNEL); 438562306a36Sopenharmony_ci if (!sit_i->sentries) 438662306a36Sopenharmony_ci return -ENOMEM; 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci main_bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 438962306a36Sopenharmony_ci sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(sbi, main_bitmap_size, 439062306a36Sopenharmony_ci GFP_KERNEL); 439162306a36Sopenharmony_ci if (!sit_i->dirty_sentries_bitmap) 439262306a36Sopenharmony_ci return -ENOMEM; 439362306a36Sopenharmony_ci 439462306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 439562306a36Sopenharmony_ci bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map); 439662306a36Sopenharmony_ci#else 439762306a36Sopenharmony_ci bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map); 439862306a36Sopenharmony_ci#endif 439962306a36Sopenharmony_ci sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 440062306a36Sopenharmony_ci if (!sit_i->bitmap) 440162306a36Sopenharmony_ci return -ENOMEM; 440262306a36Sopenharmony_ci 440362306a36Sopenharmony_ci bitmap = sit_i->bitmap; 440462306a36Sopenharmony_ci 440562306a36Sopenharmony_ci for (start = 0; start < MAIN_SEGS(sbi); start++) { 440662306a36Sopenharmony_ci sit_i->sentries[start].cur_valid_map = bitmap; 440762306a36Sopenharmony_ci bitmap += SIT_VBLOCK_MAP_SIZE; 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci sit_i->sentries[start].ckpt_valid_map = bitmap; 441062306a36Sopenharmony_ci bitmap += SIT_VBLOCK_MAP_SIZE; 441162306a36Sopenharmony_ci 441262306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 441362306a36Sopenharmony_ci sit_i->sentries[start].cur_valid_map_mir = bitmap; 441462306a36Sopenharmony_ci bitmap += SIT_VBLOCK_MAP_SIZE; 441562306a36Sopenharmony_ci#endif 441662306a36Sopenharmony_ci 441762306a36Sopenharmony_ci if (discard_map) { 441862306a36Sopenharmony_ci sit_i->sentries[start].discard_map = bitmap; 441962306a36Sopenharmony_ci bitmap += SIT_VBLOCK_MAP_SIZE; 442062306a36Sopenharmony_ci } 442162306a36Sopenharmony_ci } 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_ci sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); 442462306a36Sopenharmony_ci if (!sit_i->tmp_map) 442562306a36Sopenharmony_ci return -ENOMEM; 442662306a36Sopenharmony_ci 442762306a36Sopenharmony_ci if (__is_large_section(sbi)) { 442862306a36Sopenharmony_ci sit_i->sec_entries = 442962306a36Sopenharmony_ci f2fs_kvzalloc(sbi, array_size(sizeof(struct sec_entry), 443062306a36Sopenharmony_ci MAIN_SECS(sbi)), 443162306a36Sopenharmony_ci GFP_KERNEL); 443262306a36Sopenharmony_ci if (!sit_i->sec_entries) 443362306a36Sopenharmony_ci return -ENOMEM; 443462306a36Sopenharmony_ci } 443562306a36Sopenharmony_ci 443662306a36Sopenharmony_ci /* get information related with SIT */ 443762306a36Sopenharmony_ci sit_segs = le32_to_cpu(raw_super->segment_count_sit) >> 1; 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ci /* setup SIT bitmap from ckeckpoint pack */ 444062306a36Sopenharmony_ci sit_bitmap_size = __bitmap_size(sbi, SIT_BITMAP); 444162306a36Sopenharmony_ci src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); 444262306a36Sopenharmony_ci 444362306a36Sopenharmony_ci sit_i->sit_bitmap = kmemdup(src_bitmap, sit_bitmap_size, GFP_KERNEL); 444462306a36Sopenharmony_ci if (!sit_i->sit_bitmap) 444562306a36Sopenharmony_ci return -ENOMEM; 444662306a36Sopenharmony_ci 444762306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 444862306a36Sopenharmony_ci sit_i->sit_bitmap_mir = kmemdup(src_bitmap, 444962306a36Sopenharmony_ci sit_bitmap_size, GFP_KERNEL); 445062306a36Sopenharmony_ci if (!sit_i->sit_bitmap_mir) 445162306a36Sopenharmony_ci return -ENOMEM; 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci sit_i->invalid_segmap = f2fs_kvzalloc(sbi, 445462306a36Sopenharmony_ci main_bitmap_size, GFP_KERNEL); 445562306a36Sopenharmony_ci if (!sit_i->invalid_segmap) 445662306a36Sopenharmony_ci return -ENOMEM; 445762306a36Sopenharmony_ci#endif 445862306a36Sopenharmony_ci 445962306a36Sopenharmony_ci sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); 446062306a36Sopenharmony_ci sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; 446162306a36Sopenharmony_ci sit_i->written_valid_blocks = 0; 446262306a36Sopenharmony_ci sit_i->bitmap_size = sit_bitmap_size; 446362306a36Sopenharmony_ci sit_i->dirty_sentries = 0; 446462306a36Sopenharmony_ci sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; 446562306a36Sopenharmony_ci sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); 446662306a36Sopenharmony_ci sit_i->mounted_time = ktime_get_boottime_seconds(); 446762306a36Sopenharmony_ci init_rwsem(&sit_i->sentry_lock); 446862306a36Sopenharmony_ci return 0; 446962306a36Sopenharmony_ci} 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_cistatic int build_free_segmap(struct f2fs_sb_info *sbi) 447262306a36Sopenharmony_ci{ 447362306a36Sopenharmony_ci struct free_segmap_info *free_i; 447462306a36Sopenharmony_ci unsigned int bitmap_size, sec_bitmap_size; 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci /* allocate memory for free segmap information */ 447762306a36Sopenharmony_ci free_i = f2fs_kzalloc(sbi, sizeof(struct free_segmap_info), GFP_KERNEL); 447862306a36Sopenharmony_ci if (!free_i) 447962306a36Sopenharmony_ci return -ENOMEM; 448062306a36Sopenharmony_ci 448162306a36Sopenharmony_ci SM_I(sbi)->free_info = free_i; 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ci bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 448462306a36Sopenharmony_ci free_i->free_segmap = f2fs_kvmalloc(sbi, bitmap_size, GFP_KERNEL); 448562306a36Sopenharmony_ci if (!free_i->free_segmap) 448662306a36Sopenharmony_ci return -ENOMEM; 448762306a36Sopenharmony_ci 448862306a36Sopenharmony_ci sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 448962306a36Sopenharmony_ci free_i->free_secmap = f2fs_kvmalloc(sbi, sec_bitmap_size, GFP_KERNEL); 449062306a36Sopenharmony_ci if (!free_i->free_secmap) 449162306a36Sopenharmony_ci return -ENOMEM; 449262306a36Sopenharmony_ci 449362306a36Sopenharmony_ci /* set all segments as dirty temporarily */ 449462306a36Sopenharmony_ci memset(free_i->free_segmap, 0xff, bitmap_size); 449562306a36Sopenharmony_ci memset(free_i->free_secmap, 0xff, sec_bitmap_size); 449662306a36Sopenharmony_ci 449762306a36Sopenharmony_ci /* init free segmap information */ 449862306a36Sopenharmony_ci free_i->start_segno = GET_SEGNO_FROM_SEG0(sbi, MAIN_BLKADDR(sbi)); 449962306a36Sopenharmony_ci free_i->free_segments = 0; 450062306a36Sopenharmony_ci free_i->free_sections = 0; 450162306a36Sopenharmony_ci spin_lock_init(&free_i->segmap_lock); 450262306a36Sopenharmony_ci return 0; 450362306a36Sopenharmony_ci} 450462306a36Sopenharmony_ci 450562306a36Sopenharmony_cistatic int build_curseg(struct f2fs_sb_info *sbi) 450662306a36Sopenharmony_ci{ 450762306a36Sopenharmony_ci struct curseg_info *array; 450862306a36Sopenharmony_ci int i; 450962306a36Sopenharmony_ci 451062306a36Sopenharmony_ci array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, 451162306a36Sopenharmony_ci sizeof(*array)), GFP_KERNEL); 451262306a36Sopenharmony_ci if (!array) 451362306a36Sopenharmony_ci return -ENOMEM; 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci SM_I(sbi)->curseg_array = array; 451662306a36Sopenharmony_ci 451762306a36Sopenharmony_ci for (i = 0; i < NO_CHECK_TYPE; i++) { 451862306a36Sopenharmony_ci mutex_init(&array[i].curseg_mutex); 451962306a36Sopenharmony_ci array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); 452062306a36Sopenharmony_ci if (!array[i].sum_blk) 452162306a36Sopenharmony_ci return -ENOMEM; 452262306a36Sopenharmony_ci init_rwsem(&array[i].journal_rwsem); 452362306a36Sopenharmony_ci array[i].journal = f2fs_kzalloc(sbi, 452462306a36Sopenharmony_ci sizeof(struct f2fs_journal), GFP_KERNEL); 452562306a36Sopenharmony_ci if (!array[i].journal) 452662306a36Sopenharmony_ci return -ENOMEM; 452762306a36Sopenharmony_ci if (i < NR_PERSISTENT_LOG) 452862306a36Sopenharmony_ci array[i].seg_type = CURSEG_HOT_DATA + i; 452962306a36Sopenharmony_ci else if (i == CURSEG_COLD_DATA_PINNED) 453062306a36Sopenharmony_ci array[i].seg_type = CURSEG_COLD_DATA; 453162306a36Sopenharmony_ci else if (i == CURSEG_ALL_DATA_ATGC) 453262306a36Sopenharmony_ci array[i].seg_type = CURSEG_COLD_DATA; 453362306a36Sopenharmony_ci array[i].segno = NULL_SEGNO; 453462306a36Sopenharmony_ci array[i].next_blkoff = 0; 453562306a36Sopenharmony_ci array[i].inited = false; 453662306a36Sopenharmony_ci } 453762306a36Sopenharmony_ci return restore_curseg_summaries(sbi); 453862306a36Sopenharmony_ci} 453962306a36Sopenharmony_ci 454062306a36Sopenharmony_cistatic int build_sit_entries(struct f2fs_sb_info *sbi) 454162306a36Sopenharmony_ci{ 454262306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 454362306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); 454462306a36Sopenharmony_ci struct f2fs_journal *journal = curseg->journal; 454562306a36Sopenharmony_ci struct seg_entry *se; 454662306a36Sopenharmony_ci struct f2fs_sit_entry sit; 454762306a36Sopenharmony_ci int sit_blk_cnt = SIT_BLK_CNT(sbi); 454862306a36Sopenharmony_ci unsigned int i, start, end; 454962306a36Sopenharmony_ci unsigned int readed, start_blk = 0; 455062306a36Sopenharmony_ci int err = 0; 455162306a36Sopenharmony_ci block_t sit_valid_blocks[2] = {0, 0}; 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_ci do { 455462306a36Sopenharmony_ci readed = f2fs_ra_meta_pages(sbi, start_blk, BIO_MAX_VECS, 455562306a36Sopenharmony_ci META_SIT, true); 455662306a36Sopenharmony_ci 455762306a36Sopenharmony_ci start = start_blk * sit_i->sents_per_block; 455862306a36Sopenharmony_ci end = (start_blk + readed) * sit_i->sents_per_block; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci for (; start < end && start < MAIN_SEGS(sbi); start++) { 456162306a36Sopenharmony_ci struct f2fs_sit_block *sit_blk; 456262306a36Sopenharmony_ci struct page *page; 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci se = &sit_i->sentries[start]; 456562306a36Sopenharmony_ci page = get_current_sit_page(sbi, start); 456662306a36Sopenharmony_ci if (IS_ERR(page)) 456762306a36Sopenharmony_ci return PTR_ERR(page); 456862306a36Sopenharmony_ci sit_blk = (struct f2fs_sit_block *)page_address(page); 456962306a36Sopenharmony_ci sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; 457062306a36Sopenharmony_ci f2fs_put_page(page, 1); 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci err = check_block_count(sbi, start, &sit); 457362306a36Sopenharmony_ci if (err) 457462306a36Sopenharmony_ci return err; 457562306a36Sopenharmony_ci seg_info_from_raw_sit(se, &sit); 457662306a36Sopenharmony_ci 457762306a36Sopenharmony_ci if (se->type >= NR_PERSISTENT_LOG) { 457862306a36Sopenharmony_ci f2fs_err(sbi, "Invalid segment type: %u, segno: %u", 457962306a36Sopenharmony_ci se->type, start); 458062306a36Sopenharmony_ci f2fs_handle_error(sbi, 458162306a36Sopenharmony_ci ERROR_INCONSISTENT_SUM_TYPE); 458262306a36Sopenharmony_ci return -EFSCORRUPTED; 458362306a36Sopenharmony_ci } 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci sit_valid_blocks[SE_PAGETYPE(se)] += se->valid_blocks; 458662306a36Sopenharmony_ci 458762306a36Sopenharmony_ci if (f2fs_block_unit_discard(sbi)) { 458862306a36Sopenharmony_ci /* build discard map only one time */ 458962306a36Sopenharmony_ci if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { 459062306a36Sopenharmony_ci memset(se->discard_map, 0xff, 459162306a36Sopenharmony_ci SIT_VBLOCK_MAP_SIZE); 459262306a36Sopenharmony_ci } else { 459362306a36Sopenharmony_ci memcpy(se->discard_map, 459462306a36Sopenharmony_ci se->cur_valid_map, 459562306a36Sopenharmony_ci SIT_VBLOCK_MAP_SIZE); 459662306a36Sopenharmony_ci sbi->discard_blks += 459762306a36Sopenharmony_ci sbi->blocks_per_seg - 459862306a36Sopenharmony_ci se->valid_blocks; 459962306a36Sopenharmony_ci } 460062306a36Sopenharmony_ci } 460162306a36Sopenharmony_ci 460262306a36Sopenharmony_ci if (__is_large_section(sbi)) 460362306a36Sopenharmony_ci get_sec_entry(sbi, start)->valid_blocks += 460462306a36Sopenharmony_ci se->valid_blocks; 460562306a36Sopenharmony_ci } 460662306a36Sopenharmony_ci start_blk += readed; 460762306a36Sopenharmony_ci } while (start_blk < sit_blk_cnt); 460862306a36Sopenharmony_ci 460962306a36Sopenharmony_ci down_read(&curseg->journal_rwsem); 461062306a36Sopenharmony_ci for (i = 0; i < sits_in_cursum(journal); i++) { 461162306a36Sopenharmony_ci unsigned int old_valid_blocks; 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci start = le32_to_cpu(segno_in_journal(journal, i)); 461462306a36Sopenharmony_ci if (start >= MAIN_SEGS(sbi)) { 461562306a36Sopenharmony_ci f2fs_err(sbi, "Wrong journal entry on segno %u", 461662306a36Sopenharmony_ci start); 461762306a36Sopenharmony_ci err = -EFSCORRUPTED; 461862306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_CORRUPTED_JOURNAL); 461962306a36Sopenharmony_ci break; 462062306a36Sopenharmony_ci } 462162306a36Sopenharmony_ci 462262306a36Sopenharmony_ci se = &sit_i->sentries[start]; 462362306a36Sopenharmony_ci sit = sit_in_journal(journal, i); 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci old_valid_blocks = se->valid_blocks; 462662306a36Sopenharmony_ci 462762306a36Sopenharmony_ci sit_valid_blocks[SE_PAGETYPE(se)] -= old_valid_blocks; 462862306a36Sopenharmony_ci 462962306a36Sopenharmony_ci err = check_block_count(sbi, start, &sit); 463062306a36Sopenharmony_ci if (err) 463162306a36Sopenharmony_ci break; 463262306a36Sopenharmony_ci seg_info_from_raw_sit(se, &sit); 463362306a36Sopenharmony_ci 463462306a36Sopenharmony_ci if (se->type >= NR_PERSISTENT_LOG) { 463562306a36Sopenharmony_ci f2fs_err(sbi, "Invalid segment type: %u, segno: %u", 463662306a36Sopenharmony_ci se->type, start); 463762306a36Sopenharmony_ci err = -EFSCORRUPTED; 463862306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INCONSISTENT_SUM_TYPE); 463962306a36Sopenharmony_ci break; 464062306a36Sopenharmony_ci } 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_ci sit_valid_blocks[SE_PAGETYPE(se)] += se->valid_blocks; 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_ci if (f2fs_block_unit_discard(sbi)) { 464562306a36Sopenharmony_ci if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) { 464662306a36Sopenharmony_ci memset(se->discard_map, 0xff, SIT_VBLOCK_MAP_SIZE); 464762306a36Sopenharmony_ci } else { 464862306a36Sopenharmony_ci memcpy(se->discard_map, se->cur_valid_map, 464962306a36Sopenharmony_ci SIT_VBLOCK_MAP_SIZE); 465062306a36Sopenharmony_ci sbi->discard_blks += old_valid_blocks; 465162306a36Sopenharmony_ci sbi->discard_blks -= se->valid_blocks; 465262306a36Sopenharmony_ci } 465362306a36Sopenharmony_ci } 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_ci if (__is_large_section(sbi)) { 465662306a36Sopenharmony_ci get_sec_entry(sbi, start)->valid_blocks += 465762306a36Sopenharmony_ci se->valid_blocks; 465862306a36Sopenharmony_ci get_sec_entry(sbi, start)->valid_blocks -= 465962306a36Sopenharmony_ci old_valid_blocks; 466062306a36Sopenharmony_ci } 466162306a36Sopenharmony_ci } 466262306a36Sopenharmony_ci up_read(&curseg->journal_rwsem); 466362306a36Sopenharmony_ci 466462306a36Sopenharmony_ci if (err) 466562306a36Sopenharmony_ci return err; 466662306a36Sopenharmony_ci 466762306a36Sopenharmony_ci if (sit_valid_blocks[NODE] != valid_node_count(sbi)) { 466862306a36Sopenharmony_ci f2fs_err(sbi, "SIT is corrupted node# %u vs %u", 466962306a36Sopenharmony_ci sit_valid_blocks[NODE], valid_node_count(sbi)); 467062306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INCONSISTENT_NODE_COUNT); 467162306a36Sopenharmony_ci return -EFSCORRUPTED; 467262306a36Sopenharmony_ci } 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_ci if (sit_valid_blocks[DATA] + sit_valid_blocks[NODE] > 467562306a36Sopenharmony_ci valid_user_blocks(sbi)) { 467662306a36Sopenharmony_ci f2fs_err(sbi, "SIT is corrupted data# %u %u vs %u", 467762306a36Sopenharmony_ci sit_valid_blocks[DATA], sit_valid_blocks[NODE], 467862306a36Sopenharmony_ci valid_user_blocks(sbi)); 467962306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INCONSISTENT_BLOCK_COUNT); 468062306a36Sopenharmony_ci return -EFSCORRUPTED; 468162306a36Sopenharmony_ci } 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci return 0; 468462306a36Sopenharmony_ci} 468562306a36Sopenharmony_ci 468662306a36Sopenharmony_cistatic void init_free_segmap(struct f2fs_sb_info *sbi) 468762306a36Sopenharmony_ci{ 468862306a36Sopenharmony_ci unsigned int start; 468962306a36Sopenharmony_ci int type; 469062306a36Sopenharmony_ci struct seg_entry *sentry; 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_ci for (start = 0; start < MAIN_SEGS(sbi); start++) { 469362306a36Sopenharmony_ci if (f2fs_usable_blks_in_seg(sbi, start) == 0) 469462306a36Sopenharmony_ci continue; 469562306a36Sopenharmony_ci sentry = get_seg_entry(sbi, start); 469662306a36Sopenharmony_ci if (!sentry->valid_blocks) 469762306a36Sopenharmony_ci __set_free(sbi, start); 469862306a36Sopenharmony_ci else 469962306a36Sopenharmony_ci SIT_I(sbi)->written_valid_blocks += 470062306a36Sopenharmony_ci sentry->valid_blocks; 470162306a36Sopenharmony_ci } 470262306a36Sopenharmony_ci 470362306a36Sopenharmony_ci /* set use the current segments */ 470462306a36Sopenharmony_ci for (type = CURSEG_HOT_DATA; type <= CURSEG_COLD_NODE; type++) { 470562306a36Sopenharmony_ci struct curseg_info *curseg_t = CURSEG_I(sbi, type); 470662306a36Sopenharmony_ci 470762306a36Sopenharmony_ci __set_test_and_inuse(sbi, curseg_t->segno); 470862306a36Sopenharmony_ci } 470962306a36Sopenharmony_ci} 471062306a36Sopenharmony_ci 471162306a36Sopenharmony_cistatic void init_dirty_segmap(struct f2fs_sb_info *sbi) 471262306a36Sopenharmony_ci{ 471362306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 471462306a36Sopenharmony_ci struct free_segmap_info *free_i = FREE_I(sbi); 471562306a36Sopenharmony_ci unsigned int segno = 0, offset = 0, secno; 471662306a36Sopenharmony_ci block_t valid_blocks, usable_blks_in_seg; 471762306a36Sopenharmony_ci 471862306a36Sopenharmony_ci while (1) { 471962306a36Sopenharmony_ci /* find dirty segment based on free segmap */ 472062306a36Sopenharmony_ci segno = find_next_inuse(free_i, MAIN_SEGS(sbi), offset); 472162306a36Sopenharmony_ci if (segno >= MAIN_SEGS(sbi)) 472262306a36Sopenharmony_ci break; 472362306a36Sopenharmony_ci offset = segno + 1; 472462306a36Sopenharmony_ci valid_blocks = get_valid_blocks(sbi, segno, false); 472562306a36Sopenharmony_ci usable_blks_in_seg = f2fs_usable_blks_in_seg(sbi, segno); 472662306a36Sopenharmony_ci if (valid_blocks == usable_blks_in_seg || !valid_blocks) 472762306a36Sopenharmony_ci continue; 472862306a36Sopenharmony_ci if (valid_blocks > usable_blks_in_seg) { 472962306a36Sopenharmony_ci f2fs_bug_on(sbi, 1); 473062306a36Sopenharmony_ci continue; 473162306a36Sopenharmony_ci } 473262306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 473362306a36Sopenharmony_ci __locate_dirty_segment(sbi, segno, DIRTY); 473462306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 473562306a36Sopenharmony_ci } 473662306a36Sopenharmony_ci 473762306a36Sopenharmony_ci if (!__is_large_section(sbi)) 473862306a36Sopenharmony_ci return; 473962306a36Sopenharmony_ci 474062306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 474162306a36Sopenharmony_ci for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { 474262306a36Sopenharmony_ci valid_blocks = get_valid_blocks(sbi, segno, true); 474362306a36Sopenharmony_ci secno = GET_SEC_FROM_SEG(sbi, segno); 474462306a36Sopenharmony_ci 474562306a36Sopenharmony_ci if (!valid_blocks || valid_blocks == CAP_BLKS_PER_SEC(sbi)) 474662306a36Sopenharmony_ci continue; 474762306a36Sopenharmony_ci if (IS_CURSEC(sbi, secno)) 474862306a36Sopenharmony_ci continue; 474962306a36Sopenharmony_ci set_bit(secno, dirty_i->dirty_secmap); 475062306a36Sopenharmony_ci } 475162306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 475262306a36Sopenharmony_ci} 475362306a36Sopenharmony_ci 475462306a36Sopenharmony_cistatic int init_victim_secmap(struct f2fs_sb_info *sbi) 475562306a36Sopenharmony_ci{ 475662306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 475762306a36Sopenharmony_ci unsigned int bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 475862306a36Sopenharmony_ci 475962306a36Sopenharmony_ci dirty_i->victim_secmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 476062306a36Sopenharmony_ci if (!dirty_i->victim_secmap) 476162306a36Sopenharmony_ci return -ENOMEM; 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci dirty_i->pinned_secmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL); 476462306a36Sopenharmony_ci if (!dirty_i->pinned_secmap) 476562306a36Sopenharmony_ci return -ENOMEM; 476662306a36Sopenharmony_ci 476762306a36Sopenharmony_ci dirty_i->pinned_secmap_cnt = 0; 476862306a36Sopenharmony_ci dirty_i->enable_pin_section = true; 476962306a36Sopenharmony_ci return 0; 477062306a36Sopenharmony_ci} 477162306a36Sopenharmony_ci 477262306a36Sopenharmony_cistatic int build_dirty_segmap(struct f2fs_sb_info *sbi) 477362306a36Sopenharmony_ci{ 477462306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i; 477562306a36Sopenharmony_ci unsigned int bitmap_size, i; 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci /* allocate memory for dirty segments list information */ 477862306a36Sopenharmony_ci dirty_i = f2fs_kzalloc(sbi, sizeof(struct dirty_seglist_info), 477962306a36Sopenharmony_ci GFP_KERNEL); 478062306a36Sopenharmony_ci if (!dirty_i) 478162306a36Sopenharmony_ci return -ENOMEM; 478262306a36Sopenharmony_ci 478362306a36Sopenharmony_ci SM_I(sbi)->dirty_info = dirty_i; 478462306a36Sopenharmony_ci mutex_init(&dirty_i->seglist_lock); 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_ci bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi)); 478762306a36Sopenharmony_ci 478862306a36Sopenharmony_ci for (i = 0; i < NR_DIRTY_TYPE; i++) { 478962306a36Sopenharmony_ci dirty_i->dirty_segmap[i] = f2fs_kvzalloc(sbi, bitmap_size, 479062306a36Sopenharmony_ci GFP_KERNEL); 479162306a36Sopenharmony_ci if (!dirty_i->dirty_segmap[i]) 479262306a36Sopenharmony_ci return -ENOMEM; 479362306a36Sopenharmony_ci } 479462306a36Sopenharmony_ci 479562306a36Sopenharmony_ci if (__is_large_section(sbi)) { 479662306a36Sopenharmony_ci bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi)); 479762306a36Sopenharmony_ci dirty_i->dirty_secmap = f2fs_kvzalloc(sbi, 479862306a36Sopenharmony_ci bitmap_size, GFP_KERNEL); 479962306a36Sopenharmony_ci if (!dirty_i->dirty_secmap) 480062306a36Sopenharmony_ci return -ENOMEM; 480162306a36Sopenharmony_ci } 480262306a36Sopenharmony_ci 480362306a36Sopenharmony_ci init_dirty_segmap(sbi); 480462306a36Sopenharmony_ci return init_victim_secmap(sbi); 480562306a36Sopenharmony_ci} 480662306a36Sopenharmony_ci 480762306a36Sopenharmony_cistatic int sanity_check_curseg(struct f2fs_sb_info *sbi) 480862306a36Sopenharmony_ci{ 480962306a36Sopenharmony_ci int i; 481062306a36Sopenharmony_ci 481162306a36Sopenharmony_ci /* 481262306a36Sopenharmony_ci * In LFS/SSR curseg, .next_blkoff should point to an unused blkaddr; 481362306a36Sopenharmony_ci * In LFS curseg, all blkaddr after .next_blkoff should be unused. 481462306a36Sopenharmony_ci */ 481562306a36Sopenharmony_ci for (i = 0; i < NR_PERSISTENT_LOG; i++) { 481662306a36Sopenharmony_ci struct curseg_info *curseg = CURSEG_I(sbi, i); 481762306a36Sopenharmony_ci struct seg_entry *se = get_seg_entry(sbi, curseg->segno); 481862306a36Sopenharmony_ci unsigned int blkofs = curseg->next_blkoff; 481962306a36Sopenharmony_ci 482062306a36Sopenharmony_ci if (f2fs_sb_has_readonly(sbi) && 482162306a36Sopenharmony_ci i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE) 482262306a36Sopenharmony_ci continue; 482362306a36Sopenharmony_ci 482462306a36Sopenharmony_ci sanity_check_seg_type(sbi, curseg->seg_type); 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci if (curseg->alloc_type != LFS && curseg->alloc_type != SSR) { 482762306a36Sopenharmony_ci f2fs_err(sbi, 482862306a36Sopenharmony_ci "Current segment has invalid alloc_type:%d", 482962306a36Sopenharmony_ci curseg->alloc_type); 483062306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INVALID_CURSEG); 483162306a36Sopenharmony_ci return -EFSCORRUPTED; 483262306a36Sopenharmony_ci } 483362306a36Sopenharmony_ci 483462306a36Sopenharmony_ci if (f2fs_test_bit(blkofs, se->cur_valid_map)) 483562306a36Sopenharmony_ci goto out; 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_ci if (curseg->alloc_type == SSR) 483862306a36Sopenharmony_ci continue; 483962306a36Sopenharmony_ci 484062306a36Sopenharmony_ci for (blkofs += 1; blkofs < sbi->blocks_per_seg; blkofs++) { 484162306a36Sopenharmony_ci if (!f2fs_test_bit(blkofs, se->cur_valid_map)) 484262306a36Sopenharmony_ci continue; 484362306a36Sopenharmony_ciout: 484462306a36Sopenharmony_ci f2fs_err(sbi, 484562306a36Sopenharmony_ci "Current segment's next free block offset is inconsistent with bitmap, logtype:%u, segno:%u, type:%u, next_blkoff:%u, blkofs:%u", 484662306a36Sopenharmony_ci i, curseg->segno, curseg->alloc_type, 484762306a36Sopenharmony_ci curseg->next_blkoff, blkofs); 484862306a36Sopenharmony_ci f2fs_handle_error(sbi, ERROR_INVALID_CURSEG); 484962306a36Sopenharmony_ci return -EFSCORRUPTED; 485062306a36Sopenharmony_ci } 485162306a36Sopenharmony_ci } 485262306a36Sopenharmony_ci return 0; 485362306a36Sopenharmony_ci} 485462306a36Sopenharmony_ci 485562306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_ZONED 485662306a36Sopenharmony_ci 485762306a36Sopenharmony_cistatic int check_zone_write_pointer(struct f2fs_sb_info *sbi, 485862306a36Sopenharmony_ci struct f2fs_dev_info *fdev, 485962306a36Sopenharmony_ci struct blk_zone *zone) 486062306a36Sopenharmony_ci{ 486162306a36Sopenharmony_ci unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno; 486262306a36Sopenharmony_ci block_t zone_block, wp_block, last_valid_block; 486362306a36Sopenharmony_ci unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; 486462306a36Sopenharmony_ci int i, s, b, ret; 486562306a36Sopenharmony_ci struct seg_entry *se; 486662306a36Sopenharmony_ci 486762306a36Sopenharmony_ci if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ) 486862306a36Sopenharmony_ci return 0; 486962306a36Sopenharmony_ci 487062306a36Sopenharmony_ci wp_block = fdev->start_blk + (zone->wp >> log_sectors_per_block); 487162306a36Sopenharmony_ci wp_segno = GET_SEGNO(sbi, wp_block); 487262306a36Sopenharmony_ci wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); 487362306a36Sopenharmony_ci zone_block = fdev->start_blk + (zone->start >> log_sectors_per_block); 487462306a36Sopenharmony_ci zone_segno = GET_SEGNO(sbi, zone_block); 487562306a36Sopenharmony_ci zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno); 487662306a36Sopenharmony_ci 487762306a36Sopenharmony_ci if (zone_segno >= MAIN_SEGS(sbi)) 487862306a36Sopenharmony_ci return 0; 487962306a36Sopenharmony_ci 488062306a36Sopenharmony_ci /* 488162306a36Sopenharmony_ci * Skip check of zones cursegs point to, since 488262306a36Sopenharmony_ci * fix_curseg_write_pointer() checks them. 488362306a36Sopenharmony_ci */ 488462306a36Sopenharmony_ci for (i = 0; i < NO_CHECK_TYPE; i++) 488562306a36Sopenharmony_ci if (zone_secno == GET_SEC_FROM_SEG(sbi, 488662306a36Sopenharmony_ci CURSEG_I(sbi, i)->segno)) 488762306a36Sopenharmony_ci return 0; 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci /* 489062306a36Sopenharmony_ci * Get last valid block of the zone. 489162306a36Sopenharmony_ci */ 489262306a36Sopenharmony_ci last_valid_block = zone_block - 1; 489362306a36Sopenharmony_ci for (s = sbi->segs_per_sec - 1; s >= 0; s--) { 489462306a36Sopenharmony_ci segno = zone_segno + s; 489562306a36Sopenharmony_ci se = get_seg_entry(sbi, segno); 489662306a36Sopenharmony_ci for (b = sbi->blocks_per_seg - 1; b >= 0; b--) 489762306a36Sopenharmony_ci if (f2fs_test_bit(b, se->cur_valid_map)) { 489862306a36Sopenharmony_ci last_valid_block = START_BLOCK(sbi, segno) + b; 489962306a36Sopenharmony_ci break; 490062306a36Sopenharmony_ci } 490162306a36Sopenharmony_ci if (last_valid_block >= zone_block) 490262306a36Sopenharmony_ci break; 490362306a36Sopenharmony_ci } 490462306a36Sopenharmony_ci 490562306a36Sopenharmony_ci /* 490662306a36Sopenharmony_ci * The write pointer matches with the valid blocks or 490762306a36Sopenharmony_ci * already points to the end of the zone. 490862306a36Sopenharmony_ci */ 490962306a36Sopenharmony_ci if ((last_valid_block + 1 == wp_block) || 491062306a36Sopenharmony_ci (zone->wp == zone->start + zone->len)) 491162306a36Sopenharmony_ci return 0; 491262306a36Sopenharmony_ci 491362306a36Sopenharmony_ci if (last_valid_block + 1 == zone_block) { 491462306a36Sopenharmony_ci /* 491562306a36Sopenharmony_ci * If there is no valid block in the zone and if write pointer 491662306a36Sopenharmony_ci * is not at zone start, reset the write pointer. 491762306a36Sopenharmony_ci */ 491862306a36Sopenharmony_ci f2fs_notice(sbi, 491962306a36Sopenharmony_ci "Zone without valid block has non-zero write " 492062306a36Sopenharmony_ci "pointer. Reset the write pointer: wp[0x%x,0x%x]", 492162306a36Sopenharmony_ci wp_segno, wp_blkoff); 492262306a36Sopenharmony_ci ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block, 492362306a36Sopenharmony_ci zone->len >> log_sectors_per_block); 492462306a36Sopenharmony_ci if (ret) 492562306a36Sopenharmony_ci f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", 492662306a36Sopenharmony_ci fdev->path, ret); 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci return ret; 492962306a36Sopenharmony_ci } 493062306a36Sopenharmony_ci 493162306a36Sopenharmony_ci /* 493262306a36Sopenharmony_ci * If there are valid blocks and the write pointer doesn't 493362306a36Sopenharmony_ci * match with them, we need to report the inconsistency and 493462306a36Sopenharmony_ci * fill the zone till the end to close the zone. This inconsistency 493562306a36Sopenharmony_ci * does not cause write error because the zone will not be selected 493662306a36Sopenharmony_ci * for write operation until it get discarded. 493762306a36Sopenharmony_ci */ 493862306a36Sopenharmony_ci f2fs_notice(sbi, "Valid blocks are not aligned with write pointer: " 493962306a36Sopenharmony_ci "valid block[0x%x,0x%x] wp[0x%x,0x%x]", 494062306a36Sopenharmony_ci GET_SEGNO(sbi, last_valid_block), 494162306a36Sopenharmony_ci GET_BLKOFF_FROM_SEG0(sbi, last_valid_block), 494262306a36Sopenharmony_ci wp_segno, wp_blkoff); 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci ret = blkdev_zone_mgmt(fdev->bdev, REQ_OP_ZONE_FINISH, 494562306a36Sopenharmony_ci zone->start, zone->len, GFP_NOFS); 494662306a36Sopenharmony_ci if (ret == -EOPNOTSUPP) { 494762306a36Sopenharmony_ci ret = blkdev_issue_zeroout(fdev->bdev, zone->wp, 494862306a36Sopenharmony_ci zone->len - (zone->wp - zone->start), 494962306a36Sopenharmony_ci GFP_NOFS, 0); 495062306a36Sopenharmony_ci if (ret) 495162306a36Sopenharmony_ci f2fs_err(sbi, "Fill up zone failed: %s (errno=%d)", 495262306a36Sopenharmony_ci fdev->path, ret); 495362306a36Sopenharmony_ci } else if (ret) { 495462306a36Sopenharmony_ci f2fs_err(sbi, "Finishing zone failed: %s (errno=%d)", 495562306a36Sopenharmony_ci fdev->path, ret); 495662306a36Sopenharmony_ci } 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci return ret; 495962306a36Sopenharmony_ci} 496062306a36Sopenharmony_ci 496162306a36Sopenharmony_cistatic struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi, 496262306a36Sopenharmony_ci block_t zone_blkaddr) 496362306a36Sopenharmony_ci{ 496462306a36Sopenharmony_ci int i; 496562306a36Sopenharmony_ci 496662306a36Sopenharmony_ci for (i = 0; i < sbi->s_ndevs; i++) { 496762306a36Sopenharmony_ci if (!bdev_is_zoned(FDEV(i).bdev)) 496862306a36Sopenharmony_ci continue; 496962306a36Sopenharmony_ci if (sbi->s_ndevs == 1 || (FDEV(i).start_blk <= zone_blkaddr && 497062306a36Sopenharmony_ci zone_blkaddr <= FDEV(i).end_blk)) 497162306a36Sopenharmony_ci return &FDEV(i); 497262306a36Sopenharmony_ci } 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci return NULL; 497562306a36Sopenharmony_ci} 497662306a36Sopenharmony_ci 497762306a36Sopenharmony_cistatic int report_one_zone_cb(struct blk_zone *zone, unsigned int idx, 497862306a36Sopenharmony_ci void *data) 497962306a36Sopenharmony_ci{ 498062306a36Sopenharmony_ci memcpy(data, zone, sizeof(struct blk_zone)); 498162306a36Sopenharmony_ci return 0; 498262306a36Sopenharmony_ci} 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_cistatic int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) 498562306a36Sopenharmony_ci{ 498662306a36Sopenharmony_ci struct curseg_info *cs = CURSEG_I(sbi, type); 498762306a36Sopenharmony_ci struct f2fs_dev_info *zbd; 498862306a36Sopenharmony_ci struct blk_zone zone; 498962306a36Sopenharmony_ci unsigned int cs_section, wp_segno, wp_blkoff, wp_sector_off; 499062306a36Sopenharmony_ci block_t cs_zone_block, wp_block; 499162306a36Sopenharmony_ci unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; 499262306a36Sopenharmony_ci sector_t zone_sector; 499362306a36Sopenharmony_ci int err; 499462306a36Sopenharmony_ci 499562306a36Sopenharmony_ci cs_section = GET_SEC_FROM_SEG(sbi, cs->segno); 499662306a36Sopenharmony_ci cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section)); 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci zbd = get_target_zoned_dev(sbi, cs_zone_block); 499962306a36Sopenharmony_ci if (!zbd) 500062306a36Sopenharmony_ci return 0; 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci /* report zone for the sector the curseg points to */ 500362306a36Sopenharmony_ci zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) 500462306a36Sopenharmony_ci << log_sectors_per_block; 500562306a36Sopenharmony_ci err = blkdev_report_zones(zbd->bdev, zone_sector, 1, 500662306a36Sopenharmony_ci report_one_zone_cb, &zone); 500762306a36Sopenharmony_ci if (err != 1) { 500862306a36Sopenharmony_ci f2fs_err(sbi, "Report zone failed: %s errno=(%d)", 500962306a36Sopenharmony_ci zbd->path, err); 501062306a36Sopenharmony_ci return err; 501162306a36Sopenharmony_ci } 501262306a36Sopenharmony_ci 501362306a36Sopenharmony_ci if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) 501462306a36Sopenharmony_ci return 0; 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_ci wp_block = zbd->start_blk + (zone.wp >> log_sectors_per_block); 501762306a36Sopenharmony_ci wp_segno = GET_SEGNO(sbi, wp_block); 501862306a36Sopenharmony_ci wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); 501962306a36Sopenharmony_ci wp_sector_off = zone.wp & GENMASK(log_sectors_per_block - 1, 0); 502062306a36Sopenharmony_ci 502162306a36Sopenharmony_ci if (cs->segno == wp_segno && cs->next_blkoff == wp_blkoff && 502262306a36Sopenharmony_ci wp_sector_off == 0) 502362306a36Sopenharmony_ci return 0; 502462306a36Sopenharmony_ci 502562306a36Sopenharmony_ci f2fs_notice(sbi, "Unaligned curseg[%d] with write pointer: " 502662306a36Sopenharmony_ci "curseg[0x%x,0x%x] wp[0x%x,0x%x]", 502762306a36Sopenharmony_ci type, cs->segno, cs->next_blkoff, wp_segno, wp_blkoff); 502862306a36Sopenharmony_ci 502962306a36Sopenharmony_ci f2fs_notice(sbi, "Assign new section to curseg[%d]: " 503062306a36Sopenharmony_ci "curseg[0x%x,0x%x]", type, cs->segno, cs->next_blkoff); 503162306a36Sopenharmony_ci 503262306a36Sopenharmony_ci f2fs_allocate_new_section(sbi, type, true); 503362306a36Sopenharmony_ci 503462306a36Sopenharmony_ci /* check consistency of the zone curseg pointed to */ 503562306a36Sopenharmony_ci if (check_zone_write_pointer(sbi, zbd, &zone)) 503662306a36Sopenharmony_ci return -EIO; 503762306a36Sopenharmony_ci 503862306a36Sopenharmony_ci /* check newly assigned zone */ 503962306a36Sopenharmony_ci cs_section = GET_SEC_FROM_SEG(sbi, cs->segno); 504062306a36Sopenharmony_ci cs_zone_block = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, cs_section)); 504162306a36Sopenharmony_ci 504262306a36Sopenharmony_ci zbd = get_target_zoned_dev(sbi, cs_zone_block); 504362306a36Sopenharmony_ci if (!zbd) 504462306a36Sopenharmony_ci return 0; 504562306a36Sopenharmony_ci 504662306a36Sopenharmony_ci zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) 504762306a36Sopenharmony_ci << log_sectors_per_block; 504862306a36Sopenharmony_ci err = blkdev_report_zones(zbd->bdev, zone_sector, 1, 504962306a36Sopenharmony_ci report_one_zone_cb, &zone); 505062306a36Sopenharmony_ci if (err != 1) { 505162306a36Sopenharmony_ci f2fs_err(sbi, "Report zone failed: %s errno=(%d)", 505262306a36Sopenharmony_ci zbd->path, err); 505362306a36Sopenharmony_ci return err; 505462306a36Sopenharmony_ci } 505562306a36Sopenharmony_ci 505662306a36Sopenharmony_ci if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) 505762306a36Sopenharmony_ci return 0; 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci if (zone.wp != zone.start) { 506062306a36Sopenharmony_ci f2fs_notice(sbi, 506162306a36Sopenharmony_ci "New zone for curseg[%d] is not yet discarded. " 506262306a36Sopenharmony_ci "Reset the zone: curseg[0x%x,0x%x]", 506362306a36Sopenharmony_ci type, cs->segno, cs->next_blkoff); 506462306a36Sopenharmony_ci err = __f2fs_issue_discard_zone(sbi, zbd->bdev, cs_zone_block, 506562306a36Sopenharmony_ci zone.len >> log_sectors_per_block); 506662306a36Sopenharmony_ci if (err) { 506762306a36Sopenharmony_ci f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", 506862306a36Sopenharmony_ci zbd->path, err); 506962306a36Sopenharmony_ci return err; 507062306a36Sopenharmony_ci } 507162306a36Sopenharmony_ci } 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_ci return 0; 507462306a36Sopenharmony_ci} 507562306a36Sopenharmony_ci 507662306a36Sopenharmony_ciint f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) 507762306a36Sopenharmony_ci{ 507862306a36Sopenharmony_ci int i, ret; 507962306a36Sopenharmony_ci 508062306a36Sopenharmony_ci for (i = 0; i < NR_PERSISTENT_LOG; i++) { 508162306a36Sopenharmony_ci ret = fix_curseg_write_pointer(sbi, i); 508262306a36Sopenharmony_ci if (ret) 508362306a36Sopenharmony_ci return ret; 508462306a36Sopenharmony_ci } 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_ci return 0; 508762306a36Sopenharmony_ci} 508862306a36Sopenharmony_ci 508962306a36Sopenharmony_cistruct check_zone_write_pointer_args { 509062306a36Sopenharmony_ci struct f2fs_sb_info *sbi; 509162306a36Sopenharmony_ci struct f2fs_dev_info *fdev; 509262306a36Sopenharmony_ci}; 509362306a36Sopenharmony_ci 509462306a36Sopenharmony_cistatic int check_zone_write_pointer_cb(struct blk_zone *zone, unsigned int idx, 509562306a36Sopenharmony_ci void *data) 509662306a36Sopenharmony_ci{ 509762306a36Sopenharmony_ci struct check_zone_write_pointer_args *args; 509862306a36Sopenharmony_ci 509962306a36Sopenharmony_ci args = (struct check_zone_write_pointer_args *)data; 510062306a36Sopenharmony_ci 510162306a36Sopenharmony_ci return check_zone_write_pointer(args->sbi, args->fdev, zone); 510262306a36Sopenharmony_ci} 510362306a36Sopenharmony_ci 510462306a36Sopenharmony_ciint f2fs_check_write_pointer(struct f2fs_sb_info *sbi) 510562306a36Sopenharmony_ci{ 510662306a36Sopenharmony_ci int i, ret; 510762306a36Sopenharmony_ci struct check_zone_write_pointer_args args; 510862306a36Sopenharmony_ci 510962306a36Sopenharmony_ci for (i = 0; i < sbi->s_ndevs; i++) { 511062306a36Sopenharmony_ci if (!bdev_is_zoned(FDEV(i).bdev)) 511162306a36Sopenharmony_ci continue; 511262306a36Sopenharmony_ci 511362306a36Sopenharmony_ci args.sbi = sbi; 511462306a36Sopenharmony_ci args.fdev = &FDEV(i); 511562306a36Sopenharmony_ci ret = blkdev_report_zones(FDEV(i).bdev, 0, BLK_ALL_ZONES, 511662306a36Sopenharmony_ci check_zone_write_pointer_cb, &args); 511762306a36Sopenharmony_ci if (ret < 0) 511862306a36Sopenharmony_ci return ret; 511962306a36Sopenharmony_ci } 512062306a36Sopenharmony_ci 512162306a36Sopenharmony_ci return 0; 512262306a36Sopenharmony_ci} 512362306a36Sopenharmony_ci 512462306a36Sopenharmony_ci/* 512562306a36Sopenharmony_ci * Return the number of usable blocks in a segment. The number of blocks 512662306a36Sopenharmony_ci * returned is always equal to the number of blocks in a segment for 512762306a36Sopenharmony_ci * segments fully contained within a sequential zone capacity or a 512862306a36Sopenharmony_ci * conventional zone. For segments partially contained in a sequential 512962306a36Sopenharmony_ci * zone capacity, the number of usable blocks up to the zone capacity 513062306a36Sopenharmony_ci * is returned. 0 is returned in all other cases. 513162306a36Sopenharmony_ci */ 513262306a36Sopenharmony_cistatic inline unsigned int f2fs_usable_zone_blks_in_seg( 513362306a36Sopenharmony_ci struct f2fs_sb_info *sbi, unsigned int segno) 513462306a36Sopenharmony_ci{ 513562306a36Sopenharmony_ci block_t seg_start, sec_start_blkaddr, sec_cap_blkaddr; 513662306a36Sopenharmony_ci unsigned int secno; 513762306a36Sopenharmony_ci 513862306a36Sopenharmony_ci if (!sbi->unusable_blocks_per_sec) 513962306a36Sopenharmony_ci return sbi->blocks_per_seg; 514062306a36Sopenharmony_ci 514162306a36Sopenharmony_ci secno = GET_SEC_FROM_SEG(sbi, segno); 514262306a36Sopenharmony_ci seg_start = START_BLOCK(sbi, segno); 514362306a36Sopenharmony_ci sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno)); 514462306a36Sopenharmony_ci sec_cap_blkaddr = sec_start_blkaddr + CAP_BLKS_PER_SEC(sbi); 514562306a36Sopenharmony_ci 514662306a36Sopenharmony_ci /* 514762306a36Sopenharmony_ci * If segment starts before zone capacity and spans beyond 514862306a36Sopenharmony_ci * zone capacity, then usable blocks are from seg start to 514962306a36Sopenharmony_ci * zone capacity. If the segment starts after the zone capacity, 515062306a36Sopenharmony_ci * then there are no usable blocks. 515162306a36Sopenharmony_ci */ 515262306a36Sopenharmony_ci if (seg_start >= sec_cap_blkaddr) 515362306a36Sopenharmony_ci return 0; 515462306a36Sopenharmony_ci if (seg_start + sbi->blocks_per_seg > sec_cap_blkaddr) 515562306a36Sopenharmony_ci return sec_cap_blkaddr - seg_start; 515662306a36Sopenharmony_ci 515762306a36Sopenharmony_ci return sbi->blocks_per_seg; 515862306a36Sopenharmony_ci} 515962306a36Sopenharmony_ci#else 516062306a36Sopenharmony_ciint f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi) 516162306a36Sopenharmony_ci{ 516262306a36Sopenharmony_ci return 0; 516362306a36Sopenharmony_ci} 516462306a36Sopenharmony_ci 516562306a36Sopenharmony_ciint f2fs_check_write_pointer(struct f2fs_sb_info *sbi) 516662306a36Sopenharmony_ci{ 516762306a36Sopenharmony_ci return 0; 516862306a36Sopenharmony_ci} 516962306a36Sopenharmony_ci 517062306a36Sopenharmony_cistatic inline unsigned int f2fs_usable_zone_blks_in_seg(struct f2fs_sb_info *sbi, 517162306a36Sopenharmony_ci unsigned int segno) 517262306a36Sopenharmony_ci{ 517362306a36Sopenharmony_ci return 0; 517462306a36Sopenharmony_ci} 517562306a36Sopenharmony_ci 517662306a36Sopenharmony_ci#endif 517762306a36Sopenharmony_ciunsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi, 517862306a36Sopenharmony_ci unsigned int segno) 517962306a36Sopenharmony_ci{ 518062306a36Sopenharmony_ci if (f2fs_sb_has_blkzoned(sbi)) 518162306a36Sopenharmony_ci return f2fs_usable_zone_blks_in_seg(sbi, segno); 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_ci return sbi->blocks_per_seg; 518462306a36Sopenharmony_ci} 518562306a36Sopenharmony_ci 518662306a36Sopenharmony_ciunsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi, 518762306a36Sopenharmony_ci unsigned int segno) 518862306a36Sopenharmony_ci{ 518962306a36Sopenharmony_ci if (f2fs_sb_has_blkzoned(sbi)) 519062306a36Sopenharmony_ci return CAP_SEGS_PER_SEC(sbi); 519162306a36Sopenharmony_ci 519262306a36Sopenharmony_ci return sbi->segs_per_sec; 519362306a36Sopenharmony_ci} 519462306a36Sopenharmony_ci 519562306a36Sopenharmony_ci/* 519662306a36Sopenharmony_ci * Update min, max modified time for cost-benefit GC algorithm 519762306a36Sopenharmony_ci */ 519862306a36Sopenharmony_cistatic void init_min_max_mtime(struct f2fs_sb_info *sbi) 519962306a36Sopenharmony_ci{ 520062306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 520162306a36Sopenharmony_ci unsigned int segno; 520262306a36Sopenharmony_ci 520362306a36Sopenharmony_ci down_write(&sit_i->sentry_lock); 520462306a36Sopenharmony_ci 520562306a36Sopenharmony_ci sit_i->min_mtime = ULLONG_MAX; 520662306a36Sopenharmony_ci 520762306a36Sopenharmony_ci for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) { 520862306a36Sopenharmony_ci unsigned int i; 520962306a36Sopenharmony_ci unsigned long long mtime = 0; 521062306a36Sopenharmony_ci 521162306a36Sopenharmony_ci for (i = 0; i < sbi->segs_per_sec; i++) 521262306a36Sopenharmony_ci mtime += get_seg_entry(sbi, segno + i)->mtime; 521362306a36Sopenharmony_ci 521462306a36Sopenharmony_ci mtime = div_u64(mtime, sbi->segs_per_sec); 521562306a36Sopenharmony_ci 521662306a36Sopenharmony_ci if (sit_i->min_mtime > mtime) 521762306a36Sopenharmony_ci sit_i->min_mtime = mtime; 521862306a36Sopenharmony_ci } 521962306a36Sopenharmony_ci sit_i->max_mtime = get_mtime(sbi, false); 522062306a36Sopenharmony_ci sit_i->dirty_max_mtime = 0; 522162306a36Sopenharmony_ci up_write(&sit_i->sentry_lock); 522262306a36Sopenharmony_ci} 522362306a36Sopenharmony_ci 522462306a36Sopenharmony_ciint f2fs_build_segment_manager(struct f2fs_sb_info *sbi) 522562306a36Sopenharmony_ci{ 522662306a36Sopenharmony_ci struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); 522762306a36Sopenharmony_ci struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); 522862306a36Sopenharmony_ci struct f2fs_sm_info *sm_info; 522962306a36Sopenharmony_ci int err; 523062306a36Sopenharmony_ci 523162306a36Sopenharmony_ci sm_info = f2fs_kzalloc(sbi, sizeof(struct f2fs_sm_info), GFP_KERNEL); 523262306a36Sopenharmony_ci if (!sm_info) 523362306a36Sopenharmony_ci return -ENOMEM; 523462306a36Sopenharmony_ci 523562306a36Sopenharmony_ci /* init sm info */ 523662306a36Sopenharmony_ci sbi->sm_info = sm_info; 523762306a36Sopenharmony_ci sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); 523862306a36Sopenharmony_ci sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); 523962306a36Sopenharmony_ci sm_info->segment_count = le32_to_cpu(raw_super->segment_count); 524062306a36Sopenharmony_ci sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); 524162306a36Sopenharmony_ci sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); 524262306a36Sopenharmony_ci sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main); 524362306a36Sopenharmony_ci sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); 524462306a36Sopenharmony_ci sm_info->rec_prefree_segments = sm_info->main_segments * 524562306a36Sopenharmony_ci DEF_RECLAIM_PREFREE_SEGMENTS / 100; 524662306a36Sopenharmony_ci if (sm_info->rec_prefree_segments > DEF_MAX_RECLAIM_PREFREE_SEGMENTS) 524762306a36Sopenharmony_ci sm_info->rec_prefree_segments = DEF_MAX_RECLAIM_PREFREE_SEGMENTS; 524862306a36Sopenharmony_ci 524962306a36Sopenharmony_ci if (!f2fs_lfs_mode(sbi)) 525062306a36Sopenharmony_ci sm_info->ipu_policy = BIT(F2FS_IPU_FSYNC); 525162306a36Sopenharmony_ci sm_info->min_ipu_util = DEF_MIN_IPU_UTIL; 525262306a36Sopenharmony_ci sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS; 525362306a36Sopenharmony_ci sm_info->min_seq_blocks = sbi->blocks_per_seg; 525462306a36Sopenharmony_ci sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS; 525562306a36Sopenharmony_ci sm_info->min_ssr_sections = reserved_sections(sbi); 525662306a36Sopenharmony_ci 525762306a36Sopenharmony_ci INIT_LIST_HEAD(&sm_info->sit_entry_set); 525862306a36Sopenharmony_ci 525962306a36Sopenharmony_ci init_f2fs_rwsem(&sm_info->curseg_lock); 526062306a36Sopenharmony_ci 526162306a36Sopenharmony_ci err = f2fs_create_flush_cmd_control(sbi); 526262306a36Sopenharmony_ci if (err) 526362306a36Sopenharmony_ci return err; 526462306a36Sopenharmony_ci 526562306a36Sopenharmony_ci err = create_discard_cmd_control(sbi); 526662306a36Sopenharmony_ci if (err) 526762306a36Sopenharmony_ci return err; 526862306a36Sopenharmony_ci 526962306a36Sopenharmony_ci err = build_sit_info(sbi); 527062306a36Sopenharmony_ci if (err) 527162306a36Sopenharmony_ci return err; 527262306a36Sopenharmony_ci err = build_free_segmap(sbi); 527362306a36Sopenharmony_ci if (err) 527462306a36Sopenharmony_ci return err; 527562306a36Sopenharmony_ci err = build_curseg(sbi); 527662306a36Sopenharmony_ci if (err) 527762306a36Sopenharmony_ci return err; 527862306a36Sopenharmony_ci 527962306a36Sopenharmony_ci /* reinit free segmap based on SIT */ 528062306a36Sopenharmony_ci err = build_sit_entries(sbi); 528162306a36Sopenharmony_ci if (err) 528262306a36Sopenharmony_ci return err; 528362306a36Sopenharmony_ci 528462306a36Sopenharmony_ci init_free_segmap(sbi); 528562306a36Sopenharmony_ci err = build_dirty_segmap(sbi); 528662306a36Sopenharmony_ci if (err) 528762306a36Sopenharmony_ci return err; 528862306a36Sopenharmony_ci 528962306a36Sopenharmony_ci err = sanity_check_curseg(sbi); 529062306a36Sopenharmony_ci if (err) 529162306a36Sopenharmony_ci return err; 529262306a36Sopenharmony_ci 529362306a36Sopenharmony_ci init_min_max_mtime(sbi); 529462306a36Sopenharmony_ci return 0; 529562306a36Sopenharmony_ci} 529662306a36Sopenharmony_ci 529762306a36Sopenharmony_cistatic void discard_dirty_segmap(struct f2fs_sb_info *sbi, 529862306a36Sopenharmony_ci enum dirty_type dirty_type) 529962306a36Sopenharmony_ci{ 530062306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 530162306a36Sopenharmony_ci 530262306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 530362306a36Sopenharmony_ci kvfree(dirty_i->dirty_segmap[dirty_type]); 530462306a36Sopenharmony_ci dirty_i->nr_dirty[dirty_type] = 0; 530562306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 530662306a36Sopenharmony_ci} 530762306a36Sopenharmony_ci 530862306a36Sopenharmony_cistatic void destroy_victim_secmap(struct f2fs_sb_info *sbi) 530962306a36Sopenharmony_ci{ 531062306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 531162306a36Sopenharmony_ci 531262306a36Sopenharmony_ci kvfree(dirty_i->pinned_secmap); 531362306a36Sopenharmony_ci kvfree(dirty_i->victim_secmap); 531462306a36Sopenharmony_ci} 531562306a36Sopenharmony_ci 531662306a36Sopenharmony_cistatic void destroy_dirty_segmap(struct f2fs_sb_info *sbi) 531762306a36Sopenharmony_ci{ 531862306a36Sopenharmony_ci struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); 531962306a36Sopenharmony_ci int i; 532062306a36Sopenharmony_ci 532162306a36Sopenharmony_ci if (!dirty_i) 532262306a36Sopenharmony_ci return; 532362306a36Sopenharmony_ci 532462306a36Sopenharmony_ci /* discard pre-free/dirty segments list */ 532562306a36Sopenharmony_ci for (i = 0; i < NR_DIRTY_TYPE; i++) 532662306a36Sopenharmony_ci discard_dirty_segmap(sbi, i); 532762306a36Sopenharmony_ci 532862306a36Sopenharmony_ci if (__is_large_section(sbi)) { 532962306a36Sopenharmony_ci mutex_lock(&dirty_i->seglist_lock); 533062306a36Sopenharmony_ci kvfree(dirty_i->dirty_secmap); 533162306a36Sopenharmony_ci mutex_unlock(&dirty_i->seglist_lock); 533262306a36Sopenharmony_ci } 533362306a36Sopenharmony_ci 533462306a36Sopenharmony_ci destroy_victim_secmap(sbi); 533562306a36Sopenharmony_ci SM_I(sbi)->dirty_info = NULL; 533662306a36Sopenharmony_ci kfree(dirty_i); 533762306a36Sopenharmony_ci} 533862306a36Sopenharmony_ci 533962306a36Sopenharmony_cistatic void destroy_curseg(struct f2fs_sb_info *sbi) 534062306a36Sopenharmony_ci{ 534162306a36Sopenharmony_ci struct curseg_info *array = SM_I(sbi)->curseg_array; 534262306a36Sopenharmony_ci int i; 534362306a36Sopenharmony_ci 534462306a36Sopenharmony_ci if (!array) 534562306a36Sopenharmony_ci return; 534662306a36Sopenharmony_ci SM_I(sbi)->curseg_array = NULL; 534762306a36Sopenharmony_ci for (i = 0; i < NR_CURSEG_TYPE; i++) { 534862306a36Sopenharmony_ci kfree(array[i].sum_blk); 534962306a36Sopenharmony_ci kfree(array[i].journal); 535062306a36Sopenharmony_ci } 535162306a36Sopenharmony_ci kfree(array); 535262306a36Sopenharmony_ci} 535362306a36Sopenharmony_ci 535462306a36Sopenharmony_cistatic void destroy_free_segmap(struct f2fs_sb_info *sbi) 535562306a36Sopenharmony_ci{ 535662306a36Sopenharmony_ci struct free_segmap_info *free_i = SM_I(sbi)->free_info; 535762306a36Sopenharmony_ci 535862306a36Sopenharmony_ci if (!free_i) 535962306a36Sopenharmony_ci return; 536062306a36Sopenharmony_ci SM_I(sbi)->free_info = NULL; 536162306a36Sopenharmony_ci kvfree(free_i->free_segmap); 536262306a36Sopenharmony_ci kvfree(free_i->free_secmap); 536362306a36Sopenharmony_ci kfree(free_i); 536462306a36Sopenharmony_ci} 536562306a36Sopenharmony_ci 536662306a36Sopenharmony_cistatic void destroy_sit_info(struct f2fs_sb_info *sbi) 536762306a36Sopenharmony_ci{ 536862306a36Sopenharmony_ci struct sit_info *sit_i = SIT_I(sbi); 536962306a36Sopenharmony_ci 537062306a36Sopenharmony_ci if (!sit_i) 537162306a36Sopenharmony_ci return; 537262306a36Sopenharmony_ci 537362306a36Sopenharmony_ci if (sit_i->sentries) 537462306a36Sopenharmony_ci kvfree(sit_i->bitmap); 537562306a36Sopenharmony_ci kfree(sit_i->tmp_map); 537662306a36Sopenharmony_ci 537762306a36Sopenharmony_ci kvfree(sit_i->sentries); 537862306a36Sopenharmony_ci kvfree(sit_i->sec_entries); 537962306a36Sopenharmony_ci kvfree(sit_i->dirty_sentries_bitmap); 538062306a36Sopenharmony_ci 538162306a36Sopenharmony_ci SM_I(sbi)->sit_info = NULL; 538262306a36Sopenharmony_ci kvfree(sit_i->sit_bitmap); 538362306a36Sopenharmony_ci#ifdef CONFIG_F2FS_CHECK_FS 538462306a36Sopenharmony_ci kvfree(sit_i->sit_bitmap_mir); 538562306a36Sopenharmony_ci kvfree(sit_i->invalid_segmap); 538662306a36Sopenharmony_ci#endif 538762306a36Sopenharmony_ci kfree(sit_i); 538862306a36Sopenharmony_ci} 538962306a36Sopenharmony_ci 539062306a36Sopenharmony_civoid f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi) 539162306a36Sopenharmony_ci{ 539262306a36Sopenharmony_ci struct f2fs_sm_info *sm_info = SM_I(sbi); 539362306a36Sopenharmony_ci 539462306a36Sopenharmony_ci if (!sm_info) 539562306a36Sopenharmony_ci return; 539662306a36Sopenharmony_ci f2fs_destroy_flush_cmd_control(sbi, true); 539762306a36Sopenharmony_ci destroy_discard_cmd_control(sbi); 539862306a36Sopenharmony_ci destroy_dirty_segmap(sbi); 539962306a36Sopenharmony_ci destroy_curseg(sbi); 540062306a36Sopenharmony_ci destroy_free_segmap(sbi); 540162306a36Sopenharmony_ci destroy_sit_info(sbi); 540262306a36Sopenharmony_ci sbi->sm_info = NULL; 540362306a36Sopenharmony_ci kfree(sm_info); 540462306a36Sopenharmony_ci} 540562306a36Sopenharmony_ci 540662306a36Sopenharmony_ciint __init f2fs_create_segment_manager_caches(void) 540762306a36Sopenharmony_ci{ 540862306a36Sopenharmony_ci discard_entry_slab = f2fs_kmem_cache_create("f2fs_discard_entry", 540962306a36Sopenharmony_ci sizeof(struct discard_entry)); 541062306a36Sopenharmony_ci if (!discard_entry_slab) 541162306a36Sopenharmony_ci goto fail; 541262306a36Sopenharmony_ci 541362306a36Sopenharmony_ci discard_cmd_slab = f2fs_kmem_cache_create("f2fs_discard_cmd", 541462306a36Sopenharmony_ci sizeof(struct discard_cmd)); 541562306a36Sopenharmony_ci if (!discard_cmd_slab) 541662306a36Sopenharmony_ci goto destroy_discard_entry; 541762306a36Sopenharmony_ci 541862306a36Sopenharmony_ci sit_entry_set_slab = f2fs_kmem_cache_create("f2fs_sit_entry_set", 541962306a36Sopenharmony_ci sizeof(struct sit_entry_set)); 542062306a36Sopenharmony_ci if (!sit_entry_set_slab) 542162306a36Sopenharmony_ci goto destroy_discard_cmd; 542262306a36Sopenharmony_ci 542362306a36Sopenharmony_ci revoke_entry_slab = f2fs_kmem_cache_create("f2fs_revoke_entry", 542462306a36Sopenharmony_ci sizeof(struct revoke_entry)); 542562306a36Sopenharmony_ci if (!revoke_entry_slab) 542662306a36Sopenharmony_ci goto destroy_sit_entry_set; 542762306a36Sopenharmony_ci return 0; 542862306a36Sopenharmony_ci 542962306a36Sopenharmony_cidestroy_sit_entry_set: 543062306a36Sopenharmony_ci kmem_cache_destroy(sit_entry_set_slab); 543162306a36Sopenharmony_cidestroy_discard_cmd: 543262306a36Sopenharmony_ci kmem_cache_destroy(discard_cmd_slab); 543362306a36Sopenharmony_cidestroy_discard_entry: 543462306a36Sopenharmony_ci kmem_cache_destroy(discard_entry_slab); 543562306a36Sopenharmony_cifail: 543662306a36Sopenharmony_ci return -ENOMEM; 543762306a36Sopenharmony_ci} 543862306a36Sopenharmony_ci 543962306a36Sopenharmony_civoid f2fs_destroy_segment_manager_caches(void) 544062306a36Sopenharmony_ci{ 544162306a36Sopenharmony_ci kmem_cache_destroy(sit_entry_set_slab); 544262306a36Sopenharmony_ci kmem_cache_destroy(discard_cmd_slab); 544362306a36Sopenharmony_ci kmem_cache_destroy(discard_entry_slab); 544462306a36Sopenharmony_ci kmem_cache_destroy(revoke_entry_slab); 544562306a36Sopenharmony_ci} 5446