blob: fb26d1a6ea78b80c13c7bcf3c769ba8f8b92232f [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{
Jerry Zhang5a755072018-06-12 16:18:53 -0700125 struct backed_block_list *b = reinterpret_cast<backed_block_list*>(
126 calloc(sizeof(struct backed_block_list), 1));
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700127 b->block_size = block_size;
Colin Cross411619e2012-04-24 18:51:42 -0700128 return b;
129}
130
Colin Crossb55dcee2012-04-24 23:07:49 -0700131void backed_block_list_destroy(struct backed_block_list *bbl)
Colin Cross411619e2012-04-24 18:51:42 -0700132{
Colin Crossb55dcee2012-04-24 23:07:49 -0700133 if (bbl->data_blocks) {
134 struct backed_block *bb = bbl->data_blocks;
135 while (bb) {
136 struct backed_block *next = bb->next;
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700137 backed_block_destroy(bb);
Colin Crossb55dcee2012-04-24 23:07:49 -0700138 bb = next;
Colin Cross411619e2012-04-24 18:51:42 -0700139 }
140 }
141
Colin Crossb55dcee2012-04-24 23:07:49 -0700142 free(bbl);
Colin Cross411619e2012-04-24 18:51:42 -0700143}
144
Colin Crossbdc6d392012-05-02 15:18:22 -0700145void backed_block_list_move(struct backed_block_list *from,
146 struct backed_block_list *to, struct backed_block *start,
147 struct backed_block *end)
148{
149 struct backed_block *bb;
150
151 if (start == NULL) {
152 start = from->data_blocks;
153 }
154
155 if (!end) {
156 for (end = start; end && end->next; end = end->next)
157 ;
158 }
159
160 if (start == NULL || end == NULL) {
161 return;
162 }
163
164 from->last_used = NULL;
165 to->last_used = NULL;
166 if (from->data_blocks == start) {
167 from->data_blocks = end->next;
168 } else {
169 for (bb = from->data_blocks; bb; bb = bb->next) {
170 if (bb->next == start) {
171 bb->next = end->next;
172 break;
173 }
174 }
175 }
176
177 if (!to->data_blocks) {
178 to->data_blocks = start;
179 end->next = NULL;
180 } else {
181 for (bb = to->data_blocks; bb; bb = bb->next) {
182 if (!bb->next || bb->next->block > start->block) {
183 end->next = bb->next;
184 bb->next = start;
185 break;
186 }
187 }
188 }
189}
190
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700191/* may free b */
192static int merge_bb(struct backed_block_list *bbl,
193 struct backed_block *a, struct backed_block *b)
194{
195 unsigned int block_len;
196
197 /* Block doesn't exist (possible if one block is the last block) */
198 if (!a || !b) {
199 return -EINVAL;
200 }
201
202 assert(a->block < b->block);
203
204 /* Blocks are of different types */
205 if (a->type != b->type) {
206 return -EINVAL;
207 }
208
209 /* Blocks are not adjacent */
210 block_len = a->len / bbl->block_size; /* rounds down */
211 if (a->block + block_len != b->block) {
212 return -EINVAL;
213 }
214
215 switch (a->type) {
216 case BACKED_BLOCK_DATA:
217 /* Don't support merging data for now */
218 return -EINVAL;
219 case BACKED_BLOCK_FILL:
220 if (a->fill.val != b->fill.val) {
221 return -EINVAL;
222 }
223 break;
224 case BACKED_BLOCK_FILE:
lei wang wangc227a1d2015-08-21 11:13:46 +0800225 /* Already make sure b->type is BACKED_BLOCK_FILE */
226 if (strcmp(a->file.filename, b->file.filename) ||
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700227 a->file.offset + a->len != b->file.offset) {
228 return -EINVAL;
229 }
230 break;
231 case BACKED_BLOCK_FD:
232 if (a->fd.fd != b->fd.fd ||
233 a->fd.offset + a->len != b->fd.offset) {
234 return -EINVAL;
235 }
236 break;
237 }
238
239 /* Blocks are compatible and adjacent, with a before b. Merge b into a,
240 * and free b */
241 a->len += b->len;
242 a->next = b->next;
243
244 backed_block_destroy(b);
245
246 return 0;
247}
248
Colin Crossb55dcee2012-04-24 23:07:49 -0700249static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700250{
Colin Crossb55dcee2012-04-24 23:07:49 -0700251 struct backed_block *bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700252
Colin Crossb55dcee2012-04-24 23:07:49 -0700253 if (bbl->data_blocks == NULL) {
254 bbl->data_blocks = new_bb;
255 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700256 }
257
Colin Crossb55dcee2012-04-24 23:07:49 -0700258 if (bbl->data_blocks->block > new_bb->block) {
259 new_bb->next = bbl->data_blocks;
260 bbl->data_blocks = new_bb;
261 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700262 }
263
264 /* Optimization: blocks are mostly queued in sequence, so save the
Colin Crossb55dcee2012-04-24 23:07:49 -0700265 pointer to the last bb that was added, and start searching from
Colin Cross28fa5bc2012-05-20 13:28:05 -0700266 there if the next block number is higher */
Colin Crossb55dcee2012-04-24 23:07:49 -0700267 if (bbl->last_used && new_bb->block > bbl->last_used->block)
268 bb = bbl->last_used;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700269 else
Colin Crossb55dcee2012-04-24 23:07:49 -0700270 bb = bbl->data_blocks;
271 bbl->last_used = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700272
Colin Crossb55dcee2012-04-24 23:07:49 -0700273 for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700274 ;
275
Colin Crossb55dcee2012-04-24 23:07:49 -0700276 if (bb->next == NULL) {
277 bb->next = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700278 } else {
Colin Crossb55dcee2012-04-24 23:07:49 -0700279 new_bb->next = bb->next;
280 bb->next = new_bb;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700281 }
Colin Crossb55dcee2012-04-24 23:07:49 -0700282
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700283 merge_bb(bbl, new_bb, new_bb->next);
lei wang wangc227a1d2015-08-21 11:13:46 +0800284 if (!merge_bb(bbl, bb, new_bb)) {
285 /* new_bb destroyed, point to retained as last_used */
286 bbl->last_used = bb;
287 }
Colin Crossbe8ddcb2012-04-25 21:02:16 -0700288
Colin Crossb55dcee2012-04-24 23:07:49 -0700289 return 0;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700290}
291
292/* Queues a fill block of memory to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700293int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
Colin Cross411619e2012-04-24 18:51:42 -0700294 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700295{
Jerry Zhang5a755072018-06-12 16:18:53 -0700296 struct backed_block *bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
Colin Crossb55dcee2012-04-24 23:07:49 -0700297 if (bb == NULL) {
298 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700299 }
300
Colin Crossb55dcee2012-04-24 23:07:49 -0700301 bb->block = block;
302 bb->len = len;
303 bb->type = BACKED_BLOCK_FILL;
304 bb->fill.val = fill_val;
305 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700306
Colin Crossb55dcee2012-04-24 23:07:49 -0700307 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700308}
309
310/* Queues a block of memory to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700311int backed_block_add_data(struct backed_block_list *bbl, void *data,
312 unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700313{
Jerry Zhang5a755072018-06-12 16:18:53 -0700314 struct backed_block *bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
Colin Crossb55dcee2012-04-24 23:07:49 -0700315 if (bb == NULL) {
316 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700317 }
318
Colin Crossb55dcee2012-04-24 23:07:49 -0700319 bb->block = block;
320 bb->len = len;
321 bb->type = BACKED_BLOCK_DATA;
322 bb->data.data = data;
323 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700324
Colin Crossb55dcee2012-04-24 23:07:49 -0700325 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700326}
327
328/* Queues a chunk of a file on disk to be written to the specified data blocks */
Colin Crossb55dcee2012-04-24 23:07:49 -0700329int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
Colin Cross411619e2012-04-24 18:51:42 -0700330 int64_t offset, unsigned int len, unsigned int block)
Colin Cross28fa5bc2012-05-20 13:28:05 -0700331{
Jerry Zhang5a755072018-06-12 16:18:53 -0700332 struct backed_block *bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
Colin Crossb55dcee2012-04-24 23:07:49 -0700333 if (bb == NULL) {
334 return -ENOMEM;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700335 }
336
Colin Crossb55dcee2012-04-24 23:07:49 -0700337 bb->block = block;
338 bb->len = len;
339 bb->type = BACKED_BLOCK_FILE;
340 bb->file.filename = strdup(filename);
341 bb->file.offset = offset;
342 bb->next = NULL;
Colin Cross28fa5bc2012-05-20 13:28:05 -0700343
Colin Crossb55dcee2012-04-24 23:07:49 -0700344 return queue_bb(bbl, bb);
Colin Cross28fa5bc2012-05-20 13:28:05 -0700345}
Colin Cross9e1f17e2012-04-25 18:31:39 -0700346
347/* Queues a chunk of a fd to be written to the specified data blocks */
348int backed_block_add_fd(struct backed_block_list *bbl, int fd, int64_t offset,
349 unsigned int len, unsigned int block)
350{
Jerry Zhang5a755072018-06-12 16:18:53 -0700351 struct backed_block *bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
Colin Cross9e1f17e2012-04-25 18:31:39 -0700352 if (bb == NULL) {
353 return -ENOMEM;
354 }
355
356 bb->block = block;
357 bb->len = len;
358 bb->type = BACKED_BLOCK_FD;
359 bb->fd.fd = fd;
360 bb->fd.offset = offset;
361 bb->next = NULL;
362
363 return queue_bb(bbl, bb);
364}
Colin Crossbdc6d392012-05-02 15:18:22 -0700365
366int backed_block_split(struct backed_block_list *bbl, struct backed_block *bb,
367 unsigned int max_len)
368{
369 struct backed_block *new_bb;
370
371 max_len = ALIGN_DOWN(max_len, bbl->block_size);
372
373 if (bb->len <= max_len) {
374 return 0;
375 }
376
Jerry Zhang5a755072018-06-12 16:18:53 -0700377 new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));
Elliott Hughes14e28d32013-10-29 14:12:46 -0700378 if (new_bb == NULL) {
Colin Crossbdc6d392012-05-02 15:18:22 -0700379 return -ENOMEM;
380 }
381
382 *new_bb = *bb;
383
384 new_bb->len = bb->len - max_len;
385 new_bb->block = bb->block + max_len / bbl->block_size;
386 new_bb->next = bb->next;
387 bb->next = new_bb;
388 bb->len = max_len;
389
390 switch (bb->type) {
391 case BACKED_BLOCK_DATA:
392 new_bb->data.data = (char *)bb->data.data + max_len;
393 break;
394 case BACKED_BLOCK_FILE:
395 new_bb->file.offset += max_len;
396 break;
397 case BACKED_BLOCK_FD:
398 new_bb->fd.offset += max_len;
399 break;
400 case BACKED_BLOCK_FILL:
401 break;
402 }
403
404 return 0;
405}