- uint64_t pba;
- uint32_t pb_cnt;
- uint32_t i;
- bool is_new;
- int r;
-
- ext4_assert(bdev && b);
-
- if(!(bdev->flags & EXT4_BDEV_INITIALIZED))
- return EIO;
-
- if(!(lba < bdev->lg_bcnt))
- return ERANGE;
-
- b->dirty = 0;
- b->lb_id = lba;
-
- /*If cache is full we have to flush it anyway :(*/
- if(ext4_bcache_is_full(bdev->bc) && bdev->cache_flush_delay){
- for (i = 0; i < bdev->bc->cnt; ++i) {
- /*Check if buffer free was delayed.*/
- if(!bdev->bc->free_delay[i])
- continue;
-
- /*Check reference counter.*/
- if(bdev->bc->refctr[i])
- continue;
-
- /*Buffer free was delayed and have no reference. Flush it.*/
- r = ext4_blocks_set_direct(bdev,
- bdev->bc->data + bdev->bc->itemsize * i,
- bdev->bc->lba[i], 1);
- if(r != EOK)
- return r;
-
- /*No delayed anymore*/
- bdev->bc->free_delay[i] = 0;
-
- /*Reduce refered block count*/
- bdev->bc->ref_blocks--;
- }
- }
-
-
- r = ext4_bcache_alloc(bdev->bc, b, &is_new);
- if(r != EOK)
- return r;
-
-
- if(!is_new){
- /*Block is in cache. Read from physical device is not required*/
- return EOK;
- }
-
- if(!b->data)
- return ENOMEM;
-
- pba = (lba * bdev->lg_bsize) / bdev->ph_bsize;
- pb_cnt = bdev->lg_bsize / bdev->ph_bsize;
-
- r = bdev->bread(bdev, b->data, pba, pb_cnt);
-
- if(r != EOK){
- ext4_bcache_free(bdev->bc, b, 0);
- b->lb_id = 0;
- return r;
- }
-
- bdev->bread_ctr++;
- return EOK;
+ uint64_t pba;
+ uint32_t pb_cnt;
+ uint32_t i;
+ bool is_new;
+ int r;
+
+ ext4_assert(bdev && b);
+
+ if (!(bdev->flags & EXT4_BDEV_INITIALIZED))
+ return EIO;
+
+ if (!(lba < bdev->lg_bcnt))
+ return ERANGE;
+
+ b->dirty = 0;
+ b->lb_id = lba;
+
+ /*If cache is full we have to flush it anyway :(*/
+ if (ext4_bcache_is_full(bdev->bc) && bdev->cache_write_back) {
+
+ uint32_t free_candidate = bdev->bc->cnt;
+ uint32_t min_lru = 0xFFFFFFFF;
+
+ for (i = 0; i < bdev->bc->cnt; ++i) {
+ /*Check if buffer free was delayed.*/
+ if (!bdev->bc->free_delay[i])
+ continue;
+
+ /*Check reference counter.*/
+ if (bdev->bc->refctr[i])
+ continue;
+
+ if (bdev->bc->lru_id[i] < min_lru) {
+ min_lru = bdev->bc->lru_id[i];
+ free_candidate = i;
+ continue;
+ }
+ }
+
+ if (free_candidate < bdev->bc->cnt) {
+ /*Buffer free was delayed and have no reference. Flush
+ * it.*/
+ r = ext4_blocks_set_direct(
+ bdev, bdev->bc->data +
+ bdev->bc->itemsize * free_candidate,
+ bdev->bc->lba[free_candidate], 1);
+ if (r != EOK)
+ return r;
+
+ /*No delayed anymore*/
+ bdev->bc->free_delay[free_candidate] = 0;
+
+ /*Reduce reference counter*/
+ bdev->bc->ref_blocks--;
+ }
+ }
+
+ r = ext4_bcache_alloc(bdev->bc, b, &is_new);
+ if (r != EOK)
+ return r;
+
+ if (!is_new) {
+ /*Block is in cache. Read from physical device is not required*/
+ return EOK;
+ }
+
+ if (!b->data)
+ return ENOMEM;
+
+ pba = (lba * bdev->lg_bsize) / bdev->ph_bsize;
+ pb_cnt = bdev->lg_bsize / bdev->ph_bsize;
+
+ r = bdev->bread(bdev, b->data, pba, pb_cnt);
+
+ if (r != EOK) {
+ ext4_bcache_free(bdev->bc, b, 0);
+ b->lb_id = 0;
+ return r;
+ }
+
+ bdev->bread_ctr++;
+ return EOK;