Reorder includes to fix travis build
[lwext4.git] / lwext4 / ext4_blockdev.c
1 /*
2  * Copyright (c) 2013 Grzegorz Kostka (kostka.grzegorz@gmail.com)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * - Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  * - Redistributions in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * - The name of the author may not be used to endorse or promote products
15  *   derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /** @addtogroup lwext4
30  * @{
31  */
32 /**
33  * @file  ext4_blockdev.c
34  * @brief Block device module.
35  */
36
37 #include "ext4_config.h"
38 #include "ext4_blockdev.h"
39 #include "ext4_errno.h"
40 #include "ext4_debug.h"
41
42 #include <string.h>
43 #include <stdlib.h>
44
45 int ext4_block_init(struct ext4_blockdev *bdev)
46 {
47         int rc;
48         ext4_assert(bdev);
49
50         ext4_assert(bdev->open && bdev->close && bdev->bread && bdev->bwrite);
51
52         /*Low level block init*/
53         rc = bdev->open(bdev);
54         if (rc != EOK)
55                 return rc;
56
57         bdev->flags |= EXT4_BDEV_INITIALIZED;
58
59         return EOK;
60 }
61
62 int ext4_block_bind_bcache(struct ext4_blockdev *bdev, struct ext4_bcache *bc)
63 {
64         ext4_assert(bdev && bc);
65         bdev->bc = bc;
66         return EOK;
67 }
68
69 void ext4_block_set_lb_size(struct ext4_blockdev *bdev, uint64_t lb_bsize)
70 {
71         /*Logical block size has to be multiply of physical */
72         ext4_assert(!(lb_bsize % bdev->ph_bsize));
73
74         bdev->lg_bsize = lb_bsize;
75         bdev->lg_bcnt = (bdev->ph_bcnt * bdev->ph_bsize) / lb_bsize;
76 }
77
78 int ext4_block_fini(struct ext4_blockdev *bdev)
79 {
80         ext4_assert(bdev);
81
82         bdev->flags &= ~(EXT4_BDEV_INITIALIZED);
83
84         /*Low level block fini*/
85         return bdev->close(bdev);
86 }
87
88 int ext4_block_get(struct ext4_blockdev *bdev, struct ext4_block *b,
89                    uint64_t lba)
90 {
91         uint64_t pba;
92         uint32_t pb_cnt;
93         uint32_t i;
94         bool is_new;
95         int r;
96
97         ext4_assert(bdev && b);
98
99         if (!(bdev->flags & EXT4_BDEV_INITIALIZED))
100                 return EIO;
101
102         if (!(lba < bdev->lg_bcnt))
103                 return ERANGE;
104
105         b->dirty = 0;
106         b->lb_id = lba;
107
108         /*If cache is full we have to flush it anyway :(*/
109         if (ext4_bcache_is_full(bdev->bc) && bdev->cache_write_back) {
110
111                 uint32_t free_candidate = bdev->bc->cnt;
112                 uint32_t min_lru = 0xFFFFFFFF;
113
114                 for (i = 0; i < bdev->bc->cnt; ++i) {
115                         /*Check if buffer free was delayed.*/
116                         if (!bdev->bc->free_delay[i])
117                                 continue;
118
119                         /*Check reference counter.*/
120                         if (bdev->bc->refctr[i])
121                                 continue;
122
123                         if (bdev->bc->lru_id[i] < min_lru) {
124                                 min_lru = bdev->bc->lru_id[i];
125                                 free_candidate = i;
126                                 continue;
127                         }
128                 }
129
130                 if (free_candidate < bdev->bc->cnt) {
131                         /*Buffer free was delayed and have no reference. Flush
132                          * it.*/
133                         r = ext4_blocks_set_direct(
134                             bdev, bdev->bc->data +
135                                       bdev->bc->itemsize * free_candidate,
136                             bdev->bc->lba[free_candidate], 1);
137                         if (r != EOK)
138                                 return r;
139
140                         /*No delayed anymore*/
141                         bdev->bc->free_delay[free_candidate] = 0;
142
143                         /*Reduce reference counter*/
144                         bdev->bc->ref_blocks--;
145                 }
146         }
147
148         r = ext4_bcache_alloc(bdev->bc, b, &is_new);
149         if (r != EOK)
150                 return r;
151
152         if (!is_new) {
153                 /*Block is in cache. Read from physical device is not required*/
154                 return EOK;
155         }
156
157         if (!b->data)
158                 return ENOMEM;
159
160         pba = (lba * bdev->lg_bsize) / bdev->ph_bsize;
161         pb_cnt = bdev->lg_bsize / bdev->ph_bsize;
162
163         r = bdev->bread(bdev, b->data, pba, pb_cnt);
164
165         if (r != EOK) {
166                 ext4_bcache_free(bdev->bc, b, 0);
167                 b->lb_id = 0;
168                 return r;
169         }
170
171         bdev->bread_ctr++;
172         return EOK;
173 }
174
175 int ext4_block_set(struct ext4_blockdev *bdev, struct ext4_block *b)
176 {
177         uint64_t pba;
178         uint32_t pb_cnt;
179         int r;
180
181         ext4_assert(bdev && b);
182
183         if (!(bdev->flags & EXT4_BDEV_INITIALIZED))
184                 return EIO;
185
186         /*No need to write.*/
187         if (!b->dirty && !bdev->bc->dirty[b->cache_id]) {
188                 ext4_bcache_free(bdev->bc, b, 0);
189                 return EOK;
190         }
191
192         /*Free cache delay mode*/
193         if (bdev->cache_write_back) {
194
195                 /*Free cache block and mark as free delayed*/
196                 return ext4_bcache_free(bdev->bc, b, bdev->cache_write_back);
197         }
198
199         if (bdev->bc->refctr[b->cache_id] > 1) {
200                 bdev->bc->dirty[b->cache_id] = true;
201                 return ext4_bcache_free(bdev->bc, b, 0);
202         }
203
204         pba = (b->lb_id * bdev->lg_bsize) / bdev->ph_bsize;
205         pb_cnt = bdev->lg_bsize / bdev->ph_bsize;
206
207         r = bdev->bwrite(bdev, b->data, pba, pb_cnt);
208         bdev->bc->dirty[b->cache_id] = false;
209         if (r != EOK) {
210                 b->dirty = false;
211                 ext4_bcache_free(bdev->bc, b, 0);
212                 return r;
213         }
214
215         bdev->bwrite_ctr++;
216         b->dirty = false;
217         ext4_bcache_free(bdev->bc, b, 0);
218         return EOK;
219 }
220
221 int ext4_blocks_get_direct(struct ext4_blockdev *bdev, void *buf, uint64_t lba,
222                            uint32_t cnt)
223 {
224         uint64_t pba;
225         uint32_t pb_cnt;
226
227         ext4_assert(bdev && buf);
228
229         pba = (lba * bdev->lg_bsize) / bdev->ph_bsize;
230         pb_cnt = bdev->lg_bsize / bdev->ph_bsize;
231
232         bdev->bread_ctr++;
233         return bdev->bread(bdev, buf, pba, pb_cnt * cnt);
234 }
235
236 int ext4_blocks_set_direct(struct ext4_blockdev *bdev, const void *buf,
237                            uint64_t lba, uint32_t cnt)
238 {
239         uint64_t pba;
240         uint32_t pb_cnt;
241
242         ext4_assert(bdev && buf);
243
244         pba = (lba * bdev->lg_bsize) / bdev->ph_bsize;
245         pb_cnt = bdev->lg_bsize / bdev->ph_bsize;
246
247         bdev->bwrite_ctr++;
248
249         return bdev->bwrite(bdev, buf, pba, pb_cnt * cnt);
250 }
251
252 int ext4_block_writebytes(struct ext4_blockdev *bdev, uint64_t off,
253                           const void *buf, uint32_t len)
254 {
255         uint64_t block_idx;
256         uint64_t block_end;
257         uint32_t blen;
258         uint32_t unalg;
259         int r = EOK;
260
261         const uint8_t *p = (void *)buf;
262
263         ext4_assert(bdev && buf);
264
265         if (!(bdev->flags & EXT4_BDEV_INITIALIZED))
266                 return EIO;
267
268         block_idx = off / bdev->ph_bsize;
269         block_end = block_idx + len / bdev->ph_bsize;
270
271         if (!(block_end < bdev->ph_bcnt))
272                 return EINVAL; /*Ups. Out of range operation*/
273
274         /*OK lets deal with the first possible unaligned block*/
275         unalg = (off & (bdev->ph_bsize - 1));
276         if (unalg) {
277
278                 uint32_t wlen = (bdev->ph_bsize - unalg) > len
279                                     ? len
280                                     : (bdev->ph_bsize - unalg);
281
282                 r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1);
283
284                 if (r != EOK)
285                         return r;
286
287                 memcpy(bdev->ph_bbuf + unalg, p, wlen);
288
289                 r = bdev->bwrite(bdev, bdev->ph_bbuf, block_idx, 1);
290                 if (r != EOK)
291                         return r;
292
293                 p += wlen;
294                 len -= wlen;
295                 block_idx++;
296         }
297
298         /*Aligned data*/
299         blen = len / bdev->ph_bsize;
300         r = bdev->bwrite(bdev, p, block_idx, blen);
301
302         if (r != EOK)
303                 return r;
304
305         p += bdev->ph_bsize * blen;
306         len -= bdev->ph_bsize * blen;
307
308         block_idx += blen;
309
310         /*Rest of the data*/
311         if (len) {
312                 r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1);
313                 if (r != EOK)
314                         return r;
315
316                 memcpy(bdev->ph_bbuf, p, len);
317
318                 r = bdev->bwrite(bdev, bdev->ph_bbuf, block_idx, 1);
319
320                 if (r != EOK)
321                         return r;
322         }
323
324         return r;
325 }
326
327 int ext4_block_readbytes(struct ext4_blockdev *bdev, uint64_t off, void *buf,
328                          uint32_t len)
329 {
330         uint64_t block_idx;
331         uint64_t block_end;
332         uint32_t blen;
333         uint32_t unalg;
334         int r = EOK;
335
336         uint8_t *p = (void *)buf;
337
338         ext4_assert(bdev && buf);
339
340         if (!(bdev->flags & EXT4_BDEV_INITIALIZED))
341                 return EIO;
342
343         block_idx = off / bdev->ph_bsize;
344         block_end = block_idx + len / bdev->ph_bsize;
345
346         if (!(block_end < bdev->ph_bcnt))
347                 return EINVAL; /*Ups. Out of range operation*/
348
349         /*OK lets deal with the first possible unaligned block*/
350         unalg = (off & (bdev->ph_bsize - 1));
351         if (unalg) {
352
353                 uint32_t rlen = (bdev->ph_bsize - unalg) > len
354                                     ? len
355                                     : (bdev->ph_bsize - unalg);
356
357                 r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1);
358                 if (r != EOK)
359                         return r;
360
361                 memcpy(p, bdev->ph_bbuf + unalg, rlen);
362
363                 p += rlen;
364                 len -= rlen;
365                 block_idx++;
366         }
367
368         /*Aligned data*/
369         blen = len / bdev->ph_bsize;
370
371         r = bdev->bread(bdev, p, block_idx, blen);
372
373         if (r != EOK)
374                 return r;
375
376         p += bdev->ph_bsize * blen;
377         len -= bdev->ph_bsize * blen;
378
379         block_idx += blen;
380
381         /*Rest of the data*/
382         if (len) {
383                 r = bdev->bread(bdev, bdev->ph_bbuf, block_idx, 1);
384                 if (r != EOK)
385                         return r;
386
387                 memcpy(p, bdev->ph_bbuf, len);
388         }
389
390         return r;
391 }
392
393 int ext4_block_cache_write_back(struct ext4_blockdev *bdev, uint8_t on_off)
394 {
395         int r;
396         uint32_t i;
397
398         if (on_off)
399                 bdev->cache_write_back++;
400
401         if (!on_off && bdev->cache_write_back)
402                 bdev->cache_write_back--;
403
404         /*Flush all delayed cache blocks*/
405         if (!bdev->cache_write_back) {
406                 for (i = 0; i < bdev->bc->cnt; ++i) {
407
408                         /*Check if buffer free was delayed.*/
409                         if (!bdev->bc->free_delay[i])
410                                 continue;
411
412                         /*Check reference counter.*/
413                         if (bdev->bc->refctr[i])
414                                 continue;
415
416                         /*Buffer free was delayed and have no reference. Flush
417                          * it.*/
418                         r = ext4_blocks_set_direct(
419                             bdev, bdev->bc->data + bdev->bc->itemsize * i,
420                             bdev->bc->lba[i], 1);
421                         if (r != EOK)
422                                 return r;
423
424                         /*No delayed anymore*/
425                         bdev->bc->free_delay[i] = 0;
426
427                         /*Reduce reference counter*/
428                         bdev->bc->ref_blocks--;
429                 }
430         }
431         return EOK;
432 }
433
434 /**
435  * @}
436  */