Merge with Linux 2.4.0-test5-pre6.
[linux-2.6/linux-mips.git] / fs / sysv / balloc.c
blobf5240a75c1e5169e0a79e96ba8e883e624d3952b
1 /*
2 * linux/fs/sysv/balloc.c
4 * minix/bitmap.c
5 * Copyright (C) 1991, 1992 Linus Torvalds
7 * ext/freelists.c
8 * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
10 * xenix/alloc.c
11 * Copyright (C) 1992 Doug Evans
13 * coh/alloc.c
14 * Copyright (C) 1993 Pascal Haible, Bruno Haible
16 * sysv/balloc.c
17 * Copyright (C) 1993 Bruno Haible
19 * This file contains code for allocating/freeing blocks.
22 #include <linux/kernel.h>
23 #include <linux/fs.h>
24 #include <linux/sysv_fs.h>
25 #include <linux/string.h>
26 #include <linux/locks.h>
28 /* We don't trust the value of
29 sb->sv_sbd2->s_tfree = *sb->sv_sb_total_free_blocks
30 but we nevertheless keep it up to date. */
32 void sysv_free_block(struct super_block * sb, unsigned int block)
34 struct buffer_head * bh;
35 char * bh_data;
37 if (!sb) {
38 printk("sysv_free_block: trying to free block on nonexistent device\n");
39 return;
41 if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
42 printk("sysv_free_block: trying to free block not in datazone\n");
43 return;
45 lock_super(sb);
46 if (*sb->sv_sb_flc_count > sb->sv_flc_size) {
47 printk("sysv_free_block: flc_count > flc_size\n");
48 unlock_super(sb);
49 return;
51 /* If the free list head in super-block is full, it is copied
52 * into this block being freed:
54 if (*sb->sv_sb_flc_count == sb->sv_flc_size) {
55 u16 * flc_count;
56 u32 * flc_blocks;
58 bh = sv_getblk(sb, sb->s_dev, block);
59 if (!bh) {
60 printk("sysv_free_block: getblk() failed\n");
61 unlock_super(sb);
62 return;
64 bh_data = bh->b_data;
65 switch (sb->sv_type) {
66 case FSTYPE_XENIX:
67 flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
68 flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
69 break;
70 case FSTYPE_SYSV4:
71 flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
72 flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
73 break;
74 case FSTYPE_SYSV2:
75 flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
76 flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
77 break;
78 case FSTYPE_COH:
79 flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
80 flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
81 break;
82 default: panic("sysv_free_block: invalid fs type\n");
84 *flc_count = *sb->sv_sb_flc_count; /* = sb->sv_flc_size */
85 memcpy(flc_blocks, sb->sv_sb_flc_blocks, *flc_count * sizeof(sysv_zone_t));
86 mark_buffer_dirty(bh, 1);
87 mark_buffer_uptodate(bh, 1);
88 brelse(bh);
89 *sb->sv_sb_flc_count = 0;
90 } else
91 /* If the free list head in super-block is empty, create a new head
92 * in this block being freed:
94 if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */
95 bh = sv_getblk(sb, sb->s_dev, block);
96 if (!bh) {
97 printk("sysv_free_block: getblk() failed\n");
98 unlock_super(sb);
99 return;
101 memset(bh->b_data, 0, sb->sv_block_size);
102 /* this implies ((struct ..._freelist_chunk *) bh->b_data)->flc_count = 0; */
103 mark_buffer_dirty(bh, 1);
104 mark_buffer_uptodate(bh, 1);
105 brelse(bh);
106 /* still *sb->sv_sb_flc_count = 0 */
107 } else {
108 /* Throw away block's contents */
109 bh = sv_get_hash_table(sb, sb->s_dev, block);
110 if (bh)
111 mark_buffer_clean(bh);
112 brelse(bh);
114 if (sb->sv_convert)
115 block = to_coh_ulong(block);
116 sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)++] = block;
117 if (sb->sv_convert)
118 *sb->sv_sb_total_free_blocks =
119 to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) + 1);
120 else
121 *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks + 1;
122 mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
123 if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
124 sb->s_dirt = 1; /* and needs time stamp */
125 unlock_super(sb);
128 int sysv_new_block(struct super_block * sb)
130 unsigned int block;
131 struct buffer_head * bh;
132 char * bh_data;
134 if (!sb) {
135 printk("sysv_new_block: trying to get new block from nonexistent device\n");
136 return 0;
138 lock_super(sb);
139 if (*sb->sv_sb_flc_count == 0) { /* Applies only to Coherent FS */
140 unlock_super(sb);
141 return 0; /* no blocks available */
143 block = sb->sv_sb_flc_blocks[(*sb->sv_sb_flc_count)-1];
144 if (sb->sv_convert)
145 block = from_coh_ulong(block);
146 if (block == 0) { /* Applies only to Xenix FS, SystemV FS */
147 unlock_super(sb);
148 return 0; /* no blocks available */
150 (*sb->sv_sb_flc_count)--;
151 if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
152 printk("sysv_new_block: new block %d is not in data zone\n",block);
153 unlock_super(sb);
154 return 0;
156 if (*sb->sv_sb_flc_count == 0) { /* the last block continues the free list */
157 u16 * flc_count;
158 u32 * flc_blocks;
160 if (!(bh = sv_bread(sb, sb->s_dev, block))) {
161 printk("sysv_new_block: cannot read free-list block\n");
162 /* retry this same block next time */
163 (*sb->sv_sb_flc_count)++;
164 unlock_super(sb);
165 return 0;
167 bh_data = bh->b_data;
168 switch (sb->sv_type) {
169 case FSTYPE_XENIX:
170 flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
171 flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
172 break;
173 case FSTYPE_SYSV4:
174 flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
175 flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
176 break;
177 case FSTYPE_SYSV2:
178 flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
179 flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
180 break;
181 case FSTYPE_COH:
182 flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
183 flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
184 break;
185 default: panic("sysv_new_block: invalid fs type\n");
187 if (*flc_count > sb->sv_flc_size) {
188 printk("sysv_new_block: free-list block with >flc_size entries\n");
189 brelse(bh);
190 unlock_super(sb);
191 return 0;
193 *sb->sv_sb_flc_count = *flc_count;
194 memcpy(sb->sv_sb_flc_blocks, flc_blocks, *flc_count * sizeof(sysv_zone_t));
195 brelse(bh);
197 /* Now the free list head in the superblock is valid again. */
198 bh = sv_getblk(sb, sb->s_dev, block);
199 if (!bh) {
200 printk("sysv_new_block: getblk() failed\n");
201 unlock_super(sb);
202 return 0;
204 if (atomic_read(&bh->b_count) != 1) {
205 printk("sysv_new_block: block already in use\n");
206 unlock_super(sb);
207 return 0;
209 memset(bh->b_data, 0, sb->sv_block_size);
210 mark_buffer_dirty(bh, 1);
211 mark_buffer_uptodate(bh, 1);
212 brelse(bh);
213 if (sb->sv_convert)
214 *sb->sv_sb_total_free_blocks =
215 to_coh_ulong(from_coh_ulong(*sb->sv_sb_total_free_blocks) - 1);
216 else
217 *sb->sv_sb_total_free_blocks = *sb->sv_sb_total_free_blocks - 1;
218 mark_buffer_dirty(sb->sv_bh1, 1); /* super-block has been modified */
219 if (sb->sv_bh1 != sb->sv_bh2) mark_buffer_dirty(sb->sv_bh2, 1);
220 sb->s_dirt = 1; /* and needs time stamp */
221 unlock_super(sb);
222 return block;
225 unsigned long sysv_count_free_blocks(struct super_block * sb)
227 #if 1 /* test */
228 int count, old_count;
229 unsigned int block;
230 struct buffer_head * bh;
231 char * bh_data;
232 int i;
234 /* this causes a lot of disk traffic ... */
235 count = 0;
236 lock_super(sb);
237 if (*sb->sv_sb_flc_count > 0) {
238 for (i = *sb->sv_sb_flc_count ; /* i > 0 */ ; ) {
239 block = sb->sv_sb_flc_blocks[--i];
240 if (sb->sv_convert)
241 block = from_coh_ulong(block);
242 if (block == 0) /* block 0 terminates list */
243 goto done;
244 count++;
245 if (i == 0)
246 break;
248 /* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */
249 while (1) {
250 u16 * flc_count;
251 u32 * flc_blocks;
253 if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
254 printk("sysv_count_free_blocks: new block %d is not in data zone\n",block);
255 break;
257 if (!(bh = sv_bread(sb, sb->s_dev, block))) {
258 printk("sysv_count_free_blocks: cannot read free-list block\n");
259 break;
261 bh_data = bh->b_data;
262 switch (sb->sv_type) {
263 case FSTYPE_XENIX:
264 flc_count = &((struct xenix_freelist_chunk *) bh_data)->fl_nfree;
265 flc_blocks = &((struct xenix_freelist_chunk *) bh_data)->fl_free[0];
266 break;
267 case FSTYPE_SYSV4:
268 flc_count = &((struct sysv4_freelist_chunk *) bh_data)->fl_nfree;
269 flc_blocks = &((struct sysv4_freelist_chunk *) bh_data)->fl_free[0];
270 break;
271 case FSTYPE_SYSV2:
272 flc_count = &((struct sysv2_freelist_chunk *) bh_data)->fl_nfree;
273 flc_blocks = &((struct sysv2_freelist_chunk *) bh_data)->fl_free[0];
274 break;
275 case FSTYPE_COH:
276 flc_count = &((struct coh_freelist_chunk *) bh_data)->fl_nfree;
277 flc_blocks = &((struct coh_freelist_chunk *) bh_data)->fl_free[0];
278 break;
279 default: panic("sysv_count_free_blocks: invalid fs type\n");
281 if (*flc_count > sb->sv_flc_size) {
282 printk("sysv_count_free_blocks: free-list block with >flc_size entries\n");
283 brelse(bh);
284 break;
286 if (*flc_count == 0) { /* Applies only to Coherent FS */
287 brelse(bh);
288 break;
290 for (i = *flc_count ; /* i > 0 */ ; ) {
291 block = flc_blocks[--i];
292 if (sb->sv_convert)
293 block = from_coh_ulong(block);
294 if (block == 0) /* block 0 terminates list */
295 break;
296 count++;
297 if (i == 0)
298 break;
300 /* block = flc_blocks[0], the last block continues the free list */
301 brelse(bh);
302 if (block == 0) /* Applies only to Xenix FS and SystemV FS */
303 break;
305 done: ;
307 old_count = *sb->sv_sb_total_free_blocks;
308 if (sb->sv_convert)
309 old_count = from_coh_ulong(old_count);
310 if (count != old_count) {
311 printk("sysv_count_free_blocks: free block count was %d, correcting to %d\n",old_count,count);
312 if (!(sb->s_flags & MS_RDONLY)) {
313 *sb->sv_sb_total_free_blocks = (sb->sv_convert ? to_coh_ulong(count) : count);
314 mark_buffer_dirty(sb->sv_bh2, 1); /* super-block has been modified */
315 sb->s_dirt = 1; /* and needs time stamp */
318 unlock_super(sb);
319 return count;
320 #else
321 int count;
323 count = *sb->sv_sb_total_free_blocks;
324 if (sb->sv_convert)
325 count = from_coh_ulong(count);
326 return count;
327 #endif