diff options
Diffstat (limited to 'fs/sysv/balloc.c')
-rw-r--r-- | fs/sysv/balloc.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c new file mode 100644 index 000000000..f0fb850be --- /dev/null +++ b/fs/sysv/balloc.c @@ -0,0 +1,329 @@ +/* + * linux/fs/sysv/balloc.c + * + * minix/bitmap.c + * Copyright (C) 1991, 1992 Linus Torvalds + * + * ext/freelists.c + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) + * + * xenix/alloc.c + * Copyright (C) 1992 Doug Evans + * + * coh/alloc.c + * Copyright (C) 1993 Pascal Haible, Bruno Haible + * + * sysv/balloc.c + * Copyright (C) 1993 Bruno Haible + * + * This file contains code for allocating/freeing blocks. + */ + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/sysv_fs.h> +#include <linux/string.h> +#include <linux/locks.h> + +/* We don't trust the value of + sb->sv_sbd2->s_tfree = *sb->sv_sb_total_free_blocks + but we nevertheless keep it up to date. */ + +void sysv_free_block(struct super_block * sb, unsigned int block) +{ + struct buffer_head * bh; + char * bh_data; + + if (!sb) { + printk("sysv_free_block: trying to free block on nonexistent device\n"); + return; + } + if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) { + printk("sysv_free_block: trying to free block not in datazone\n"); + return; + } + lock_super(sb); + if (*sb->sv_sb_flc_count > sb->sv_flc_size) { + printk("sysv_free_block: flc_count > flc_size\n"); + unlock_super(sb); + return; + } + /* If the free list head in super-block is full, it is copied + * into this block being freed: + */ + if (*sb->sv_sb_flc_count == sb->sv_flc_size) { + unsigned short * flc_count; + unsigned long * flc_blocks; + + bh = sv_getblk(sb, sb->s_dev, block); + if (!bh) { + printk("sysv_free_block: getblk() failed\n"); + unlock_super(sb); + return; + } + bh_data = bh->b_data; + switch (sb->sv_type) { + case FSTYPE_XENIX: + flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_SYSV4: + flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_SYSV2: + flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_COH: + flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0]; + break; + default: panic("sysv_free_block: invalid fs type\n"); + } + *flc_count = *sb->sv_sb_flc_count; /* = sb->sv_flc_size */ + memcpy(flc_blocks, sb->sv_sb_flc_blocks, *flc_count * sizeof(sysv_zone_t)); + mark_buffer_dirty(bh, 1); + bh->b_uptodate = 1; + brelse(bh); + *sb->sv_sb_flc_count = 0; + } else + /* If the free list head in super-block is empty, create a new head + * in this block being freed: + */ + if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */ + bh = sv_getblk(sb, sb->s_dev, block); + if (!bh) { + printk("sysv_free_block: getblk() failed\n"); + unlock_super(sb); + return; + } + memset(bh->b_data, 0, sb->sv_block_size); + /* this implies ((struct ..._freelist_chunk *) bh->b_data)->flc_count = 0; */ + mark_buffer_dirty(bh, 1); + bh->b_uptodate = 1; + brelse(bh); + /* still *sb->sv_sb_flc_count = 0 */ + } else { + /* Throw away block's contents */ + bh = sv_get_hash_table(sb, sb->s_dev, block); + if (bh) + bh->b_dirt = 0; + brelse(bh); + } + if (sb->sv_convert) + block = to_coh_ulong(block); + sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)++] = block; + if (sb->sv_convert) + *sb->sv_sb_total_free_blocks = + to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) + 1); + else + *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks + 1; + mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ + if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); + sb->s_dirt = 1; /* and needs time stamp */ + unlock_super(sb); +} + +int sysv_new_block(struct super_block * sb) +{ + unsigned int block; + struct buffer_head * bh; + char * bh_data; + + if (!sb) { + printk("sysv_new_block: trying to get new block from nonexistent device\n"); + return 0; + } + lock_super(sb); + if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */ + unlock_super(sb); + return 0; /* no blocks available */ + } + block = sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)-1]; + if (sb->sv_convert) + block = from_coh_ulong(block); + if (block == 0) { /* Applies only to Xenix FS, SystemV FS */ + unlock_super(sb); + return 0; /* no blocks available */ + } + (*sb->sv_sb_flc_count)--; + if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) { + printk("sysv_new_block: new block %d is not in data zone\n",block); + unlock_super(sb); + return 0; + } + if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */ + unsigned short * flc_count; + unsigned long * flc_blocks; + + if (!(bh = sv_bread(sb, sb->s_dev, block))) { + printk("sysv_new_block: cannot read free-list block\n"); + /* retry this same block next time */ + (*sb->sv_sb_flc_count)++; + unlock_super(sb); + return 0; + } + bh_data = bh->b_data; + switch (sb->sv_type) { + case FSTYPE_XENIX: + flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_SYSV4: + flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_SYSV2: + flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_COH: + flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0]; + break; + default: panic("sysv_new_block: invalid fs type\n"); + } + if (*flc_count > sb->sv_flc_size) { + printk("sysv_new_block: free-list block with >flc_size entries\n"); + brelse(bh); + unlock_super(sb); + return 0; + } + *sb->sv_sb_flc_count = *flc_count; + memcpy(sb->sv_sb_flc_blocks, flc_blocks, *flc_count * sizeof(sysv_zone_t)); + brelse(bh); + } + /* Now the free list head in the superblock is valid again. */ + bh = sv_getblk(sb, sb->s_dev, block); + if (!bh) { + printk("sysv_new_block: getblk() failed\n"); + unlock_super(sb); + return 0; + } + if (bh->b_count != 1) { + printk("sysv_new_block: block already in use\n"); + unlock_super(sb); + return 0; + } + memset(bh->b_data, 0, sb->sv_block_size); + mark_buffer_dirty(bh, 1); + bh->b_uptodate = 1; + brelse(bh); + if (sb->sv_convert) + *sb->sv_sb_total_free_blocks = + to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) - 1); + else + *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks - 1; + mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */ + if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1); + sb->s_dirt = 1; /* and needs time stamp */ + unlock_super(sb); + return block; +} + +unsigned long sysv_count_free_blocks(struct super_block * sb) +{ +#if 1 /* test */ + int count, old_count; + unsigned int block; + struct buffer_head * bh; + char * bh_data; + int i; + + /* this causes a lot of disk traffic ... */ + count = 0; + lock_super(sb); + if (*sb->sv_sb_flc_count > 0) { + for (i = *sb->sv_sb_flc_count ; /* i > 0 */ ; ) { + block = sb->sv_sb_flc_blocks[--i]; + if (sb->sv_convert) + block = from_coh_ulong(block); + if (block == 0) /* block 0 terminates list */ + goto done; + count++; + if (i == 0) + break; + } + /* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */ + while (1) { + unsigned short * flc_count; + unsigned long * flc_blocks; + + if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) { + printk("sysv_count_free_blocks: new block %d is not in data zone\n",block); + break; + } + if (!(bh = sv_bread(sb, sb->s_dev, block))) { + printk("sysv_count_free_blocks: cannot read free-list block\n"); + break; + } + bh_data = bh->b_data; + switch (sb->sv_type) { + case FSTYPE_XENIX: + flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_SYSV4: + flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_SYSV2: + flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0]; + break; + case FSTYPE_COH: + flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree; + flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0]; + break; + default: panic("sysv_count_free_blocks: invalid fs type\n"); + } + if (*flc_count > sb->sv_flc_size) { + printk("sysv_count_free_blocks: free-list block with >flc_size entries\n"); + brelse(bh); + break; + } + if (*flc_count == 0) { /* Applies only to Coherent FS */ + brelse(bh); + break; + } + for (i = *flc_count ; /* i > 0 */ ; ) { + block = flc_blocks[--i]; + if (sb->sv_convert) + block = from_coh_ulong(block); + if (block == 0) /* block 0 terminates list */ + break; + count++; + if (i == 0) + break; + } + /* block = flc_blocks[0], the last block continues the free list */ + brelse(bh); + if (block == 0) /* Applies only to Xenix FS and SystemV FS */ + break; + } + done: ; + } + old_count = *sb->sv_sb_total_free_blocks; + if (sb->sv_convert) + old_count = from_coh_ulong(old_count); + if (count != old_count) { + printk("sysv_count_free_blocks: free block count was %d, correcting to %d\n",old_count,count); + if (!(sb->s_flags & MS_RDONLY)) { + *sb->sv_sb_total_free_blocks = (sb->sv_convert ? to_coh_ulong(count) : count); + mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */ + sb->s_dirt = 1; /* and needs time stamp */ + } + } + unlock_super(sb); + return count; +#else + int count; + + count = *sb->sv_sb_total_free_blocks; + if (sb->sv_convert) + count = from_coh_ulong(count); + return count; +#endif +} + |