162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/fs/hfs/bitmap.c
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 1996-1997  Paul H. Hargrove
562306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com>
662306a36Sopenharmony_ci * This file may be distributed under the terms of the GNU General Public License.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on GPLed code Copyright (C) 1995  Michael Dreher
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This file contains the code to modify the volume bitmap:
1162306a36Sopenharmony_ci * search/set/clear bits.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "hfs_fs.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * hfs_find_zero_bit()
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Description:
2062306a36Sopenharmony_ci *  Given a block of memory, its length in bits, and a starting bit number,
2162306a36Sopenharmony_ci *  determine the number of the first zero bits (in left-to-right ordering)
2262306a36Sopenharmony_ci *  in that range.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *  Returns >= 'size' if no zero bits are found in the range.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *  Accesses memory in 32-bit aligned chunks of 32-bits and thus
2762306a36Sopenharmony_ci *  may read beyond the 'size'th bit.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic u32 hfs_find_set_zero_bits(__be32 *bitmap, u32 size, u32 offset, u32 *max)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	__be32 *curr, *end;
3262306a36Sopenharmony_ci	u32 mask, start, len, n;
3362306a36Sopenharmony_ci	__be32 val;
3462306a36Sopenharmony_ci	int i;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	len = *max;
3762306a36Sopenharmony_ci	if (!len)
3862306a36Sopenharmony_ci		return size;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	curr = bitmap + (offset / 32);
4162306a36Sopenharmony_ci	end = bitmap + ((size + 31) / 32);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* scan the first partial u32 for zero bits */
4462306a36Sopenharmony_ci	val = *curr;
4562306a36Sopenharmony_ci	if (~val) {
4662306a36Sopenharmony_ci		n = be32_to_cpu(val);
4762306a36Sopenharmony_ci		i = offset % 32;
4862306a36Sopenharmony_ci		mask = (1U << 31) >> i;
4962306a36Sopenharmony_ci		for (; i < 32; mask >>= 1, i++) {
5062306a36Sopenharmony_ci			if (!(n & mask))
5162306a36Sopenharmony_ci				goto found;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* scan complete u32s for the first zero bit */
5662306a36Sopenharmony_ci	while (++curr < end) {
5762306a36Sopenharmony_ci		val = *curr;
5862306a36Sopenharmony_ci		if (~val) {
5962306a36Sopenharmony_ci			n = be32_to_cpu(val);
6062306a36Sopenharmony_ci			mask = 1 << 31;
6162306a36Sopenharmony_ci			for (i = 0; i < 32; mask >>= 1, i++) {
6262306a36Sopenharmony_ci				if (!(n & mask))
6362306a36Sopenharmony_ci					goto found;
6462306a36Sopenharmony_ci			}
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci	return size;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cifound:
7062306a36Sopenharmony_ci	start = (curr - bitmap) * 32 + i;
7162306a36Sopenharmony_ci	if (start >= size)
7262306a36Sopenharmony_ci		return start;
7362306a36Sopenharmony_ci	/* do any partial u32 at the start */
7462306a36Sopenharmony_ci	len = min(size - start, len);
7562306a36Sopenharmony_ci	while (1) {
7662306a36Sopenharmony_ci		n |= mask;
7762306a36Sopenharmony_ci		if (++i >= 32)
7862306a36Sopenharmony_ci			break;
7962306a36Sopenharmony_ci		mask >>= 1;
8062306a36Sopenharmony_ci		if (!--len || n & mask)
8162306a36Sopenharmony_ci			goto done;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci	if (!--len)
8462306a36Sopenharmony_ci		goto done;
8562306a36Sopenharmony_ci	*curr++ = cpu_to_be32(n);
8662306a36Sopenharmony_ci	/* do full u32s */
8762306a36Sopenharmony_ci	while (1) {
8862306a36Sopenharmony_ci		n = be32_to_cpu(*curr);
8962306a36Sopenharmony_ci		if (len < 32)
9062306a36Sopenharmony_ci			break;
9162306a36Sopenharmony_ci		if (n) {
9262306a36Sopenharmony_ci			len = 32;
9362306a36Sopenharmony_ci			break;
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci		*curr++ = cpu_to_be32(0xffffffff);
9662306a36Sopenharmony_ci		len -= 32;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci	/* do any partial u32 at end */
9962306a36Sopenharmony_ci	mask = 1U << 31;
10062306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
10162306a36Sopenharmony_ci		if (n & mask)
10262306a36Sopenharmony_ci			break;
10362306a36Sopenharmony_ci		n |= mask;
10462306a36Sopenharmony_ci		mask >>= 1;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_cidone:
10762306a36Sopenharmony_ci	*curr = cpu_to_be32(n);
10862306a36Sopenharmony_ci	*max = (curr - bitmap) * 32 + i - start;
10962306a36Sopenharmony_ci	return start;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * hfs_vbm_search_free()
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci * Description:
11662306a36Sopenharmony_ci *   Search for 'num_bits' consecutive cleared bits in the bitmap blocks of
11762306a36Sopenharmony_ci *   the hfs MDB. 'mdb' had better be locked or the returned range
11862306a36Sopenharmony_ci *   may be no longer free, when this functions returns!
11962306a36Sopenharmony_ci *   XXX Currently the search starts from bit 0, but it should start with
12062306a36Sopenharmony_ci *   the bit number stored in 's_alloc_ptr' of the MDB.
12162306a36Sopenharmony_ci * Input Variable(s):
12262306a36Sopenharmony_ci *   struct hfs_mdb *mdb: Pointer to the hfs MDB
12362306a36Sopenharmony_ci *   u16 *num_bits: Pointer to the number of cleared bits
12462306a36Sopenharmony_ci *     to search for
12562306a36Sopenharmony_ci * Output Variable(s):
12662306a36Sopenharmony_ci *   u16 *num_bits: The number of consecutive clear bits of the
12762306a36Sopenharmony_ci *     returned range. If the bitmap is fragmented, this will be less than
12862306a36Sopenharmony_ci *     requested and it will be zero, when the disk is full.
12962306a36Sopenharmony_ci * Returns:
13062306a36Sopenharmony_ci *   The number of the first bit of the range of cleared bits which has been
13162306a36Sopenharmony_ci *   found. When 'num_bits' is zero, this is invalid!
13262306a36Sopenharmony_ci * Preconditions:
13362306a36Sopenharmony_ci *   'mdb' points to a "valid" (struct hfs_mdb).
13462306a36Sopenharmony_ci *   'num_bits' points to a variable of type (u16), which contains
13562306a36Sopenharmony_ci *	the number of cleared bits to find.
13662306a36Sopenharmony_ci * Postconditions:
13762306a36Sopenharmony_ci *   'num_bits' is set to the length of the found sequence.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ciu32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	void *bitmap;
14262306a36Sopenharmony_ci	u32 pos;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* make sure we have actual work to perform */
14562306a36Sopenharmony_ci	if (!*num_bits)
14662306a36Sopenharmony_ci		return 0;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	mutex_lock(&HFS_SB(sb)->bitmap_lock);
14962306a36Sopenharmony_ci	bitmap = HFS_SB(sb)->bitmap;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	pos = hfs_find_set_zero_bits(bitmap, HFS_SB(sb)->fs_ablocks, goal, num_bits);
15262306a36Sopenharmony_ci	if (pos >= HFS_SB(sb)->fs_ablocks) {
15362306a36Sopenharmony_ci		if (goal)
15462306a36Sopenharmony_ci			pos = hfs_find_set_zero_bits(bitmap, goal, 0, num_bits);
15562306a36Sopenharmony_ci		if (pos >= HFS_SB(sb)->fs_ablocks) {
15662306a36Sopenharmony_ci			*num_bits = pos = 0;
15762306a36Sopenharmony_ci			goto out;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	hfs_dbg(BITMAP, "alloc_bits: %u,%u\n", pos, *num_bits);
16262306a36Sopenharmony_ci	HFS_SB(sb)->free_ablocks -= *num_bits;
16362306a36Sopenharmony_ci	hfs_bitmap_dirty(sb);
16462306a36Sopenharmony_ciout:
16562306a36Sopenharmony_ci	mutex_unlock(&HFS_SB(sb)->bitmap_lock);
16662306a36Sopenharmony_ci	return pos;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/*
17162306a36Sopenharmony_ci * hfs_clear_vbm_bits()
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci * Description:
17462306a36Sopenharmony_ci *   Clear the requested bits in the volume bitmap of the hfs filesystem
17562306a36Sopenharmony_ci * Input Variable(s):
17662306a36Sopenharmony_ci *   struct hfs_mdb *mdb: Pointer to the hfs MDB
17762306a36Sopenharmony_ci *   u16 start: The offset of the first bit
17862306a36Sopenharmony_ci *   u16 count: The number of bits
17962306a36Sopenharmony_ci * Output Variable(s):
18062306a36Sopenharmony_ci *   None
18162306a36Sopenharmony_ci * Returns:
18262306a36Sopenharmony_ci *    0: no error
18362306a36Sopenharmony_ci *   -1: One of the bits was already clear.  This is a strange
18462306a36Sopenharmony_ci *	 error and when it happens, the filesystem must be repaired!
18562306a36Sopenharmony_ci *   -2: One or more of the bits are out of range of the bitmap.
18662306a36Sopenharmony_ci * Preconditions:
18762306a36Sopenharmony_ci *   'mdb' points to a "valid" (struct hfs_mdb).
18862306a36Sopenharmony_ci * Postconditions:
18962306a36Sopenharmony_ci *   Starting with bit number 'start', 'count' bits in the volume bitmap
19062306a36Sopenharmony_ci *   are cleared. The affected bitmap blocks are marked "dirty", the free
19162306a36Sopenharmony_ci *   block count of the MDB is updated and the MDB is marked dirty.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_ciint hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	__be32 *curr;
19662306a36Sopenharmony_ci	u32 mask;
19762306a36Sopenharmony_ci	int i, len;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* is there any actual work to be done? */
20062306a36Sopenharmony_ci	if (!count)
20162306a36Sopenharmony_ci		return 0;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	hfs_dbg(BITMAP, "clear_bits: %u,%u\n", start, count);
20462306a36Sopenharmony_ci	/* are all of the bits in range? */
20562306a36Sopenharmony_ci	if ((start + count) > HFS_SB(sb)->fs_ablocks)
20662306a36Sopenharmony_ci		return -2;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	mutex_lock(&HFS_SB(sb)->bitmap_lock);
20962306a36Sopenharmony_ci	/* bitmap is always on a 32-bit boundary */
21062306a36Sopenharmony_ci	curr = HFS_SB(sb)->bitmap + (start / 32);
21162306a36Sopenharmony_ci	len = count;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* do any partial u32 at the start */
21462306a36Sopenharmony_ci	i = start % 32;
21562306a36Sopenharmony_ci	if (i) {
21662306a36Sopenharmony_ci		int j = 32 - i;
21762306a36Sopenharmony_ci		mask = 0xffffffffU << j;
21862306a36Sopenharmony_ci		if (j > count) {
21962306a36Sopenharmony_ci			mask |= 0xffffffffU >> (i + count);
22062306a36Sopenharmony_ci			*curr &= cpu_to_be32(mask);
22162306a36Sopenharmony_ci			goto out;
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci		*curr++ &= cpu_to_be32(mask);
22462306a36Sopenharmony_ci		count -= j;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* do full u32s */
22862306a36Sopenharmony_ci	while (count >= 32) {
22962306a36Sopenharmony_ci		*curr++ = 0;
23062306a36Sopenharmony_ci		count -= 32;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci	/* do any partial u32 at end */
23362306a36Sopenharmony_ci	if (count) {
23462306a36Sopenharmony_ci		mask = 0xffffffffU >> count;
23562306a36Sopenharmony_ci		*curr &= cpu_to_be32(mask);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ciout:
23862306a36Sopenharmony_ci	HFS_SB(sb)->free_ablocks += len;
23962306a36Sopenharmony_ci	mutex_unlock(&HFS_SB(sb)->bitmap_lock);
24062306a36Sopenharmony_ci	hfs_bitmap_dirty(sb);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return 0;
24362306a36Sopenharmony_ci}
244