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
30static 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
42void 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
98u32 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
151Enospc:
152	unlock_super(sb);
153	return 0;
154}
155
156unsigned 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;
209done:
210	unlock_super(sb);
211	return count;
212
213Einval:
214	printk("sysv_count_free_blocks: new block %d is not in data zone\n",
215		block);
216	goto trust_sb;
217Eio:
218	printk("sysv_count_free_blocks: cannot read free-list block\n");
219	goto trust_sb;
220E2big:
221	printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
222	if (bh)
223		brelse(bh);
224trust_sb:
225	count = sb_count;
226	goto done;
227Ecount:
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