4 * Copyright (C) 1997-1999 Russell King
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 #include <linux/version.h>
11 #include <linux/errno.h>
13 #include <linux/adfs_fs.h>
14 #include <linux/spinlock.h>
21 static rwlock_t adfs_map_lock;
23 #define GET_FRAG_ID(_map,_start,_idmask) \
25 unsigned long _v2, _frag; \
28 _frag = le32_to_cpu(_map[_tmp]); \
29 _v2 = le32_to_cpu(_map[_tmp + 1]); \
31 _frag = (_frag >> _tmp) | (_v2 << (32 - _tmp)); \
36 * return the map bit offset of the fragment frag_id in
38 * Note that the loop is optimised for best asm code -
39 * look at the output of:
40 * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
43 lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
44 const unsigned int frag_id, unsigned int *offset)
46 const unsigned int mapsize = dm->dm_endbit;
47 const unsigned int idmask = (1 << idlen) - 1;
48 unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
49 unsigned int start = dm->dm_startbit;
55 frag = GET_FRAG_ID(map, start, idmask);
56 mapptr = start + idlen;
59 * find end of fragment
64 while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
65 mapptr = (mapptr & ~31) + 32;
66 if (mapptr >= mapsize)
70 mapptr += 1 + ffz(~v2);
77 } while (mapptr < mapsize);
84 int length = mapptr - start;
85 if (*offset >= length) {
90 return start + *offset;
94 * Scan the free space map, for this zone, calculating the total
95 * number of map bits in each free space fragment.
97 * Note: idmask is limited to 15 bits [3.2]
100 scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
102 const unsigned int mapsize = dm->dm_endbit + 32;
103 const unsigned int idlen = asb->s_idlen;
104 const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
105 const unsigned int idmask = (1 << frag_idlen) - 1;
106 unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
107 unsigned int start = 8, mapptr;
109 unsigned long total = 0;
114 frag = GET_FRAG_ID(map, start, idmask);
117 * If the freelink is null, then no free fragments
118 * exist in this zone.
129 frag = GET_FRAG_ID(map, start, idmask);
130 mapptr = start + idlen;
133 * find end of fragment
138 while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
139 mapptr = (mapptr & ~31) + 32;
140 if (mapptr >= mapsize)
144 mapptr += 1 + ffz(~v2);
147 total += mapptr - start;
148 } while (frag >= idlen + 1);
151 printk(KERN_ERR "adfs: undersized free fragment\n");
155 printk(KERN_ERR "adfs: oversized free fragment\n");
160 scan_map(struct adfs_sb_info *asb, unsigned int zone,
161 const unsigned int frag_id, unsigned int mapoff)
163 const unsigned int idlen = asb->s_idlen;
164 struct adfs_discmap *dm, *dm_end;
167 dm = asb->s_map + zone;
168 zone = asb->s_map_size;
169 dm_end = asb->s_map + zone;
172 result = lookup_zone(dm, idlen, frag_id, &mapoff);
180 } while (--zone > 0);
184 result -= dm->dm_startbit;
185 result += dm->dm_startblk;
191 * calculate the amount of free blocks in the map.
194 * total_free = E(free_in_zone_n)
198 adfs_map_free(struct super_block *sb)
200 struct adfs_sb_info *asb = &sb->u.adfs_sb;
201 struct adfs_discmap *dm;
202 unsigned int total = 0;
206 zone = asb->s_map_size;
209 total += scan_free_map(asb, dm++);
210 } while (--zone > 0);
212 return signed_asl(total, asb->s_map2blk);
215 int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
217 struct adfs_sb_info *asb = &sb->u.adfs_sb;
218 unsigned int zone, mapoff;
222 * map & root fragment is special - it starts in the center of the
223 * disk. The other fragments start at zone (frag / ids_per_zone)
225 if (frag_id == ADFS_ROOT_FRAG)
226 zone = asb->s_map_size >> 1;
228 zone = frag_id / asb->s_ids_per_zone;
230 if (zone >= asb->s_map_size)
233 /* Convert sector offset to map offset */
234 mapoff = signed_asl(offset, -asb->s_map2blk);
236 read_lock(&adfs_map_lock);
237 result = scan_map(asb, zone, frag_id, mapoff);
238 read_unlock(&adfs_map_lock);
243 /* Calculate sector offset into map block */
244 secoff = offset - signed_asl(mapoff, asb->s_map2blk);
245 return secoff + signed_asl(result, asb->s_map2blk);
248 adfs_error(sb, "fragment %04X at offset %d not found in map",
253 adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
254 frag_id, zone, asb->s_map_size);