*/
#include <stdlib.h>
#include <erofs/cache.h>
+#include <erofs/bitops.h>
#include "erofs/print.h"
static int erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh)
struct erofs_bufmgr *erofs_buffer_init(struct erofs_sb_info *sbi,
erofs_blk_t startblk)
{
- struct erofs_bufmgr *bufmgr;
+ unsigned int blksiz = erofs_blksiz(sbi);
+ struct erofs_bufmgr *bmgr;
int i, j, k;
- bufmgr = malloc(sizeof(struct erofs_bufmgr));
- if (!bufmgr)
+ bmgr = malloc(sizeof(struct erofs_bufmgr));
+ if (!bmgr)
return NULL;
- init_list_head(&bufmgr->blkh.list);
- bufmgr->blkh.blkaddr = NULL_ADDR;
- bufmgr->last_mapped_block = &bufmgr->blkh;
-
- for (i = 0; i < ARRAY_SIZE(bufmgr->watermeter); i++)
- for (j = 0; j < ARRAY_SIZE(bufmgr->watermeter[0]); j++)
- for (k = 0; k < (1 << sbi->blkszbits); k++)
- init_list_head(&bufmgr->watermeter[i][j][k]);
- bufmgr->tail_blkaddr = startblk;
- bufmgr->sbi = sbi;
- return bufmgr;
+ bmgr->sbi = sbi;
+ for (i = 0; i < ARRAY_SIZE(bmgr->watermeter); i++) {
+ for (j = 0; j < ARRAY_SIZE(bmgr->watermeter[0]); j++) {
+ for (k = 0; k < blksiz; k++)
+ init_list_head(&bmgr->watermeter[i][j][k]);
+ memset(bmgr->bktmap[i][j], 0, blksiz / BITS_PER_LONG);
+ }
+ }
+ init_list_head(&bmgr->blkh.list);
+ bmgr->blkh.blkaddr = NULL_ADDR;
+ bmgr->tail_blkaddr = startblk;
+ bmgr->last_mapped_block = &bmgr->blkh;
+ return bmgr;
+}
+
+static void erofs_clear_bbktmap(struct erofs_bufmgr *bmgr, int type,
+ bool mapped, int nr)
+{
+ int bit = erofs_blksiz(bmgr->sbi) - (nr + 1);
+
+ DBG_BUGON(bit < 0);
+ __erofs_clear_bit(bit, bmgr->bktmap[type][mapped]);
}
-static void erofs_update_bwatermeter(struct erofs_buffer_block *bb)
+static void erofs_set_bbktmap(struct erofs_bufmgr *bmgr, int type,
+ bool mapped, int nr)
+{
+ int bit = erofs_blksiz(bmgr->sbi) - (nr + 1);
+
+ DBG_BUGON(bit < 0);
+ __erofs_set_bit(bit, bmgr->bktmap[type][mapped]);
+}
+
+static void erofs_update_bwatermeter(struct erofs_buffer_block *bb, bool free)
{
struct erofs_bufmgr *bmgr = bb->buffers.fsprivate;
struct erofs_sb_info *sbi = bmgr->sbi;
- struct list_head *bkt;
-
- bkt = bmgr->watermeter[bb->type][bb->blkaddr != NULL_ADDR] +
- (bb->buffers.off & (erofs_blksiz(sbi) - 1));
+ unsigned int blksiz = erofs_blksiz(sbi);
+ bool mapped = bb->blkaddr != NULL_ADDR;
+ struct list_head *h = bmgr->watermeter[bb->type][mapped];
+ unsigned int nr;
+
+ if (bb->sibling.next == bb->sibling.prev) {
+ if ((uintptr_t)(bb->sibling.next - h) < blksiz) {
+ nr = bb->sibling.next - h;
+ erofs_clear_bbktmap(bmgr, bb->type, mapped, nr);
+ } else if (bb->sibling.next != &bb->sibling) {
+ nr = bb->sibling.next -
+ bmgr->watermeter[bb->type][!mapped];
+ erofs_clear_bbktmap(bmgr, bb->type, !mapped, nr);
+ }
+ }
list_del(&bb->sibling);
- list_add_tail(&bb->sibling, bkt);
+ if (free)
+ return;
+ nr = bb->buffers.off & (blksiz - 1);
+ list_add_tail(&bb->sibling, h + nr);
+ erofs_set_bbktmap(bmgr, bb->type, mapped, nr);
}
/* return occupied bytes in specific buffer block if succeed */
/* need to update the tail_blkaddr */
if (tailupdate)
bmgr->tail_blkaddr = blkaddr + bb->buffers.nblocks;
- erofs_update_bwatermeter(bb);
+ erofs_update_bwatermeter(bb, false);
}
return ((alignedoffset + incr + blkmask) & blkmask) + 1;
}
return __erofs_battach(bb, NULL, incr, 1, 0, false);
}
+static bool __find_next_bucket(struct erofs_bufmgr *bmgr, int type, bool mapped,
+ unsigned int *index, unsigned int end)
+{
+ const unsigned int blksiz = erofs_blksiz(bmgr->sbi);
+ const unsigned int blkmask = blksiz - 1;
+ unsigned int l = *index, r;
+
+ if (l <= end) {
+ DBG_BUGON(l < end);
+ return false;
+ }
+
+ l = blkmask - (l & blkmask);
+ r = blkmask - (end & blkmask);
+ if (l >= r) {
+ l = erofs_find_next_bit(bmgr->bktmap[type][mapped], blksiz, l);
+ if (l < blksiz) {
+ *index = round_down(*index, blksiz) + blkmask - l;
+ return true;
+ }
+ l = 0;
+ *index -= blksiz;
+ }
+ l = erofs_find_next_bit(bmgr->bktmap[type][mapped], r, l);
+ if (l >= r)
+ return false;
+ *index = round_down(*index, blksiz) + blkmask - l;
+ return true;
+}
+
static int erofs_bfind_for_attach(struct erofs_bufmgr *bmgr,
int type, erofs_off_t size,
unsigned int inline_ext,
while (mapped) {
--mapped;
- for (; index > end; --index) {
+ for (; __find_next_bucket(bmgr, type, mapped, &index, end);
+ --index) {
struct list_head *bt;
used = index & blkmask;
bt = bmgr->watermeter[type][mapped] + used;
- if (list_empty(bt))
- continue;
+ DBG_BUGON(list_empty(bt));
cur = list_first_entry(bt, struct erofs_buffer_block,
sibling);
}
}
bmgr->last_mapped_block = bb;
- erofs_update_bwatermeter(bb);
+ erofs_update_bwatermeter(bb, false);
}
blkaddr = bb->blkaddr + bb->buffers.nblocks;
if (bb == bmgr->last_mapped_block)
bmgr->last_mapped_block = list_prev_entry(bb, list);
- list_del(&bb->sibling);
+ erofs_update_bwatermeter(bb, true);
list_del(&bb->list);
free(bb);
}