ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / sysv / balloc.c
1 /*
2  *  linux/fs/sysv/balloc.c
3  *
4  *  minix/bitmap.c
5  *  Copyright (C) 1991, 1992  Linus Torvalds
6  *
7  *  ext/freelists.c
8  *  Copyright (C) 1992  Remy Card (card@masi.ibp.fr)
9  *
10  *  xenix/alloc.c
11  *  Copyright (C) 1992  Doug Evans
12  *
13  *  coh/alloc.c
14  *  Copyright (C) 1993  Pascal Haible, Bruno Haible
15  *
16  *  sysv/balloc.c
17  *  Copyright (C) 1993  Bruno Haible
18  *
19  *  This file contains code for allocating/freeing blocks.
20  */
21
22 #include <linux/buffer_head.h>
23 #include <linux/string.h>
24 #include "sysv.h"
25
26 /* We don't trust the value of
27    sb->sv_sbd2->s_tfree = *sb->sv_free_blocks
28    but we nevertheless keep it up to date. */
29
30 static inline u32 *get_chunk(struct super_block *sb, struct buffer_head *bh)
31 {
32         char *bh_data = bh->b_data;
33
34         if (SYSV_SB(sb)->s_type == FSTYPE_SYSV4)
35                 return (u32*)(bh_data+4);
36         else
37                 return (u32*)(bh_data+2);
38 }
39
40 /* NOTE NOTE NOTE: nr is a block number _as_ _stored_ _on_ _disk_ */
41
42 void sysv_free_block(struct super_block * sb, u32 nr)
43 {
44         struct sysv_sb_info * sbi = SYSV_SB(sb);
45         struct buffer_head * bh;
46         u32 *blocks = sbi->s_bcache;
47         unsigned count;
48         unsigned block = fs32_to_cpu(sbi, nr);
49
50         /*
51          * This code does not work at all for AFS (it has a bitmap
52          * free list).  As AFS is supposed to be read-only no one
53          * should call this for an AFS filesystem anyway...
54          */
55         if (sbi->s_type == FSTYPE_AFS)
56                 return;
57
58         if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
59                 printk("sysv_free_block: trying to free block not in datazone\n");
60                 return;
61         }
62
63         lock_super(sb);
64         count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
65
66         if (count > sbi->s_flc_size) {
67                 printk("sysv_free_block: flc_count > flc_size\n");
68                 unlock_super(sb);
69                 return;
70         }
71         /* If the free list head in super-block is full, it is copied
72          * into this block being freed, ditto if it's completely empty
73          * (applies only on Coherent).
74          */
75         if (count == sbi->s_flc_size || count == 0) {
76                 block += sbi->s_block_base;
77                 bh = sb_getblk(sb, block);
78                 if (!bh) {
79                         printk("sysv_free_block: getblk() failed\n");
80                         unlock_super(sb);
81                         return;
82                 }
83                 memset(bh->b_data, 0, sb->s_blocksize);
84                 *(u16*)bh->b_data = cpu_to_fs16(sbi, count);
85                 memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t));
86                 mark_buffer_dirty(bh);
87                 set_buffer_uptodate(bh);
88                 brelse(bh);
89                 count = 0;
90         }
91         sbi->s_bcache[count++] = nr;
92
93         *sbi->s_bcache_count = cpu_to_fs16(sbi, count);
94         fs32_add(sbi, sbi->s_free_blocks, 1);
95         dirty_sb(sb);
96         unlock_super(sb);
97 }
98
99 u32 sysv_new_block(struct super_block * sb)
100 {
101         struct sysv_sb_info *sbi = SYSV_SB(sb);
102         unsigned int block;
103         u32 nr;
104         struct buffer_head * bh;
105         unsigned count;
106
107         lock_super(sb);
108         count = fs16_to_cpu(sbi, *sbi->s_bcache_count);
109
110         if (count == 0) /* Applies only to Coherent FS */
111                 goto Enospc;
112         nr = sbi->s_bcache[--count];
113         if (nr == 0)  /* Applies only to Xenix FS, SystemV FS */
114                 goto Enospc;
115
116         block = fs32_to_cpu(sbi, nr);
117
118         *sbi->s_bcache_count = cpu_to_fs16(sbi, count);
119
120         if (block < sbi->s_firstdatazone || block >= sbi->s_nzones) {
121                 printk("sysv_new_block: new block %d is not in data zone\n",
122                         block);
123                 goto Enospc;
124         }
125
126         if (count == 0) { /* the last block continues the free list */
127                 unsigned count;
128
129                 block += sbi->s_block_base;
130                 if (!(bh = sb_bread(sb, block))) {
131                         printk("sysv_new_block: cannot read free-list block\n");
132                         /* retry this same block next time */
133                         *sbi->s_bcache_count = cpu_to_fs16(sbi, 1);
134                         goto Enospc;
135                 }
136                 count = fs16_to_cpu(sbi, *(u16*)bh->b_data);
137                 if (count > sbi->s_flc_size) {
138                         printk("sysv_new_block: free-list block with >flc_size entries\n");
139                         brelse(bh);
140                         goto Enospc;
141                 }
142                 *sbi->s_bcache_count = cpu_to_fs16(sbi, count);
143                 memcpy(sbi->s_bcache, get_chunk(sb, bh),
144                                 count * sizeof(sysv_zone_t));
145                 brelse(bh);
146         }
147         /* Now the free list head in the superblock is valid again. */
148         fs32_add(sbi, sbi->s_free_blocks, -1);
149         dirty_sb(sb);
150         unlock_super(sb);
151         return nr;
152
153 Enospc:
154         unlock_super(sb);
155         return 0;
156 }
157
158 unsigned long sysv_count_free_blocks(struct super_block * sb)
159 {
160         struct sysv_sb_info * sbi = SYSV_SB(sb);
161         int sb_count;
162         int count;
163         struct buffer_head * bh = NULL;
164         u32 *blocks;
165         unsigned block;
166         int n;
167
168         /*
169          * This code does not work at all for AFS (it has a bitmap
170          * free list).  As AFS is supposed to be read-only we just
171          * lie and say it has no free block at all.
172          */
173         if (sbi->s_type == FSTYPE_AFS)
174                 return 0;
175
176         lock_super(sb);
177         sb_count = fs32_to_cpu(sbi, *sbi->s_free_blocks);
178
179         if (0)
180                 goto trust_sb;
181
182         /* this causes a lot of disk traffic ... */
183         count = 0;
184         n = fs16_to_cpu(sbi, *sbi->s_bcache_count);
185         blocks = sbi->s_bcache;
186         while (1) {
187                 if (n > sbi->s_flc_size)
188                         goto E2big;
189                 block = 0;
190                 while (n && (block = blocks[--n]) != 0)
191                         count++;
192                 if (block == 0)
193                         break;
194
195                 block = fs32_to_cpu(sbi, block);
196                 if (bh)
197                         brelse(bh);
198
199                 if (block < sbi->s_firstdatazone || block >= sbi->s_nzones)
200                         goto Einval;
201                 block += sbi->s_block_base;
202                 bh = sb_bread(sb, block);
203                 if (!bh)
204                         goto Eio;
205                 n = fs16_to_cpu(sbi, *(u16*)bh->b_data);
206                 blocks = get_chunk(sb, bh);
207         }
208         if (bh)
209                 brelse(bh);
210         if (count != sb_count)
211                 goto Ecount;
212 done:
213         unlock_super(sb);
214         return count;
215
216 Einval:
217         printk("sysv_count_free_blocks: new block %d is not in data zone\n",
218                 block);
219         goto trust_sb;
220 Eio:
221         printk("sysv_count_free_blocks: cannot read free-list block\n");
222         goto trust_sb;
223 E2big:
224         printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
225         if (bh)
226                 brelse(bh);
227 trust_sb:
228         count = sb_count;
229         goto done;
230 Ecount:
231         printk("sysv_count_free_blocks: free block count was %d, "
232                 "correcting to %d\n", sb_count, count);
233         if (!(sb->s_flags & MS_RDONLY)) {
234                 *sbi->s_free_blocks = cpu_to_fs32(sbi, count);
235                 dirty_sb(sb);
236         }
237         goto done;
238 }