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/fs.h>
23 #include <linux/sysv_fs.h>
24 #include <linux/locks.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
get_chunk(struct super_block * sb,struct buffer_head * bh)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 (sb->sv_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
sysv_free_block(struct super_block * sb,u32 nr)42 void sysv_free_block(struct super_block * sb, u32 nr)
43 {
44 struct buffer_head * bh;
45 u32 *blocks = sb->sv_bcache;
46 unsigned count;
47 unsigned block = fs32_to_cpu(sb, nr);
48
49 /*
50 * This code does not work at all for AFS (it has a bitmap
51 * free list). As AFS is supposed to be read-only no one
52 * should call this for an AFS filesystem anyway...
53 */
54 if (sb->sv_type == FSTYPE_AFS)
55 return;
56
57 if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
58 printk("sysv_free_block: trying to free block not in datazone\n");
59 return;
60 }
61
62 lock_super(sb);
63 count = fs16_to_cpu(sb, *sb->sv_bcache_count);
64
65 if (count > sb->sv_flc_size) {
66 printk("sysv_free_block: flc_count > flc_size\n");
67 unlock_super(sb);
68 return;
69 }
70 /* If the free list head in super-block is full, it is copied
71 * into this block being freed, ditto if it's completely empty
72 * (applies only on Coherent).
73 */
74 if (count == sb->sv_flc_size || count == 0) {
75 block += sb->sv_block_base;
76 bh = sb_getblk(sb, block);
77 if (!bh) {
78 printk("sysv_free_block: getblk() failed\n");
79 unlock_super(sb);
80 return;
81 }
82 memset(bh->b_data, 0, sb->s_blocksize);
83 *(u16*)bh->b_data = cpu_to_fs16(sb, count);
84 memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t));
85 mark_buffer_dirty(bh);
86 mark_buffer_uptodate(bh, 1);
87 brelse(bh);
88 count = 0;
89 }
90 sb->sv_bcache[count++] = nr;
91
92 *sb->sv_bcache_count = cpu_to_fs16(sb, count);
93 fs32_add(sb, sb->sv_free_blocks, 1);
94 dirty_sb(sb);
95 unlock_super(sb);
96 }
97
sysv_new_block(struct super_block * sb)98 u32 sysv_new_block(struct super_block * sb)
99 {
100 unsigned int block;
101 u32 nr;
102 struct buffer_head * bh;
103 unsigned count;
104
105 lock_super(sb);
106 count = fs16_to_cpu(sb, *sb->sv_bcache_count);
107
108 if (count == 0) /* Applies only to Coherent FS */
109 goto Enospc;
110 nr = sb->sv_bcache[--count];
111 if (nr == 0) /* Applies only to Xenix FS, SystemV FS */
112 goto Enospc;
113
114 block = fs32_to_cpu(sb, nr);
115
116 *sb->sv_bcache_count = cpu_to_fs16(sb, count);
117
118 if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
119 printk("sysv_new_block: new block %d is not in data zone\n",
120 block);
121 goto Enospc;
122 }
123
124 if (count == 0) { /* the last block continues the free list */
125 unsigned count;
126
127 block += sb->sv_block_base;
128 if (!(bh = sb_bread(sb, block))) {
129 printk("sysv_new_block: cannot read free-list block\n");
130 /* retry this same block next time */
131 *sb->sv_bcache_count = cpu_to_fs16(sb, 1);
132 goto Enospc;
133 }
134 count = fs16_to_cpu(sb, *(u16*)bh->b_data);
135 if (count > sb->sv_flc_size) {
136 printk("sysv_new_block: free-list block with >flc_size entries\n");
137 brelse(bh);
138 goto Enospc;
139 }
140 *sb->sv_bcache_count = cpu_to_fs16(sb, count);
141 memcpy(sb->sv_bcache, get_chunk(sb, bh),
142 count * sizeof(sysv_zone_t));
143 brelse(bh);
144 }
145 /* Now the free list head in the superblock is valid again. */
146 fs32_add(sb, sb->sv_free_blocks, -1);
147 dirty_sb(sb);
148 unlock_super(sb);
149 return nr;
150
151 Enospc:
152 unlock_super(sb);
153 return 0;
154 }
155
sysv_count_free_blocks(struct super_block * sb)156 unsigned long sysv_count_free_blocks(struct super_block * sb)
157 {
158 int sb_count;
159 int count;
160 struct buffer_head * bh = NULL;
161 u32 *blocks;
162 unsigned block;
163 int n;
164
165 /*
166 * This code does not work at all for AFS (it has a bitmap
167 * free list). As AFS is supposed to be read-only we just
168 * lie and say it has no free block at all.
169 */
170 if (sb->sv_type == FSTYPE_AFS)
171 return 0;
172
173 lock_super(sb);
174 sb_count = fs32_to_cpu(sb, *sb->sv_free_blocks);
175
176 if (0)
177 goto trust_sb;
178
179 /* this causes a lot of disk traffic ... */
180 count = 0;
181 n = fs16_to_cpu(sb, *sb->sv_bcache_count);
182 blocks = sb->sv_bcache;
183 while (1) {
184 if (n > sb->sv_flc_size)
185 goto E2big;
186 block = 0;
187 while (n && (block = blocks[--n]) != 0)
188 count++;
189 if (block == 0)
190 break;
191
192 block = fs32_to_cpu(sb, block);
193 if (bh)
194 brelse(bh);
195
196 if (block < sb->sv_firstdatazone || block >= sb->sv_nzones)
197 goto Einval;
198 block += sb->sv_block_base;
199 bh = sb_bread(sb, block);
200 if (!bh)
201 goto Eio;
202 n = fs16_to_cpu(sb, *(u16*)bh->b_data);
203 blocks = get_chunk(sb, bh);
204 }
205 if (bh)
206 brelse(bh);
207 if (count != sb_count)
208 goto Ecount;
209 done:
210 unlock_super(sb);
211 return count;
212
213 Einval:
214 printk("sysv_count_free_blocks: new block %d is not in data zone\n",
215 block);
216 goto trust_sb;
217 Eio:
218 printk("sysv_count_free_blocks: cannot read free-list block\n");
219 goto trust_sb;
220 E2big:
221 printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
222 if (bh)
223 brelse(bh);
224 trust_sb:
225 count = sb_count;
226 goto done;
227 Ecount:
228 printk("sysv_count_free_blocks: free block count was %d, "
229 "correcting to %d\n", sb_count, count);
230 if (!(sb->s_flags & MS_RDONLY)) {
231 *sb->sv_free_blocks = cpu_to_fs32(sb, count);
232 dirty_sb(sb);
233 }
234 goto done;
235 }
236