minix/servers/ext2/balloc.c

355 lines
10 KiB
C
Raw Normal View History

/* This files manages blocks allocation and deallocation.
*
* The entry points into this file are:
* discard_preallocated_blocks: Discard preallocated blocks.
* alloc_block: somebody wants to allocate a block; find one.
* free_block: indicate that a block is available for new allocation.
*
* Created:
* June 2010 (Evgeniy Ivanov)
*/
#include "fs.h"
#include <string.h>
#include <stdlib.h>
#include <minix/com.h>
#include <minix/u64.h>
#include "buf.h"
#include "inode.h"
#include "super.h"
#include "const.h"
FORWARD _PROTOTYPE( block_t alloc_block_bit, (struct super_block *sp,
block_t origin,
struct inode *rip));
/*===========================================================================*
* discard_preallocated_blocks *
*===========================================================================*/
PUBLIC void discard_preallocated_blocks(struct inode *rip)
{
/* When called for rip, discard (free) blocks preallocated for rip,
* otherwise discard all preallocated blocks.
* Normally it should be called in following situations:
* 1. File is closed.
* 2. File is truncated.
* 3. Non-sequential write.
* 4. inode is "unloaded" from the memory.
* 5. No free blocks left (discard all preallocated blocks).
*/
int i;
if (rip) {
rip->i_prealloc_count = rip->i_prealloc_index = 0;
for (i = 0; i < EXT2_PREALLOC_BLOCKS; i++) {
if (rip->i_prealloc_blocks[i] != NO_BLOCK) {
free_block(rip->i_sp, rip->i_prealloc_blocks[i]);
rip->i_prealloc_blocks[i] = NO_BLOCK;
}
}
return;
}
/* Discard all allocated blocks.
* Probably there are just few blocks on the disc, so forbid preallocation.*/
for(rip = &inode[0]; rip < &inode[NR_INODES]; rip++) {
rip->i_prealloc_count = rip->i_prealloc_index = 0;
rip->i_preallocation = 0; /* forbid preallocation */
for (i = 0; i < EXT2_PREALLOC_BLOCKS; i++) {
if (rip->i_prealloc_blocks[i] != NO_BLOCK) {
free_block(rip->i_sp, rip->i_prealloc_blocks[i]);
rip->i_prealloc_blocks[i] = NO_BLOCK;
}
}
}
}
/*===========================================================================*
* alloc_block *
*===========================================================================*/
PUBLIC block_t alloc_block(struct inode *rip, block_t block)
{
/* Allocate a block for inode. If block is provided, then use it as a goal:
* try to allocate this block or his neghbors.
* If block is not provided then goal is group, where inode lives.
*/
block_t goal;
block_t b;
struct super_block *sp = rip->i_sp;
if (sp->s_rd_only)
panic("can't alloc block on read-only filesys.");
/* Check for free blocks. First time discard preallocation,
* next time return NO_BLOCK
*/
if (!opt.use_reserved_blocks &&
sp->s_free_blocks_count <= sp->s_r_blocks_count) {
discard_preallocated_blocks(NULL);
} else if (sp->s_free_blocks_count <= EXT2_PREALLOC_BLOCKS) {
discard_preallocated_blocks(NULL);
}
if (!opt.use_reserved_blocks &&
sp->s_free_blocks_count <= sp->s_r_blocks_count) {
return(NO_BLOCK);
} else if (sp->s_free_blocks_count == 0) {
return(NO_BLOCK);
}
if (block != NO_BLOCK) {
goal = block;
if (rip->i_preallocation && rip->i_prealloc_count > 0) {
/* check if goal is preallocated */
b = rip->i_prealloc_blocks[rip->i_prealloc_index];
if (block == b || (block + 1) == b) {
/* use preallocated block */
rip->i_prealloc_blocks[rip->i_prealloc_index] = NO_BLOCK;
rip->i_prealloc_count--;
rip->i_prealloc_index++;
if (rip->i_prealloc_index >= EXT2_PREALLOC_BLOCKS) {
rip->i_prealloc_index = 0;
ASSERT(rip->i_prealloc_count == 0);
}
rip->i_bsearch = b;
return b;
} else {
/* probably non-sequential write operation,
* disable preallocation for this inode.
*/
rip->i_preallocation = 0;
discard_preallocated_blocks(rip);
}
}
} else {
int group = (rip->i_num - 1) / sp->s_inodes_per_group;
goal = sp->s_blocks_per_group*group + sp->s_first_data_block;
}
if (rip->i_preallocation && rip->i_prealloc_count) {
ext2_debug("There're preallocated blocks, but they're\
neither used or freed!");
}
b = alloc_block_bit(sp, goal, rip);
if (b != NO_BLOCK)
rip->i_bsearch = b;
return b;
}
FORWARD _PROTOTYPE( void check_block_number, (block_t block,
struct super_block *sp,
struct group_desc *gd) );
/*===========================================================================*
* alloc_block_bit *
*===========================================================================*/
PRIVATE block_t alloc_block_bit(sp, goal, rip)
struct super_block *sp; /* the filesystem to allocate from */
block_t goal; /* try to allocate near this block */
struct inode *rip; /* used for preallocation */
{
block_t block = NO_BLOCK; /* allocated block */
int word; /* word in block bitmap */
bit_t bit = -1;
int group;
char update_bsearch = FALSE;
int i;
if (goal >= sp->s_blocks_count ||
(goal < sp->s_first_data_block && goal != 0)) {
goal = sp->s_bsearch;
}
if (goal <= sp->s_bsearch) {
/* No reason to search in a place with no free blocks */
goal = sp->s_bsearch;
update_bsearch = TRUE;
}
/* Figure out where to start the bit search. */
word = ((goal - sp->s_first_data_block) % sp->s_blocks_per_group)
/ FS_BITCHUNK_BITS;
/* Try to allocate block at any group starting from the goal's group.
* First time goal's group is checked from the word=goal, after all
* groups checked, it's checked again from word=0, that's why "i <=".
*/
group = (goal - sp->s_first_data_block) / sp->s_blocks_per_group;
for (i = 0; i <= sp->s_groups_count; i++, group++) {
struct buf *bp;
struct group_desc *gd;
if (group >= sp->s_groups_count)
group = 0;
gd = get_group_desc(group);
if (gd == NULL)
panic("can't get group_desc to alloc block");
if (gd->free_blocks_count == 0) {
word = 0;
continue;
}
bp = get_block(sp->s_dev, gd->block_bitmap, NORMAL);
if (rip->i_preallocation &&
gd->free_blocks_count >= (EXT2_PREALLOC_BLOCKS * 4) ) {
/* Try to preallocate blocks */
if (rip->i_prealloc_count != 0) {
/* kind of glitch... */
discard_preallocated_blocks(rip);
ext2_debug("warning, discarding previously preallocated\
blocks! It had to be done by another code.");
}
ASSERT(rip->i_prealloc_count == 0);
/* we preallocate bytes only */
ASSERT(EXT2_PREALLOC_BLOCKS == sizeof(char)*CHAR_BIT);
bit = setbyte(bp->b_bitmap, sp->s_blocks_per_group, word);
if (bit != -1) {
block = bit + sp->s_first_data_block +
group * sp->s_blocks_per_group;
check_block_number(block, sp, gd);
/* We preallocate a byte starting from block.
* First preallocated block will be returned as
* normally allocated block.
*/
for (i = 1; i < EXT2_PREALLOC_BLOCKS; i++) {
check_block_number(block + i, sp, gd);
rip->i_prealloc_blocks[i-1] = block + i;
}
rip->i_prealloc_index = 0;
rip->i_prealloc_count = EXT2_PREALLOC_BLOCKS - 1;
bp->b_dirt = DIRTY; /* by setbyte */
put_block(bp, MAP_BLOCK);
gd->free_blocks_count -= EXT2_PREALLOC_BLOCKS;
sp->s_free_blocks_count -= EXT2_PREALLOC_BLOCKS;
group_descriptors_dirty = DIRTY;
return block;
}
}
bit = setbit(bp->b_bitmap, sp->s_blocks_per_group, word);
if (bit == -1) {
if (word == 0) {
panic("ext2: allocator failed to allocate a bit in bitmap\
with free bits.");
} else {
word = 0;
continue;
}
}
block = sp->s_first_data_block + group * sp->s_blocks_per_group + bit;
check_block_number(block, sp, gd);
bp->b_dirt = DIRTY; /* Now it's safe to mark it as dirty */
put_block(bp, MAP_BLOCK);
gd->free_blocks_count--;
sp->s_free_blocks_count--;
group_descriptors_dirty = DIRTY;
if (update_bsearch && block != -1 && block != NO_BLOCK) {
/* We searched from the beginning, update bsearch. */
sp->s_bsearch = block;
}
return block;
}
return block;
}
/*===========================================================================*
* free_block *
*===========================================================================*/
PUBLIC void free_block(struct super_block *sp, bit_t bit_returned)
{
/* Return a block by turning off its bitmap bit. */
int group; /* group number of bit_returned */
int bit; /* bit_returned number within its group */
struct buf *bp;
struct group_desc *gd;
if (sp->s_rd_only)
panic("can't free bit on read-only filesys.");
if (bit_returned >= sp->s_blocks_count ||
bit_returned < sp->s_first_data_block)
panic("trying to free block %d beyond blocks scope.",
bit_returned);
/* At first search group, to which bit_returned belongs to
* and figure out in what word bit is stored.
*/
group = (bit_returned - sp->s_first_data_block) / sp->s_blocks_per_group;
bit = (bit_returned - sp->s_first_data_block) % sp->s_blocks_per_group;
gd = get_group_desc(group);
if (gd == NULL)
panic("can't get group_desc to alloc block");
/* We might be buggy (No way! :P), so check if we deallocate
* data block, but not control (system) block.
* This should never happen.
*/
if (bit_returned == gd->inode_bitmap || bit_returned == gd->block_bitmap
|| (bit_returned >= gd->inode_table
&& bit_returned < (gd->inode_table + sp->s_itb_per_group))) {
ext2_debug("ext2: freeing non-data block %d\n", bit_returned);
panic("trying to deallocate \
system/control block, hardly poke author.");
}
bp = get_block(sp->s_dev, gd->block_bitmap, NORMAL);
if (unsetbit(bp->b_bitmap, bit))
panic("Tried to free unused block", bit_returned);
bp->b_dirt = DIRTY;
put_block(bp, MAP_BLOCK);
gd->free_blocks_count++;
sp->s_free_blocks_count++;
group_descriptors_dirty = DIRTY;
if (bit_returned < sp->s_bsearch)
sp->s_bsearch = bit_returned;
}
PRIVATE void check_block_number(block_t block, struct super_block *sp,
struct group_desc *gd)
{
/* Check if we allocated a data block, but not control (system) block.
* Only major bug can cause us to allocate wrong block. If it happens,
* we panic (and don't bloat filesystem's bitmap).
*/
if (block == gd->inode_bitmap || block == gd->block_bitmap ||
(block >= gd->inode_table
&& block < (gd->inode_table + sp->s_itb_per_group))) {
ext2_debug("ext2: allocating non-data block %d\n", block);
panic("ext2: block allocator tryed to return \
system/control block, poke author.\n");
}
if (block >= sp->s_blocks_count) {
panic("ext2: allocator returned blocknum greater, than \
total number of blocks.\n");
}
}