blob: 794cd6b1713a6e5881984539029ede2d6e4f960c [file] [log] [blame]
Colin Cross28fa5bc2012-05-20 13:28:05 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Colin Crossb55dcee2012-04-24 23:07:49 -070017#include <assert.h>
18#include <errno.h>
19#include <stdint.h>
Colin Cross28fa5bc2012-05-20 13:28:05 -070020#include <stdlib.h>
21#include <string.h>
22
23#include "backed_block.h"
Colin Crossbe8ddcb2012-04-25 21:02:16 -070024#include "sparse_defs.h"
Colin Cross28fa5bc2012-05-20 13:28:05 -070025
Colin Crossb55dcee2012-04-24 23:07:49 -070026struct backed_block {
27 unsigned int block;
28 unsigned int len;
29 enum backed_block_type type;
30 union {
31 struct {
32 void *data;
33 } data;
34 struct {
35 char *filename;
36 int64_t offset;
37 } file;
38 struct {
Colin Cross9e1f17e2012-04-25 18:31:39 -070039 int fd;
40 int64_t offset;
41 } fd;
42 struct {
Colin Crossb55dcee2012-04-24 23:07:49 -070043 uint32_t val;
44 } fill;
45 };
46 struct backed_block *next;
Colin Cross28fa5bc2012-05-20 13:28:05 -070047};
48
Colin Cross411619e2012-04-24 18:51:42 -070049struct backed_block_list {
Colin Crossb55dcee2012-04-24 23:07:49 -070050 struct backed_block *data_blocks;
51 struct backed_block *last_used;
Colin Crossbe8ddcb2012-04-25 21:02:16 -070052 unsigned int block_size;
Colin Cross411619e2012-04-24 18:51:42 -070053};
Colin Cross28fa5bc2012-05-20 13:28:05 -070054
Colin Crossb55dcee2012-04-24 23:07:49 -070055struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
56{
57 return bbl->data_blocks;
58}
59
60struct backed_block *backed_block_iter_next(struct backed_block *bb)
61{
62 return bb->next;
63}
64
65unsigned int backed_block_len(struct backed_block *bb)
66{
67 return bb->len;
68}
69
70unsigned int backed_block_block(struct backed_block *bb)
71{
72 return bb->block;
73}
74
75void *backed_block_data(struct backed_block *bb)
76{
77 assert(bb->type == BACKED_BLOCK_DATA);
78 return bb->data.data;
79}
80
81const char *backed_block_filename(struct backed_block *bb)
82{
83 assert(bb->type == BACKED_BLOCK_FILE);
84 return bb->file.filename;
85}
86
Colin Cross9e1f17e2012-04-25 18:31:39 -070087int backed_block_fd(struct backed_block *bb)
88{
89 assert(bb->type == BACKED_BLOCK_FD);
90 return bb->fd.fd;
91}
92
Colin Crossb55dcee2012-04-24 23:07:49 -070093int64_t backed_block_file_offset(struct backed_block *bb)
94{
Colin Cross9e1f17e2012-04-25 18:31:39 -070095 assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);
96 if (bb->type == BACKED_BLOCK_FILE) {
97 return bb->file.offset;
98 } else { /* bb->type == BACKED_BLOCK_FD */
99 return bb->fd.offset;
100 }
Colin Crossb55dcee2012-04-24 23:07:49 -0700101}
102
103uint32_t backed_block_fill_val(struct backed_block *bb)
104{
105 assert(bb->type == BACKED_BLOCK_FILL);
106 return bb->fill.val;
107}
108
109enum backed_block_type backed_block_type(struct backed_block *bb)
110{
111 return bb->type;
112}
113
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700114void backed_block_destroy(struct backed_block *bb)
115{
116 if (bb->type == BACKED_BLOCK_FILE) {
117 free(bb->file.filename);
118 }
119
120 free(bb);
121}
122
123struct backed_block_list *backed_block_list_new(unsigned int block_size)
Colin Cross411619e2012-04-24 18:51:42 -0700124{
125 struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700126 b->block_size = block_size;
Colin Cross411619e2012-04-24 18:51:42 -0700127 return b;
128}
129
Colin Crossb55dcee2012-04-24 23:07:49 -0700130void backed_block_list_destroy(struct backed_block_list *bbl)
Colin Cross411619e2012-04-24 18:51:42 -0700131{
Colin Crossb55dcee2012-04-24 23:07:49 -0700132 if (bbl->data_blocks) {
133 struct backed_block *bb = bbl->data_blocks;
134 while (bb) {
135 struct backed_block *next = bb->next;
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700136 backed_block_destroy(bb);
Colin Crossb55dcee2012-04-24 23:07:49 -0700137 bb = next;
Colin Cross411619e2012-04-24 18:51:42 -0700138 }
139 }
140
Colin Crossb55dcee2012-04-24 23:07:49 -0700141 free(bbl);
Colin Cross411619e2012-04-24 18:51:42 -0700142}
143
Colin Crossbdc6d392012-05-02 15:18:22 -0700144void backed_block_list_move(struct backed_block_list *from,
145 struct backed_block_list *to, struct backed_block *start,
146 struct backed_block *end)
147{
148 struct backed_block *bb;
149
150 if (start == NULL) {
151 start = from->data_blocks;
152 }
153
154 if (!end) {
155 for (end = start; end && end->next; end = end->next)
156 ;
157 }
158
159 if (start == NULL || end == NULL) {
160 return;
161 }
162
163 from->last_used = NULL;
164 to->last_used = NULL;
165 if (from->data_blocks == start) {
166 from->data_blocks = end->next;
167 } else {
168 for (bb = from->data_blocks; bb; bb = bb->next) {
169 if (bb->next == start) {
170 bb->next = end->next;
171 break;
172 }
173 }
174 }
175
176 if (!to->data_blocks) {
177 to->data_blocks = start;
178 end->next = NULL;
179 } else {
180 for (bb = to->data_blocks; bb; bb = bb->next) {
181 if (!bb->next || bb->next->block > start->block) {
182 end->next = bb->next;
183 bb->next = start;
184 break;
185 }
186 }
187 }
188}
189
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700190/* may free b */
191static int merge_bb(struct backed_block_list *bbl,
192 struct backed_block *a, struct backed_block *b)
193{
194 unsigned int block_len;
195
196 /* Block doesn't exist (possible if one block is the last block) */
197 if (!a || !b) {
198 return -EINVAL;
199 }
200
201 assert(a->block < b->block);
202
203 /* Blocks are of different types */
204 if (a->type != b->type) {
205 return -EINVAL;
206 }
207
208 /* Blocks are not adjacent */
209 block_len = a->len / bbl->block_size; /* rounds down */
210 if (a->block + block_len != b->block) {
211 return -EINVAL;
212 }
213
214 switch (a->type) {
215 case BACKED_BLOCK_DATA:
216 /* Don't support merging data for now */
217 return -EINVAL;
218 case BACKED_BLOCK_FILL:
219 if (a->fill.val != b->fill.val) {
220 return -EINVAL;
221 }
222 break;
223 case BACKED_BLOCK_FILE:
lei wang wangc227a1d2015-08-21 11:13:46 +0800224 /* Already make sure b->type is BACKED_BLOCK_FILE */
225 if (strcmp(a->file.filename, b->file.filename) ||
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700226 a->file.offset + a->len != b->file.offset) {
227 return -EINVAL;
228 }
229 break;
230 case BACKED_BLOCK_FD:
231 if (a->fd.fd != b->fd.fd ||
232 a->fd.offset + a->len != b->fd.offset) {
233 return -EINVAL;
234 }
235 break;
236 }
237
238 /* Blocks are compatible and adjacent, with a before b. Merge b into a,
239 * and free b */
240 a->len += b->len;
241 a->next = b->next;
242
243 backed_block_destroy(b);
244
245 return 0;
246}
247
Colin Crossb55dcee2012-04-24 23:07:49 -0700248static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700249{
Colin Crossb55dcee2012-04-24 23:07:49 -0700250 struct backed_block *bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700251
Colin Crossb55dcee2012-04-24 23:07:49 -0700252 if (bbl->data_blocks == NULL) {
253 bbl->data_blocks = new_bb;
254 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700255 }
256
Colin Crossb55dcee2012-04-24 23:07:49 -0700257 if (bbl->data_blocks->block > new_bb->block) {
258 new_bb->next = bbl->data_blocks;
259 bbl->data_blocks = new_bb;
260 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700261 }
262
263 /* Optimization: blocks are mostly queued in sequence, so save the
Colin Crossb55dcee2012-04-24 23:07:49 -0700264 pointer to the last bb that was added, and start searching from
Colin Cross28fa5bc2012-05-20 13:28:05 -0700265 there if the next block number is higher */
Colin Crossb55dcee2012-04-24 23:07:49 -0700266 if (bbl->last_used && new_bb->block > bbl->last_used->block)
267 bb = bbl->last_used;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700268 else
Colin Crossb55dcee2012-04-24 23:07:49 -0700269 bb = bbl->data_blocks;
270 bbl->last_used = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700271
Colin Crossb55dcee2012-04-24 23:07:49 -0700272 for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700273 ;
274
Colin Crossb55dcee2012-04-24 23:07:49 -0700275 if (bb->next == NULL) {
276 bb->next = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700277 } else {
Colin Crossb55dcee2012-04-24 23:07:49 -0700278 new_bb->next = bb->next;
279 bb->next = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700280 }
Colin Crossb55dcee2012-04-24 23:07:49 -0700281
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700282 merge_bb(bbl, new_bb, new_bb->next);
lei wang wangc227a1d2015-08-21 11:13:46 +0800283 if (!merge_bb(bbl, bb, new_bb)) {
284 /* new_bb destroyed, point to retained as last_used */
285 bbl->last_used = bb;
286 }
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700287
Colin Crossb55dcee2012-04-24 23:07:49 -0700288 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700289}
290
291/* Queues a fill block of memory to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700292int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
Colin Cross411619e2012-04-24 18:51:42 -0700293 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700294{
Colin Crossb55dcee2012-04-24 23:07:49 -0700295 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
296 if (bb == NULL) {
297 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700298 }
299
Colin Crossb55dcee2012-04-24 23:07:49 -0700300 bb->block = block;
301 bb->len = len;
302 bb->type = BACKED_BLOCK_FILL;
303 bb->fill.val = fill_val;
304 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700305
Colin Crossb55dcee2012-04-24 23:07:49 -0700306 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700307}
308
309/* Queues a block of memory to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700310int backed_block_add_data(struct backed_block_list *bbl, void *data,
311 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700312{
Colin Crossb55dcee2012-04-24 23:07:49 -0700313 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
314 if (bb == NULL) {
315 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700316 }
317
Colin Crossb55dcee2012-04-24 23:07:49 -0700318 bb->block = block;
319 bb->len = len;
320 bb->type = BACKED_BLOCK_DATA;
321 bb->data.data = data;
322 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700323
Colin Crossb55dcee2012-04-24 23:07:49 -0700324 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700325}
326
327/* Queues a chunk of a file on disk to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700328int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
Colin Cross411619e2012-04-24 18:51:42 -0700329 int64_t offset, unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700330{
Colin Crossb55dcee2012-04-24 23:07:49 -0700331 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
332 if (bb == NULL) {
333 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700334 }
335
Colin Crossb55dcee2012-04-24 23:07:49 -0700336 bb->block = block;
337 bb->len = len;
338 bb->type = BACKED_BLOCK_FILE;
339 bb->file.filename = strdup(filename);
340 bb->file.offset = offset;
341 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700342
Colin Crossb55dcee2012-04-24 23:07:49 -0700343 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700344}
Colin Cross9e1f17e2012-04-25 18:31:39 -0700345
346/* Queues a chunk of a fd to be written to the specified data blocks */
347int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
348 unsigned int len, unsigned int block)
349{
350 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
351 if (bb == NULL) {
352 return -ENOMEM;
353 }
354
355 bb->block = block;
356 bb->len = len;
357 bb->type = BACKED_BLOCK_FD;
358 bb->fd.fd = fd;
359 bb->fd.offset = offset;
360 bb->next = NULL;
361
362 return queue_bb(bbl, bb);
363}
Colin Crossbdc6d392012-05-02 15:18:22 -0700364
365int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
366 unsigned int max_len)
367{
368 struct backed_block *new_bb;
369
370 max_len = ALIGN_DOWN(max_len, bbl->block_size);
371
372 if (bb->len <= max_len) {
373 return 0;
374 }
375
376 new_bb = malloc(sizeof(struct backed_block));
Elliott Hughes14e28d32013-10-29 14:12:46 -0700377 if (new_bb == NULL) {
Colin Crossbdc6d392012-05-02 15:18:22 -0700378 return -ENOMEM;
379 }
380
381 *new_bb = *bb;
382
383 new_bb->len = bb->len - max_len;
384 new_bb->block = bb->block + max_len / bbl->block_size;
385 new_bb->next = bb->next;
386 bb->next = new_bb;
387 bb->len = max_len;
388
389 switch (bb->type) {
390 case BACKED_BLOCK_DATA:
391 new_bb->data.data = (char *)bb->data.data + max_len;
392 break;
393 case BACKED_BLOCK_FILE:
394 new_bb->file.offset += max_len;
395 break;
396 case BACKED_BLOCK_FD:
397 new_bb->fd.offset += max_len;
398 break;
399 case BACKED_BLOCK_FILL:
400 break;
401 }
402
403 return 0;
404}