blob: 3e72b57c71da4f85d53acd8aebc574f4290435ed [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:
224 if (a->file.filename != b->file.filename ||
225 a->file.offset + a->len != b->file.offset) {
226 return -EINVAL;
227 }
228 break;
229 case BACKED_BLOCK_FD:
230 if (a->fd.fd != b->fd.fd ||
231 a->fd.offset + a->len != b->fd.offset) {
232 return -EINVAL;
233 }
234 break;
235 }
236
237 /* Blocks are compatible and adjacent, with a before b. Merge b into a,
238 * and free b */
239 a->len += b->len;
240 a->next = b->next;
241
242 backed_block_destroy(b);
243
244 return 0;
245}
246
Colin Crossb55dcee2012-04-24 23:07:49 -0700247static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700248{
Colin Crossb55dcee2012-04-24 23:07:49 -0700249 struct backed_block *bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700250
Colin Crossb55dcee2012-04-24 23:07:49 -0700251 if (bbl->data_blocks == NULL) {
252 bbl->data_blocks = new_bb;
253 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700254 }
255
Colin Crossb55dcee2012-04-24 23:07:49 -0700256 if (bbl->data_blocks->block > new_bb->block) {
257 new_bb->next = bbl->data_blocks;
258 bbl->data_blocks = new_bb;
259 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700260 }
261
262 /* Optimization: blocks are mostly queued in sequence, so save the
Colin Crossb55dcee2012-04-24 23:07:49 -0700263 pointer to the last bb that was added, and start searching from
Colin Cross28fa5bc2012-05-20 13:28:05 -0700264 there if the next block number is higher */
Colin Crossb55dcee2012-04-24 23:07:49 -0700265 if (bbl->last_used && new_bb->block > bbl->last_used->block)
266 bb = bbl->last_used;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700267 else
Colin Crossb55dcee2012-04-24 23:07:49 -0700268 bb = bbl->data_blocks;
269 bbl->last_used = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700270
Colin Crossb55dcee2012-04-24 23:07:49 -0700271 for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700272 ;
273
Colin Crossb55dcee2012-04-24 23:07:49 -0700274 if (bb->next == NULL) {
275 bb->next = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700276 } else {
Colin Crossb55dcee2012-04-24 23:07:49 -0700277 new_bb->next = bb->next;
278 bb->next = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700279 }
Colin Crossb55dcee2012-04-24 23:07:49 -0700280
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700281 merge_bb(bbl, new_bb, new_bb->next);
282 merge_bb(bbl, bb, new_bb);
283
Colin Crossb55dcee2012-04-24 23:07:49 -0700284 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700285}
286
287/* Queues a fill block of memory to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700288int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
Colin Cross411619e2012-04-24 18:51:42 -0700289 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700290{
Colin Crossb55dcee2012-04-24 23:07:49 -0700291 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
292 if (bb == NULL) {
293 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700294 }
295
Colin Crossb55dcee2012-04-24 23:07:49 -0700296 bb->block = block;
297 bb->len = len;
298 bb->type = BACKED_BLOCK_FILL;
299 bb->fill.val = fill_val;
300 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700301
Colin Crossb55dcee2012-04-24 23:07:49 -0700302 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700303}
304
305/* Queues a block of memory to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700306int backed_block_add_data(struct backed_block_list *bbl, void *data,
307 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700308{
Colin Crossb55dcee2012-04-24 23:07:49 -0700309 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
310 if (bb == NULL) {
311 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700312 }
313
Colin Crossb55dcee2012-04-24 23:07:49 -0700314 bb->block = block;
315 bb->len = len;
316 bb->type = BACKED_BLOCK_DATA;
317 bb->data.data = data;
318 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700319
Colin Crossb55dcee2012-04-24 23:07:49 -0700320 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700321}
322
323/* Queues a chunk of a file on disk to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700324int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
Colin Cross411619e2012-04-24 18:51:42 -0700325 int64_t offset, unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700326{
Colin Crossb55dcee2012-04-24 23:07:49 -0700327 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
328 if (bb == NULL) {
329 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700330 }
331
Colin Crossb55dcee2012-04-24 23:07:49 -0700332 bb->block = block;
333 bb->len = len;
334 bb->type = BACKED_BLOCK_FILE;
335 bb->file.filename = strdup(filename);
336 bb->file.offset = offset;
337 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700338
Colin Crossb55dcee2012-04-24 23:07:49 -0700339 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700340}
Colin Cross9e1f17e2012-04-25 18:31:39 -0700341
342/* Queues a chunk of a fd to be written to the specified data blocks */
343int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
344 unsigned int len, unsigned int block)
345{
346 struct backed_block *bb = calloc(1, sizeof(struct backed_block));
347 if (bb == NULL) {
348 return -ENOMEM;
349 }
350
351 bb->block = block;
352 bb->len = len;
353 bb->type = BACKED_BLOCK_FD;
354 bb->fd.fd = fd;
355 bb->fd.offset = offset;
356 bb->next = NULL;
357
358 return queue_bb(bbl, bb);
359}
Colin Crossbdc6d392012-05-02 15:18:22 -0700360
361int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
362 unsigned int max_len)
363{
364 struct backed_block *new_bb;
365
366 max_len = ALIGN_DOWN(max_len, bbl->block_size);
367
368 if (bb->len <= max_len) {
369 return 0;
370 }
371
372 new_bb = malloc(sizeof(struct backed_block));
Elliott Hughes14e28d32013-10-29 14:12:46 -0700373 if (new_bb == NULL) {
Colin Crossbdc6d392012-05-02 15:18:22 -0700374 return -ENOMEM;
375 }
376
377 *new_bb = *bb;
378
379 new_bb->len = bb->len - max_len;
380 new_bb->block = bb->block + max_len / bbl->block_size;
381 new_bb->next = bb->next;
382 bb->next = new_bb;
383 bb->len = max_len;
384
385 switch (bb->type) {
386 case BACKED_BLOCK_DATA:
387 new_bb->data.data = (char *)bb->data.data + max_len;
388 break;
389 case BACKED_BLOCK_FILE:
390 new_bb->file.offset += max_len;
391 break;
392 case BACKED_BLOCK_FD:
393 new_bb->fd.offset += max_len;
394 break;
395 case BACKED_BLOCK_FILL:
396 break;
397 }
398
399 return 0;
400}