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