355 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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");
 | 
						|
  }
 | 
						|
}
 |