Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/roland...
[linux-2.6] / 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 sysv_zone_t *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 (sysv_zone_t*)(bh_data+4);
36         else
37                 return (sysv_zone_t*)(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, sysv_zone_t nr)
43 {
44         struct sysv_sb_info * sbi = SYSV_SB(sb);
45         struct buffer_head * bh;
46         sysv_zone_t *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                 *(__fs16*)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 sysv_zone_t sysv_new_block(struct super_block * sb)
100 {
101         struct sysv_sb_info *sbi = SYSV_SB(sb);
102         unsigned int block;
103         sysv_zone_t 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, *(__fs16*)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         sysv_zone_t *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                 sysv_zone_t zone;
188                 if (n > sbi->s_flc_size)
189                         goto E2big;
190                 zone = 0;
191                 while (n && (zone = blocks[--n]) != 0)
192                         count++;
193                 if (zone == 0)
194                         break;
195
196                 block = fs32_to_cpu(sbi, zone);
197                 if (bh)
198                         brelse(bh);
199
200                 if (block < sbi->s_firstdatazone || block >= sbi->s_nzones)
201                         goto Einval;
202                 block += sbi->s_block_base;
203                 bh = sb_bread(sb, block);
204                 if (!bh)
205                         goto Eio;
206                 n = fs16_to_cpu(sbi, *(__fs16*)bh->b_data);
207                 blocks = get_chunk(sb, bh);
208         }
209         if (bh)
210                 brelse(bh);
211         if (count != sb_count)
212                 goto Ecount;
213 done:
214         unlock_super(sb);
215         return count;
216
217 Einval:
218         printk("sysv_count_free_blocks: new block %d is not in data zone\n",
219                 block);
220         goto trust_sb;
221 Eio:
222         printk("sysv_count_free_blocks: cannot read free-list block\n");
223         goto trust_sb;
224 E2big:
225         printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
226         if (bh)
227                 brelse(bh);
228 trust_sb:
229         count = sb_count;
230         goto done;
231 Ecount:
232         printk("sysv_count_free_blocks: free block count was %d, "
233                 "correcting to %d\n", sb_count, count);
234         if (!(sb->s_flags & MS_RDONLY)) {
235                 *sbi->s_free_blocks = cpu_to_fs32(sbi, count);
236                 dirty_sb(sb);
237         }
238         goto done;
239 }