forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
8.3 KiB
365 lines
8.3 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* linux/fs/affs/bitmap.c |
|
* |
|
* (c) 1996 Hans-Joachim Widmaier |
|
* |
|
* bitmap.c contains the code that handles all bitmap related stuff - |
|
* block allocation, deallocation, calculation of free space. |
|
*/ |
|
|
|
#include <linux/slab.h> |
|
#include "affs.h" |
|
|
|
u32 |
|
affs_count_free_blocks(struct super_block *sb) |
|
{ |
|
struct affs_bm_info *bm; |
|
u32 free; |
|
int i; |
|
|
|
pr_debug("%s()\n", __func__); |
|
|
|
if (sb_rdonly(sb)) |
|
return 0; |
|
|
|
mutex_lock(&AFFS_SB(sb)->s_bmlock); |
|
|
|
bm = AFFS_SB(sb)->s_bitmap; |
|
free = 0; |
|
for (i = AFFS_SB(sb)->s_bmap_count; i > 0; bm++, i--) |
|
free += bm->bm_free; |
|
|
|
mutex_unlock(&AFFS_SB(sb)->s_bmlock); |
|
|
|
return free; |
|
} |
|
|
|
void |
|
affs_free_block(struct super_block *sb, u32 block) |
|
{ |
|
struct affs_sb_info *sbi = AFFS_SB(sb); |
|
struct affs_bm_info *bm; |
|
struct buffer_head *bh; |
|
u32 blk, bmap, bit, mask, tmp; |
|
__be32 *data; |
|
|
|
pr_debug("%s(%u)\n", __func__, block); |
|
|
|
if (block > sbi->s_partition_size) |
|
goto err_range; |
|
|
|
blk = block - sbi->s_reserved; |
|
bmap = blk / sbi->s_bmap_bits; |
|
bit = blk % sbi->s_bmap_bits; |
|
bm = &sbi->s_bitmap[bmap]; |
|
|
|
mutex_lock(&sbi->s_bmlock); |
|
|
|
bh = sbi->s_bmap_bh; |
|
if (sbi->s_last_bmap != bmap) { |
|
affs_brelse(bh); |
|
bh = affs_bread(sb, bm->bm_key); |
|
if (!bh) |
|
goto err_bh_read; |
|
sbi->s_bmap_bh = bh; |
|
sbi->s_last_bmap = bmap; |
|
} |
|
|
|
mask = 1 << (bit & 31); |
|
data = (__be32 *)bh->b_data + bit / 32 + 1; |
|
|
|
/* mark block free */ |
|
tmp = be32_to_cpu(*data); |
|
if (tmp & mask) |
|
goto err_free; |
|
*data = cpu_to_be32(tmp | mask); |
|
|
|
/* fix checksum */ |
|
tmp = be32_to_cpu(*(__be32 *)bh->b_data); |
|
*(__be32 *)bh->b_data = cpu_to_be32(tmp - mask); |
|
|
|
mark_buffer_dirty(bh); |
|
affs_mark_sb_dirty(sb); |
|
bm->bm_free++; |
|
|
|
mutex_unlock(&sbi->s_bmlock); |
|
return; |
|
|
|
err_free: |
|
affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block); |
|
mutex_unlock(&sbi->s_bmlock); |
|
return; |
|
|
|
err_bh_read: |
|
affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key); |
|
sbi->s_bmap_bh = NULL; |
|
sbi->s_last_bmap = ~0; |
|
mutex_unlock(&sbi->s_bmlock); |
|
return; |
|
|
|
err_range: |
|
affs_error(sb, "affs_free_block","Block %u outside partition", block); |
|
} |
|
|
|
/* |
|
* Allocate a block in the given allocation zone. |
|
* Since we have to byte-swap the bitmap on little-endian |
|
* machines, this is rather expensive. Therefore we will |
|
* preallocate up to 16 blocks from the same word, if |
|
* possible. We are not doing preallocations in the |
|
* header zone, though. |
|
*/ |
|
|
|
u32 |
|
affs_alloc_block(struct inode *inode, u32 goal) |
|
{ |
|
struct super_block *sb; |
|
struct affs_sb_info *sbi; |
|
struct affs_bm_info *bm; |
|
struct buffer_head *bh; |
|
__be32 *data, *enddata; |
|
u32 blk, bmap, bit, mask, mask2, tmp; |
|
int i; |
|
|
|
sb = inode->i_sb; |
|
sbi = AFFS_SB(sb); |
|
|
|
pr_debug("balloc(inode=%lu,goal=%u): ", inode->i_ino, goal); |
|
|
|
if (AFFS_I(inode)->i_pa_cnt) { |
|
pr_debug("%d\n", AFFS_I(inode)->i_lastalloc+1); |
|
AFFS_I(inode)->i_pa_cnt--; |
|
return ++AFFS_I(inode)->i_lastalloc; |
|
} |
|
|
|
if (!goal || goal > sbi->s_partition_size) { |
|
if (goal) |
|
affs_warning(sb, "affs_balloc", "invalid goal %d", goal); |
|
//if (!AFFS_I(inode)->i_last_block) |
|
// affs_warning(sb, "affs_balloc", "no last alloc block"); |
|
goal = sbi->s_reserved; |
|
} |
|
|
|
blk = goal - sbi->s_reserved; |
|
bmap = blk / sbi->s_bmap_bits; |
|
bm = &sbi->s_bitmap[bmap]; |
|
|
|
mutex_lock(&sbi->s_bmlock); |
|
|
|
if (bm->bm_free) |
|
goto find_bmap_bit; |
|
|
|
find_bmap: |
|
/* search for the next bmap buffer with free bits */ |
|
i = sbi->s_bmap_count; |
|
do { |
|
if (--i < 0) |
|
goto err_full; |
|
bmap++; |
|
bm++; |
|
if (bmap < sbi->s_bmap_count) |
|
continue; |
|
/* restart search at zero */ |
|
bmap = 0; |
|
bm = sbi->s_bitmap; |
|
} while (!bm->bm_free); |
|
blk = bmap * sbi->s_bmap_bits; |
|
|
|
find_bmap_bit: |
|
|
|
bh = sbi->s_bmap_bh; |
|
if (sbi->s_last_bmap != bmap) { |
|
affs_brelse(bh); |
|
bh = affs_bread(sb, bm->bm_key); |
|
if (!bh) |
|
goto err_bh_read; |
|
sbi->s_bmap_bh = bh; |
|
sbi->s_last_bmap = bmap; |
|
} |
|
|
|
/* find an unused block in this bitmap block */ |
|
bit = blk % sbi->s_bmap_bits; |
|
data = (__be32 *)bh->b_data + bit / 32 + 1; |
|
enddata = (__be32 *)((u8 *)bh->b_data + sb->s_blocksize); |
|
mask = ~0UL << (bit & 31); |
|
blk &= ~31UL; |
|
|
|
tmp = be32_to_cpu(*data); |
|
if (tmp & mask) |
|
goto find_bit; |
|
|
|
/* scan the rest of the buffer */ |
|
do { |
|
blk += 32; |
|
if (++data >= enddata) |
|
/* didn't find something, can only happen |
|
* if scan didn't start at 0, try next bmap |
|
*/ |
|
goto find_bmap; |
|
} while (!*data); |
|
tmp = be32_to_cpu(*data); |
|
mask = ~0; |
|
|
|
find_bit: |
|
/* finally look for a free bit in the word */ |
|
bit = ffs(tmp & mask) - 1; |
|
blk += bit + sbi->s_reserved; |
|
mask2 = mask = 1 << (bit & 31); |
|
AFFS_I(inode)->i_lastalloc = blk; |
|
|
|
/* prealloc as much as possible within this word */ |
|
while ((mask2 <<= 1)) { |
|
if (!(tmp & mask2)) |
|
break; |
|
AFFS_I(inode)->i_pa_cnt++; |
|
mask |= mask2; |
|
} |
|
bm->bm_free -= AFFS_I(inode)->i_pa_cnt + 1; |
|
|
|
*data = cpu_to_be32(tmp & ~mask); |
|
|
|
/* fix checksum */ |
|
tmp = be32_to_cpu(*(__be32 *)bh->b_data); |
|
*(__be32 *)bh->b_data = cpu_to_be32(tmp + mask); |
|
|
|
mark_buffer_dirty(bh); |
|
affs_mark_sb_dirty(sb); |
|
|
|
mutex_unlock(&sbi->s_bmlock); |
|
|
|
pr_debug("%d\n", blk); |
|
return blk; |
|
|
|
err_bh_read: |
|
affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key); |
|
sbi->s_bmap_bh = NULL; |
|
sbi->s_last_bmap = ~0; |
|
err_full: |
|
mutex_unlock(&sbi->s_bmlock); |
|
pr_debug("failed\n"); |
|
return 0; |
|
} |
|
|
|
int affs_init_bitmap(struct super_block *sb, int *flags) |
|
{ |
|
struct affs_bm_info *bm; |
|
struct buffer_head *bmap_bh = NULL, *bh = NULL; |
|
__be32 *bmap_blk; |
|
u32 size, blk, end, offset, mask; |
|
int i, res = 0; |
|
struct affs_sb_info *sbi = AFFS_SB(sb); |
|
|
|
if (*flags & SB_RDONLY) |
|
return 0; |
|
|
|
if (!AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag) { |
|
pr_notice("Bitmap invalid - mounting %s read only\n", sb->s_id); |
|
*flags |= SB_RDONLY; |
|
return 0; |
|
} |
|
|
|
sbi->s_last_bmap = ~0; |
|
sbi->s_bmap_bh = NULL; |
|
sbi->s_bmap_bits = sb->s_blocksize * 8 - 32; |
|
sbi->s_bmap_count = (sbi->s_partition_size - sbi->s_reserved + |
|
sbi->s_bmap_bits - 1) / sbi->s_bmap_bits; |
|
size = sbi->s_bmap_count * sizeof(*bm); |
|
bm = sbi->s_bitmap = kzalloc(size, GFP_KERNEL); |
|
if (!sbi->s_bitmap) { |
|
pr_err("Bitmap allocation failed\n"); |
|
return -ENOMEM; |
|
} |
|
|
|
bmap_blk = (__be32 *)sbi->s_root_bh->b_data; |
|
blk = sb->s_blocksize / 4 - 49; |
|
end = blk + 25; |
|
|
|
for (i = sbi->s_bmap_count; i > 0; bm++, i--) { |
|
affs_brelse(bh); |
|
|
|
bm->bm_key = be32_to_cpu(bmap_blk[blk]); |
|
bh = affs_bread(sb, bm->bm_key); |
|
if (!bh) { |
|
pr_err("Cannot read bitmap\n"); |
|
res = -EIO; |
|
goto out; |
|
} |
|
if (affs_checksum_block(sb, bh)) { |
|
pr_warn("Bitmap %u invalid - mounting %s read only.\n", |
|
bm->bm_key, sb->s_id); |
|
*flags |= SB_RDONLY; |
|
goto out; |
|
} |
|
pr_debug("read bitmap block %d: %d\n", blk, bm->bm_key); |
|
bm->bm_free = memweight(bh->b_data + 4, sb->s_blocksize - 4); |
|
|
|
/* Don't try read the extension if this is the last block, |
|
* but we also need the right bm pointer below |
|
*/ |
|
if (++blk < end || i == 1) |
|
continue; |
|
if (bmap_bh) |
|
affs_brelse(bmap_bh); |
|
bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk])); |
|
if (!bmap_bh) { |
|
pr_err("Cannot read bitmap extension\n"); |
|
res = -EIO; |
|
goto out; |
|
} |
|
bmap_blk = (__be32 *)bmap_bh->b_data; |
|
blk = 0; |
|
end = sb->s_blocksize / 4 - 1; |
|
} |
|
|
|
offset = (sbi->s_partition_size - sbi->s_reserved) % sbi->s_bmap_bits; |
|
mask = ~(0xFFFFFFFFU << (offset & 31)); |
|
pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask); |
|
offset = offset / 32 + 1; |
|
|
|
if (mask) { |
|
u32 old, new; |
|
|
|
/* Mark unused bits in the last word as allocated */ |
|
old = be32_to_cpu(((__be32 *)bh->b_data)[offset]); |
|
new = old & mask; |
|
//if (old != new) { |
|
((__be32 *)bh->b_data)[offset] = cpu_to_be32(new); |
|
/* fix checksum */ |
|
//new -= old; |
|
//old = be32_to_cpu(*(__be32 *)bh->b_data); |
|
//*(__be32 *)bh->b_data = cpu_to_be32(old - new); |
|
//mark_buffer_dirty(bh); |
|
//} |
|
/* correct offset for the bitmap count below */ |
|
//offset++; |
|
} |
|
while (++offset < sb->s_blocksize / 4) |
|
((__be32 *)bh->b_data)[offset] = 0; |
|
((__be32 *)bh->b_data)[0] = 0; |
|
((__be32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh)); |
|
mark_buffer_dirty(bh); |
|
|
|
/* recalculate bitmap count for last block */ |
|
bm--; |
|
bm->bm_free = memweight(bh->b_data + 4, sb->s_blocksize - 4); |
|
|
|
out: |
|
affs_brelse(bh); |
|
affs_brelse(bmap_bh); |
|
return res; |
|
} |
|
|
|
void affs_free_bitmap(struct super_block *sb) |
|
{ |
|
struct affs_sb_info *sbi = AFFS_SB(sb); |
|
|
|
if (!sbi->s_bitmap) |
|
return; |
|
|
|
affs_brelse(sbi->s_bmap_bh); |
|
sbi->s_bmap_bh = NULL; |
|
sbi->s_last_bmap = ~0; |
|
kfree(sbi->s_bitmap); |
|
sbi->s_bitmap = NULL; |
|
}
|
|
|