diff options
Diffstat (limited to 'fs/ubifs/gc.c')
-rw-r--r-- | fs/ubifs/gc.c | 90 |
1 files changed, 76 insertions, 14 deletions
diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c index 02aba36fe3d4..0bef6501d58a 100644 --- a/fs/ubifs/gc.c +++ b/fs/ubifs/gc.c | |||
@@ -96,6 +96,48 @@ static int switch_gc_head(struct ubifs_info *c) | |||
96 | } | 96 | } |
97 | 97 | ||
98 | /** | 98 | /** |
99 | * joinup - bring data nodes for an inode together. | ||
100 | * @c: UBIFS file-system description object | ||
101 | * @sleb: describes scanned LEB | ||
102 | * @inum: inode number | ||
103 | * @blk: block number | ||
104 | * @data: list to which to add data nodes | ||
105 | * | ||
106 | * This function looks at the first few nodes in the scanned LEB @sleb and adds | ||
107 | * them to @data if they are data nodes from @inum and have a larger block | ||
108 | * number than @blk. This function returns %0 on success and a negative error | ||
109 | * code on failure. | ||
110 | */ | ||
111 | static int joinup(struct ubifs_info *c, struct ubifs_scan_leb *sleb, ino_t inum, | ||
112 | unsigned int blk, struct list_head *data) | ||
113 | { | ||
114 | int err, cnt = 6, lnum = sleb->lnum, offs; | ||
115 | struct ubifs_scan_node *snod, *tmp; | ||
116 | union ubifs_key *key; | ||
117 | |||
118 | list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) { | ||
119 | key = &snod->key; | ||
120 | if (key_inum(c, key) == inum && | ||
121 | key_type(c, key) == UBIFS_DATA_KEY && | ||
122 | key_block(c, key) > blk) { | ||
123 | offs = snod->offs; | ||
124 | err = ubifs_tnc_has_node(c, key, 0, lnum, offs, 0); | ||
125 | if (err < 0) | ||
126 | return err; | ||
127 | list_del(&snod->list); | ||
128 | if (err) { | ||
129 | list_add_tail(&snod->list, data); | ||
130 | blk = key_block(c, key); | ||
131 | } else | ||
132 | kfree(snod); | ||
133 | cnt = 6; | ||
134 | } else if (--cnt == 0) | ||
135 | break; | ||
136 | } | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | /** | ||
99 | * move_nodes - move nodes. | 141 | * move_nodes - move nodes. |
100 | * @c: UBIFS file-system description object | 142 | * @c: UBIFS file-system description object |
101 | * @sleb: describes nodes to move | 143 | * @sleb: describes nodes to move |
@@ -116,16 +158,21 @@ static int switch_gc_head(struct ubifs_info *c) | |||
116 | static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) | 158 | static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) |
117 | { | 159 | { |
118 | struct ubifs_scan_node *snod, *tmp; | 160 | struct ubifs_scan_node *snod, *tmp; |
119 | struct list_head large, medium, small; | 161 | struct list_head data, large, medium, small; |
120 | struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; | 162 | struct ubifs_wbuf *wbuf = &c->jheads[GCHD].wbuf; |
121 | int avail, err, min = INT_MAX; | 163 | int avail, err, min = INT_MAX; |
164 | unsigned int blk = 0; | ||
165 | ino_t inum = 0; | ||
122 | 166 | ||
167 | INIT_LIST_HEAD(&data); | ||
123 | INIT_LIST_HEAD(&large); | 168 | INIT_LIST_HEAD(&large); |
124 | INIT_LIST_HEAD(&medium); | 169 | INIT_LIST_HEAD(&medium); |
125 | INIT_LIST_HEAD(&small); | 170 | INIT_LIST_HEAD(&small); |
126 | 171 | ||
127 | list_for_each_entry_safe(snod, tmp, &sleb->nodes, list) { | 172 | while (!list_empty(&sleb->nodes)) { |
128 | struct list_head *lst; | 173 | struct list_head *lst = sleb->nodes.next; |
174 | |||
175 | snod = list_entry(lst, struct ubifs_scan_node, list); | ||
129 | 176 | ||
130 | ubifs_assert(snod->type != UBIFS_IDX_NODE); | 177 | ubifs_assert(snod->type != UBIFS_IDX_NODE); |
131 | ubifs_assert(snod->type != UBIFS_REF_NODE); | 178 | ubifs_assert(snod->type != UBIFS_REF_NODE); |
@@ -136,7 +183,6 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) | |||
136 | if (err < 0) | 183 | if (err < 0) |
137 | goto out; | 184 | goto out; |
138 | 185 | ||
139 | lst = &snod->list; | ||
140 | list_del(lst); | 186 | list_del(lst); |
141 | if (!err) { | 187 | if (!err) { |
142 | /* The node is obsolete, remove it from the list */ | 188 | /* The node is obsolete, remove it from the list */ |
@@ -145,15 +191,30 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) | |||
145 | } | 191 | } |
146 | 192 | ||
147 | /* | 193 | /* |
148 | * Sort the list of nodes so that large nodes go first, and | 194 | * Sort the list of nodes so that data nodes go first, large |
149 | * small nodes go last. | 195 | * nodes go second, and small nodes go last. |
150 | */ | 196 | */ |
151 | if (snod->len > MEDIUM_NODE_WM) | 197 | if (key_type(c, &snod->key) == UBIFS_DATA_KEY) { |
152 | list_add(lst, &large); | 198 | if (inum != key_inum(c, &snod->key)) { |
199 | if (inum) { | ||
200 | /* | ||
201 | * Try to move data nodes from the same | ||
202 | * inode together. | ||
203 | */ | ||
204 | err = joinup(c, sleb, inum, blk, &data); | ||
205 | if (err) | ||
206 | goto out; | ||
207 | } | ||
208 | inum = key_inum(c, &snod->key); | ||
209 | blk = key_block(c, &snod->key); | ||
210 | } | ||
211 | list_add_tail(lst, &data); | ||
212 | } else if (snod->len > MEDIUM_NODE_WM) | ||
213 | list_add_tail(lst, &large); | ||
153 | else if (snod->len > SMALL_NODE_WM) | 214 | else if (snod->len > SMALL_NODE_WM) |
154 | list_add(lst, &medium); | 215 | list_add_tail(lst, &medium); |
155 | else | 216 | else |
156 | list_add(lst, &small); | 217 | list_add_tail(lst, &small); |
157 | 218 | ||
158 | /* And find the smallest node */ | 219 | /* And find the smallest node */ |
159 | if (snod->len < min) | 220 | if (snod->len < min) |
@@ -164,6 +225,7 @@ static int move_nodes(struct ubifs_info *c, struct ubifs_scan_leb *sleb) | |||
164 | * Join the tree lists so that we'd have one roughly sorted list | 225 | * Join the tree lists so that we'd have one roughly sorted list |
165 | * ('large' will be the head of the joined list). | 226 | * ('large' will be the head of the joined list). |
166 | */ | 227 | */ |
228 | list_splice(&data, &large); | ||
167 | list_splice(&medium, large.prev); | 229 | list_splice(&medium, large.prev); |
168 | list_splice(&small, large.prev); | 230 | list_splice(&small, large.prev); |
169 | 231 | ||
@@ -653,7 +715,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) | |||
653 | */ | 715 | */ |
654 | while (1) { | 716 | while (1) { |
655 | lp = ubifs_fast_find_freeable(c); | 717 | lp = ubifs_fast_find_freeable(c); |
656 | if (unlikely(IS_ERR(lp))) { | 718 | if (IS_ERR(lp)) { |
657 | err = PTR_ERR(lp); | 719 | err = PTR_ERR(lp); |
658 | goto out; | 720 | goto out; |
659 | } | 721 | } |
@@ -665,7 +727,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) | |||
665 | if (err) | 727 | if (err) |
666 | goto out; | 728 | goto out; |
667 | lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0); | 729 | lp = ubifs_change_lp(c, lp, c->leb_size, 0, lp->flags, 0); |
668 | if (unlikely(IS_ERR(lp))) { | 730 | if (IS_ERR(lp)) { |
669 | err = PTR_ERR(lp); | 731 | err = PTR_ERR(lp); |
670 | goto out; | 732 | goto out; |
671 | } | 733 | } |
@@ -680,7 +742,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) | |||
680 | /* Record index freeable LEBs for unmapping after commit */ | 742 | /* Record index freeable LEBs for unmapping after commit */ |
681 | while (1) { | 743 | while (1) { |
682 | lp = ubifs_fast_find_frdi_idx(c); | 744 | lp = ubifs_fast_find_frdi_idx(c); |
683 | if (unlikely(IS_ERR(lp))) { | 745 | if (IS_ERR(lp)) { |
684 | err = PTR_ERR(lp); | 746 | err = PTR_ERR(lp); |
685 | goto out; | 747 | goto out; |
686 | } | 748 | } |
@@ -696,7 +758,7 @@ int ubifs_gc_start_commit(struct ubifs_info *c) | |||
696 | /* Don't release the LEB until after the next commit */ | 758 | /* Don't release the LEB until after the next commit */ |
697 | flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX; | 759 | flags = (lp->flags | LPROPS_TAKEN) ^ LPROPS_INDEX; |
698 | lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1); | 760 | lp = ubifs_change_lp(c, lp, c->leb_size, 0, flags, 1); |
699 | if (unlikely(IS_ERR(lp))) { | 761 | if (IS_ERR(lp)) { |
700 | err = PTR_ERR(lp); | 762 | err = PTR_ERR(lp); |
701 | kfree(idx_gc); | 763 | kfree(idx_gc); |
702 | goto out; | 764 | goto out; |