summaryrefslogtreecommitdiffstats
path: root/fs/sysv/balloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/sysv/balloc.c')
-rw-r--r--fs/sysv/balloc.c329
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
+}
+