Goto sanos source index

//
// group.c
//
// Disk filesystem group routines
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os/krnl.h>

static void mark_group_desc_dirty(struct filsys *fs, int group) {
  mark_buffer_updated(fs->cache, fs->groupdesc_buffers[group / fs->groupdescs_per_block]);
}

blkno_t new_block(struct filsys *fs, blkno_t goal) {
  unsigned int group;
  unsigned int i;
  struct buf *buf;
  blkno_t block;

  if (goal < fs->super->block_count) {
    // Check the goal
    group = goal / fs->super->blocks_per_group;
    block = goal % fs->super->blocks_per_group;
    buf = get_buffer(fs->cache, fs->groups[group].desc->block_bitmap_block);
    if (!buf) return NOBLOCK;
    if (!test_bit(buf->data, block)) goto block_found;

    // Try to find first free block in group after goal
    block = find_next_zero_bit(buf->data, fs->groups[group].desc->block_count, block + 1);
    if (block != fs->groups[group].desc->block_count) goto block_found;

    release_buffer(fs->cache, buf);
  } else {
    group = 0;
  }

  // Try to locate a free block by going through all groups cyclicly
  for (i = 0; i < fs->super->group_count; i++) {
    if (fs->groups[group].desc->free_block_count > 0) {
      // Get block bitmap
      buf = get_buffer(fs->cache, fs->groups[group].desc->block_bitmap_block);
      if (!buf) return NOBLOCK;

      // Try to find first free block
      if (fs->groups[group].first_free_block != -1) {
        block = find_next_zero_bit(buf->data, fs->groups[group].desc->block_count, fs->groups[group].first_free_block);
      } else {
        block = find_first_zero_bit(buf->data, fs->groups[group].desc->block_count);
        fs->groups[group].first_free_block = block;
      }

      if (block != fs->groups[group].desc->block_count) goto block_found;
    
      release_buffer(fs->cache, buf);
    }

    // Try next group
    group++;
    if (group >= fs->super->group_count) group = 0;
  }

  //panic("disk full");
  return NOBLOCK;

block_found:
  set_bit(buf->data, block);
  mark_buffer_updated(fs->cache, buf);

  fs->super->free_block_count--;
  fs->super_dirty = 1;

  if (fs->groups[group].first_free_block == block) fs->groups[group].first_free_block = block + 1;
  fs->groups[group].desc->free_block_count--;
  mark_group_desc_dirty(fs, group);

  release_buffer(fs->cache, buf);
  block += group * fs->super->blocks_per_group;
  return block;
}

void free_blocks(struct filsys *fs, blkno_t *blocks, int count) {
  unsigned int group;
  unsigned int prev_group;
  struct buf *buf;
  blkno_t block;
  int i;

  prev_group = -1;
  buf = NULL;

  for (i = 0; i < count; i++) {
    group = blocks[i] / fs->super->blocks_per_group;
    block = blocks[i] % fs->super->blocks_per_group;

    if (group != prev_group) {
      if (buf) release_buffer(fs->cache, buf);
      buf = get_buffer(fs->cache, fs->groups[group].desc->block_bitmap_block);
      if (!buf) return;
      prev_group = group;
    }

    clear_bit(buf->data, block);
    mark_buffer_updated(fs->cache, buf);

    fs->super->free_block_count++;
    fs->super_dirty = 1;

    if (fs->groups[group].first_free_block > block) fs->groups[group].first_free_block = block;
    fs->groups[group].desc->free_block_count++;
    mark_group_desc_dirty(fs, group);
  }

  if (buf) release_buffer(fs->cache, buf);
}

ino_t new_inode(struct filsys *fs, ino_t parent, int dir) {
  unsigned int group;
  unsigned int i;
  unsigned int avefreei;
  struct buf *buf;
  ino_t ino;

  if (dir) {
    // Find group with above average free inodes with most free blocks
    avefreei = fs->super->free_inode_count / fs->super->group_count;

    group = -1;
    for (i = 0; i < fs->super->group_count; i++) {
      if (fs->groups[i].desc->free_inode_count && fs->groups[i].desc->free_inode_count >= avefreei) {
        if (group == -1 || fs->groups[i].desc->free_block_count > fs->groups[group].desc->free_block_count) {
          group = i;
        }
      }
    }
    if (group == -1) group = 0;
  } else {
    // Try to locate a free inode by going through all groups cyclicly starting with parents group
    group = parent / fs->super->inodes_per_group;
    for (i = 0; i < fs->super->group_count; i++) {
      if (fs->groups[group].desc->free_inode_count) break;
      group++;
      if (group >= fs->super->group_count) group = 0;
    }
  }

  // Get inode bitmap
  buf = get_buffer(fs->cache, fs->groups[group].desc->inode_bitmap_block);
  if (!buf) return NOINODE;

  // Find first free inode in block
  if (fs->groups[group].first_free_inode != -1) {
    ino = find_next_zero_bit(buf->data, fs->super->inodes_per_group, fs->groups[group].first_free_inode);
  } else {
    ino = find_first_zero_bit(buf->data, fs->super->inodes_per_group);
    fs->groups[group].first_free_inode = ino;
  }

  if (ino == fs->super->inodes_per_group) {
    release_buffer(fs->cache, buf);
    //panic("inode table full");
    return NOINODE;
  }

  // Mark inode as used
  set_bit(buf->data, ino);
  mark_buffer_updated(fs->cache, buf);

  fs->super->free_inode_count--;
  fs->super_dirty = 1;

  if (fs->groups[group].first_free_inode == ino) fs->groups[group].first_free_inode = ino + 1;
  fs->groups[group].desc->free_inode_count--;
  mark_group_desc_dirty(fs, group);

  release_buffer(fs->cache, buf);
  ino += group * fs->super->inodes_per_group;

  return ino;
}

void free_inode(struct filsys *fs, ino_t ino) {
  unsigned int group;
  struct buf *buf;

  group = ino / fs->super->inodes_per_group;
  ino = ino % fs->super->inodes_per_group;

  buf = get_buffer(fs->cache, fs->groups[group].desc->inode_bitmap_block);
  if (!buf) return;

  clear_bit(buf->data, ino);
  mark_buffer_updated(fs->cache, buf);

  fs->super->free_inode_count++;
  fs->super_dirty = 1;

  if (fs->groups[group].first_free_inode > ino) fs->groups[group].first_free_inode = ino;
  fs->groups[group].desc->free_inode_count++;
  mark_group_desc_dirty(fs, group);

  release_buffer(fs->cache, buf);
}