2 * linux/fs/sysv/balloc.c
5 * Copyright (C) 1991, 1992 Linus Torvalds
8 * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
11 * Copyright (C) 1992 Doug Evans
14 * Copyright (C) 1993 Pascal Haible, Bruno Haible
17 * Copyright (C) 1993 Bruno Haible
19 * This file contains code for allocating/freeing blocks.
22 #include <linux/kernel.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
;
38 printk("sysv_free_block: trying to free block on nonexistent device\n");
41 if (block
< sb
->sv_firstdatazone
|| block
>= sb
->sv_nzones
) {
42 printk("sysv_free_block: trying to free block not in datazone\n");
46 if (*sb
->sv_sb_flc_count
> sb
->sv_flc_size
) {
47 printk("sysv_free_block: flc_count > flc_size\n");
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
) {
58 bh
= sv_getblk(sb
, sb
->s_dev
, block
);
60 printk("sysv_free_block: getblk() failed\n");
65 switch (sb
->sv_type
) {
67 flc_count
= &((struct xenix_freelist_chunk
*) bh_data
)->fl_nfree
;
68 flc_blocks
= &((struct xenix_freelist_chunk
*) bh_data
)->fl_free
[0];
71 flc_count
= &((struct sysv4_freelist_chunk
*) bh_data
)->fl_nfree
;
72 flc_blocks
= &((struct sysv4_freelist_chunk
*) bh_data
)->fl_free
[0];
75 flc_count
= &((struct sysv2_freelist_chunk
*) bh_data
)->fl_nfree
;
76 flc_blocks
= &((struct sysv2_freelist_chunk
*) bh_data
)->fl_free
[0];
79 flc_count
= &((struct coh_freelist_chunk
*) bh_data
)->fl_nfree
;
80 flc_blocks
= &((struct coh_freelist_chunk
*) bh_data
)->fl_free
[0];
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);
89 *sb
->sv_sb_flc_count
= 0;
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
);
97 printk("sysv_free_block: getblk() failed\n");
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);
106 /* still *sb->sv_sb_flc_count = 0 */
108 /* Throw away block's contents */
109 bh
= sv_get_hash_table(sb
, sb
->s_dev
, block
);
111 mark_buffer_clean(bh
);
115 block
= to_coh_ulong(block
);
116 sb
->sv_sb_flc_blocks
[(*sb
->sv_sb_flc_count
)++] = block
;
118 *sb
->sv_sb_total_free_blocks
=
119 to_coh_ulong(from_coh_ulong(*sb
->sv_sb_total_free_blocks
) + 1);
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 */
128 int sysv_new_block(struct super_block
* sb
)
131 struct buffer_head
* bh
;
135 printk("sysv_new_block: trying to get new block from nonexistent device\n");
139 if (*sb
->sv_sb_flc_count
== 0) { /* Applies only to Coherent FS */
141 return 0; /* no blocks available */
143 block
= sb
->sv_sb_flc_blocks
[(*sb
->sv_sb_flc_count
)-1];
145 block
= from_coh_ulong(block
);
146 if (block
== 0) { /* Applies only to Xenix FS, SystemV FS */
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
);
156 if (*sb
->sv_sb_flc_count
== 0) { /* the last block continues the free list */
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
)++;
167 bh_data
= bh
->b_data
;
168 switch (sb
->sv_type
) {
170 flc_count
= &((struct xenix_freelist_chunk
*) bh_data
)->fl_nfree
;
171 flc_blocks
= &((struct xenix_freelist_chunk
*) bh_data
)->fl_free
[0];
174 flc_count
= &((struct sysv4_freelist_chunk
*) bh_data
)->fl_nfree
;
175 flc_blocks
= &((struct sysv4_freelist_chunk
*) bh_data
)->fl_free
[0];
178 flc_count
= &((struct sysv2_freelist_chunk
*) bh_data
)->fl_nfree
;
179 flc_blocks
= &((struct sysv2_freelist_chunk
*) bh_data
)->fl_free
[0];
182 flc_count
= &((struct coh_freelist_chunk
*) bh_data
)->fl_nfree
;
183 flc_blocks
= &((struct coh_freelist_chunk
*) bh_data
)->fl_free
[0];
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");
193 *sb
->sv_sb_flc_count
= *flc_count
;
194 memcpy(sb
->sv_sb_flc_blocks
, flc_blocks
, *flc_count
* sizeof(sysv_zone_t
));
197 /* Now the free list head in the superblock is valid again. */
198 bh
= sv_getblk(sb
, sb
->s_dev
, block
);
200 printk("sysv_new_block: getblk() failed\n");
204 if (bh
->b_count
!= 1) {
205 printk("sysv_new_block: block already in use\n");
209 memset(bh
->b_data
, 0, sb
->sv_block_size
);
210 mark_buffer_dirty(bh
, 1);
211 mark_buffer_uptodate(bh
, 1);
214 *sb
->sv_sb_total_free_blocks
=
215 to_coh_ulong(from_coh_ulong(*sb
->sv_sb_total_free_blocks
) - 1);
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 */
225 unsigned long sysv_count_free_blocks(struct super_block
* sb
)
228 int count
, old_count
;
230 struct buffer_head
* bh
;
234 /* this causes a lot of disk traffic ... */
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
];
241 block
= from_coh_ulong(block
);
242 if (block
== 0) /* block 0 terminates list */
248 /* block = sb->sv_sb_flc_blocks[0], the last block continues the free list */
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
);
257 if (!(bh
= sv_bread(sb
, sb
->s_dev
, block
))) {
258 printk("sysv_count_free_blocks: cannot read free-list block\n");
261 bh_data
= bh
->b_data
;
262 switch (sb
->sv_type
) {
264 flc_count
= &((struct xenix_freelist_chunk
*) bh_data
)->fl_nfree
;
265 flc_blocks
= &((struct xenix_freelist_chunk
*) bh_data
)->fl_free
[0];
268 flc_count
= &((struct sysv4_freelist_chunk
*) bh_data
)->fl_nfree
;
269 flc_blocks
= &((struct sysv4_freelist_chunk
*) bh_data
)->fl_free
[0];
272 flc_count
= &((struct sysv2_freelist_chunk
*) bh_data
)->fl_nfree
;
273 flc_blocks
= &((struct sysv2_freelist_chunk
*) bh_data
)->fl_free
[0];
276 flc_count
= &((struct coh_freelist_chunk
*) bh_data
)->fl_nfree
;
277 flc_blocks
= &((struct coh_freelist_chunk
*) bh_data
)->fl_free
[0];
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");
286 if (*flc_count
== 0) { /* Applies only to Coherent FS */
290 for (i
= *flc_count
; /* i > 0 */ ; ) {
291 block
= flc_blocks
[--i
];
293 block
= from_coh_ulong(block
);
294 if (block
== 0) /* block 0 terminates list */
300 /* block = flc_blocks[0], the last block continues the free list */
302 if (block
== 0) /* Applies only to Xenix FS and SystemV FS */
307 old_count
= *sb
->sv_sb_total_free_blocks
;
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 */
323 count
= *sb
->sv_sb_total_free_blocks
;
325 count
= from_coh_ulong(count
);