1/**
2 * defrag.c
3 *
4 * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10#include "fsck.h"
11
12static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to)
13{
14	void *raw = calloc(BLOCK_SZ, 1);
15	struct seg_entry *se;
16	struct f2fs_summary sum;
17	u64 offset;
18	int ret, type;
19
20	ASSERT(raw != NULL);
21
22	/* read from */
23	ret = dev_read_block(raw, from);
24	ASSERT(ret >= 0);
25
26	/* write to */
27	ret = dev_write_block(raw, to);
28	ASSERT(ret >= 0);
29
30	/* update sit bitmap & valid_blocks && se->type */
31	se = get_seg_entry(sbi, GET_SEGNO(sbi, from));
32	offset = OFFSET_IN_SEG(sbi, from);
33	type = se->type;
34	se->valid_blocks--;
35	f2fs_clear_bit(offset, (char *)se->cur_valid_map);
36	se->dirty = 1;
37
38	se = get_seg_entry(sbi, GET_SEGNO(sbi, to));
39	offset = OFFSET_IN_SEG(sbi, to);
40	se->type = type;
41	se->valid_blocks++;
42	f2fs_set_bit(offset, (char *)se->cur_valid_map);
43	se->dirty = 1;
44
45	/* read/write SSA */
46	get_sum_entry(sbi, from, &sum);
47	update_sum_entry(sbi, to, &sum);
48
49	/* if data block, read node and update node block */
50	if (IS_DATASEG(type))
51		update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
52				le16_to_cpu(sum.ofs_in_node), to);
53	else
54		update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to);
55
56	DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
57					IS_DATASEG(type) ? "data" : "node",
58					from, to);
59	free(raw);
60	return 0;
61}
62
63int f2fs_defragment(struct f2fs_sb_info *sbi, u64 from, u64 len, u64 to, int left)
64{
65	struct seg_entry *se;
66	u64 idx, offset;
67
68	/* flush NAT/SIT journal entries */
69	flush_journal_entries(sbi);
70
71	for (idx = from; idx < from + len; idx++) {
72		u64 target = to;
73
74		se = get_seg_entry(sbi, GET_SEGNO(sbi, idx));
75		offset = OFFSET_IN_SEG(sbi, idx);
76
77		if (!f2fs_test_bit(offset, (const char *)se->cur_valid_map))
78			continue;
79
80		if (find_next_free_block(sbi, &target, left, se->type)) {
81			MSG(0, "Not enough space to migrate blocks");
82			return -1;
83		}
84
85		if (migrate_block(sbi, idx, target)) {
86			ASSERT_MSG("Found inconsistency: please run FSCK");
87			return -1;
88		}
89	}
90
91	/* update curseg info; can update sit->types */
92	move_curseg_info(sbi, to);
93	zero_journal_entries(sbi);
94	write_curseg_info(sbi);
95
96	/* flush dirty sit entries */
97	flush_sit_entries(sbi);
98
99	write_checkpoint(sbi);
100
101	return 0;
102}
103