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