Logo Search packages:      
Sourcecode: parted version File versions

ext2_buffer.c

/*
    ext2_buffer.c -- ext2 buffer cache
    Copyright (C) 1998-2000 Free Software Foundation, Inc.
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "config.h"

#ifndef DISCOVER_ONLY

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ext2.h"

/* pseudo-header */

static __inline__ int ext2_block_hash(blk_t block)
{
      unsigned long x;

      x = block ^ (block >> 8) ^ (block >> 16) ^ (block >> 24);
      return x & ((1 << ext2_hash_bits) - 1);
}

static struct ext2_buffer_head *ext2_bh_alloc   (struct ext2_buffer_cache *, blk_t);
static void                     ext2_bh_dealloc (struct ext2_buffer_head *);
static struct ext2_buffer_head *ext2_bh_find    (struct ext2_buffer_cache *, blk_t);
static int                      ext2_bh_do_read (struct ext2_buffer_head *);
static int                      ext2_bh_do_write(struct ext2_buffer_head *);
static void                     ext2_bh_hash    (struct ext2_buffer_head *);
static void                     ext2_bh_unhash  (struct ext2_buffer_head *);



static int try_to_flush(struct ext2_buffer_cache *bc)
{
      int i;

      for (i=0;i<bc->size;i++)
      {
            struct ext2_buffer_head *bh;

            bh = &bc->heads[i];
      
            if (bh->alloc && !bh->usecount && !bh->dirty)
            {
                  ext2_bh_dealloc(bh);
                  return 1;
            }
      }

      for (i=0;i<bc->size;i++)
      {
            struct ext2_buffer_head *bh;

            bh = &bc->heads[i];

            if (bh->alloc && !bh->usecount && bh->dirty)
            {
                  ext2_bh_do_write(bh);
                  ext2_bh_dealloc(bh);
                  return 1;
            }
      }

      if (ped_exception_throw (PED_EXCEPTION_ERROR,
                         PED_EXCEPTION_IGNORE_CANCEL,
                         _("Couldn't flush buffer cache!"))
                  != PED_EXCEPTION_IGNORE)
            return 0;
      return 1;
}





static struct ext2_buffer_head *ext2_bh_alloc(struct ext2_buffer_cache *bc, blk_t block)
{
      struct ext2_buffer_head *bh;
      int i;

      bh = NULL;

 tryagain:
      for (i=0;i<bc->size;i++)
      {
            bh = &bc->heads[i];

            if (!bh->alloc)
                  break;
      }

      if (i == bc->size)
      {
            try_to_flush(bc);
            goto tryagain;
      }

      bh = &bc->heads[i];

      bh->next = NULL;
      bh->prev = NULL;
      bh->block = block;
      bh->usecount = 0;
      bh->dirty = 0;
      bh->alloc = 1;
      bc->numalloc++;

      ext2_bh_hash(bh);

      return bh;
}

static void ext2_bh_dealloc(struct ext2_buffer_head *bh)
{
      if (bh->dirty)
            ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_IGNORE,
                  "deallocing() a dirty buffer! %i\n", bh->block);

      ext2_bh_unhash(bh);
      bh->alloc = 0;
      bh->bc->numalloc--;
}

static struct ext2_buffer_head *ext2_bh_find(struct ext2_buffer_cache *bc, blk_t block)
{
      struct ext2_buffer_head *a;
      struct ext2_buffer_head *b;
      int                hash;

      hash = ext2_block_hash(block);
      a = bc->hash[hash];

      if (a != NULL)
      {
            b = a;
            do
            {
                  if (a->block == block)
                        return a;

                  a = a->next;
            } while (a != b);
      }

      return NULL;
}

static int ext2_bh_do_read(struct ext2_buffer_head *bh)
{
      return ext2_read_blocks(bh->bc->fs, bh->data, bh->block, 1);
}

static int ext2_bh_do_write(struct ext2_buffer_head *bh)
{
      if (!bh->alloc) {
            ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
                  "Attempt to write unallocated buffer.");
            return 0;
      }

      ext2_write_blocks(bh->bc->fs, bh->data, bh->block, 1);
      bh->dirty = 0;
      return 1;
}

static void ext2_bh_hash(struct ext2_buffer_head *bh)
{
      int hash;

      hash = ext2_block_hash(bh->block);
      if (bh->bc->hash[hash] != NULL)
      {
            bh->next = bh->bc->hash[hash];
            bh->prev = bh->next->prev;
            bh->next->prev = bh;
            bh->prev->next = bh;
            return;
      }

      bh->bc->hash[hash] = bh;
      bh->next = bh->prev = bh;
}

static void ext2_bh_unhash(struct ext2_buffer_head *bh)
{
      int hash;

      hash = ext2_block_hash(bh->block);

      bh->prev->next = bh->next;
      bh->next->prev = bh->prev;

      if (bh->bc->hash[hash] == bh)
      {
            if (bh->next != bh)
                  bh->bc->hash[hash] = bh->next;
            else
                  bh->bc->hash[hash] = NULL;
      }

      bh->next = NULL;
      bh->prev = NULL;
}







static int breadimmhits = 0;
static int breadindhits = 0;
static int breadmisses = 0;

void ext2_bcache_deinit(struct ext2_fs *fs)
{
      ext2_bcache_sync(fs);
      ped_free(fs->bc->buffermem);
      ped_free(fs->bc->hash);
      ped_free(fs->bc->heads);
      ped_free(fs->bc);

      if (fs->opt_verbose)
            fprintf(stderr,
                  "direct hits: %i, indirect hits: %i, misses: %i\n",
                  breadimmhits,
                  breadindhits,
                  breadmisses);
}

void ext2_bcache_dump(struct ext2_fs *fs)
{
      int i;

      fprintf(stderr, "buffer cache dump:\n");

      for (i=0;i<(1<<ext2_hash_bits);i++)
            if (fs->bc->hash[i] != NULL)
            {
                  struct ext2_buffer_head *a;
                  struct ext2_buffer_head *b;

                  fprintf(stderr, "%i: ", i);

                  a = b = fs->bc->hash[i];
                  do
                  {
                        fprintf(stderr, "%i ", a->block);
                        a = a->next;
                  } while (a != b);

                  fprintf(stderr, "\n");
            }
}

int ext2_bcache_flush(struct ext2_fs *fs, blk_t block)
{
      struct ext2_buffer_head *bh;

      if ((bh = ext2_bh_find(fs->bc, block)) == NULL)
            return 1;

      if (bh->usecount) {
            ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
                  "Attempt to flush a buffer that's in use! [%i,%i]",
                  bh->block, bh->usecount);
            return 0;
      }

      if (bh->dirty) {
            if (!ext2_bh_do_write(bh))
                  return 0;
      }

      ext2_bh_dealloc(bh);
      return 1;
}

int ext2_bcache_flush_range(struct ext2_fs *fs, blk_t block, blk_t num)
{
      blk_t end = block + num;

      for (; block < end; block++) {
            if (!ext2_bcache_flush(fs, block))
                  return 0;
      }
      return 1;
}

int ext2_bcache_init(struct ext2_fs *fs)
{
      struct ext2_buffer_cache *bc;
      int i;
      int size;

      size = ext2_buffer_cache_pool_size >> (fs->logsize - 10);

      if ((bc = (struct ext2_buffer_cache *) ped_malloc(sizeof(struct ext2_buffer_cache))) == NULL)
            return 0;

      if ((bc->heads = (struct ext2_buffer_head *) ped_malloc(size * sizeof(struct ext2_buffer_head))) == NULL)
            return 0;

      if ((bc->hash = (struct ext2_buffer_head **) ped_malloc(sizeof(struct ext2_buffer_head *) << ext2_hash_bits)) == NULL)
      {
            ped_free(bc->heads);
            ped_free(bc);
            return 0;
      }

      if ((bc->buffermem = (unsigned char *) ped_malloc(ext2_buffer_cache_pool_size << 10)) == NULL)
      {
            ped_free(bc->hash);
            ped_free(bc->heads);
            ped_free(bc);
            return 0;
      }

      bc->cache = &bc->heads[0];
      bc->fs = fs;
      bc->size = size;
      bc->numalloc = 0;

      for (i=0;i<size;i++)
      {
            bc->heads[i].data = bc->buffermem + (i << fs->logsize);
            bc->heads[i].bc = bc;
            bc->heads[i].alloc = 0;
      }

      for (i=0;i<(1<<ext2_hash_bits);i++)
            bc->hash[i] = NULL;

      fs->bc = bc;

      return 1;
}

int ext2_bcache_sync(struct ext2_fs *fs)
{
      int i;

      for (i=0;i<fs->bc->size;i++)
      {
            struct ext2_buffer_head *bh;

            bh = &fs->bc->heads[i];

            if (bh->alloc && bh->dirty) {
                  if (!ext2_bh_do_write(bh))
                        return 0;
            }
      }
      return 1;
}








struct ext2_buffer_head *ext2_bcreate(struct ext2_fs *fs, blk_t block)
{
      struct ext2_buffer_head *bh;

      if ((bh = ext2_bh_find(fs->bc, block)) != NULL)
      {
            bh->usecount++;
      }
      else
      {
            bh = ext2_bh_alloc(fs->bc, block);
            bh->usecount = 1;
      }

      memset(bh->data, 0, fs->blocksize);
      bh->dirty = 1;

      return bh;
}

struct ext2_buffer_head *ext2_bread(struct ext2_fs *fs, blk_t block)
{
      struct ext2_buffer_head *bh;

      if ((bh = fs->bc->cache)->block == block)
      {
            breadimmhits++;
            bh->usecount++;
            return bh;
      }

      if ((bh = ext2_bh_find(fs->bc, block)) != NULL)
      {
            fs->bc->cache = bh;
            breadindhits++;
            bh->usecount++;
            return bh;
      }

      breadmisses++;

      bh = ext2_bh_alloc(fs->bc, block);
      fs->bc->cache = bh;
      bh->usecount = 1;
      if (!ext2_bh_do_read(bh)) {
            ext2_bh_dealloc(bh);
            return NULL;
      }

      return bh;
}

int ext2_brelse(struct ext2_buffer_head *bh, int forget)
{
      if (bh->usecount-- == 1 && forget)
      {
            if (bh->dirty) {
                  if (!ext2_bh_do_write(bh))
                        return 0;
            }

            ext2_bh_dealloc(bh);
      }
      return 1;
}

#endif /* !DISCOVER_ONLY */


Generated by  Doxygen 1.6.0   Back to index