#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;
tmp = be32_to_cpu(*data);
if (tmp & mask)
goto err_free;
*data = cpu_to_be32(tmp | mask);
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);
}
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);
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:
i = sbi->s_bmap_count;
do {
if (--i < 0)
goto err_full;
bmap++;
bm++;
if (bmap < sbi->s_bmap_count)
continue;
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;
}
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;
do {
blk += 32;
if (++data >= enddata)
goto find_bmap;
} while (!*data);
tmp = be32_to_cpu(*data);
mask = ~0;
find_bit:
bit = ffs(tmp & mask) - 1;
blk += bit + sbi->s_reserved;
mask2 = mask = 1 << (bit & 31);
AFFS_I(inode)->i_lastalloc = blk;
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);
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);
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;
old = be32_to_cpu(((__be32 *)bh->b_data)[offset]);
new = old & mask;
((__be32 *)bh->b_data)[offset] = cpu_to_be32(new);
}
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);
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;
}