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