diff options
| author | Russell King <rmk@flint.arm.linux.org.uk> | 2002-05-13 02:14:57 +0100 |
|---|---|---|
| committer | Russell King <rmk@flint.arm.linux.org.uk> | 2002-05-13 02:14:57 +0100 |
| commit | b918ced48922920d562ff17935bc1363ca020035 (patch) | |
| tree | ff88df9f8b3d6ab4341462c55cc18c836b06fe4c | |
| parent | 34dc307ad5fd2af8ee41469f587c34885d554c83 (diff) | |
[ARM] ADFS updates/fixes.
Fixes lockup on SMP boxes, and fixes buggy map scanning code.
| -rw-r--r-- | fs/adfs/adfs.h | 2 | ||||
| -rw-r--r-- | fs/adfs/dir.c | 2 | ||||
| -rw-r--r-- | fs/adfs/map.c | 100 | ||||
| -rw-r--r-- | fs/adfs/super.c | 34 |
4 files changed, 73 insertions, 65 deletions
diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 720b707a34e0..1d6792fbcb89 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -77,7 +77,7 @@ void adfs_write_inode(struct inode *inode,int unused); int adfs_notify_change(struct dentry *dentry, struct iattr *attr); /* map.c */ -extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset); +extern int adfs_map_lookup(struct super_block *sb, unsigned int frag_id, unsigned int offset); extern unsigned int adfs_map_free(struct super_block *sb); /* Misc */ diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index e50456ba1e69..fe1b273ef8a5 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -24,7 +24,7 @@ /* * For future. This should probably be per-directory. */ -static rwlock_t adfs_dir_lock; +static rwlock_t adfs_dir_lock = RW_LOCK_UNLOCKED; static int adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) diff --git a/fs/adfs/map.c b/fs/adfs/map.c index ce36fcd25124..666a2678edde 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -1,7 +1,7 @@ /* * linux/fs/adfs/map.c * - * Copyright (C) 1997-1999 Russell King + * Copyright (C) 1997-2002 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,30 +13,64 @@ #include <linux/adfs_fs.h> #include <linux/spinlock.h> +#include <asm/unaligned.h> + #include "adfs.h" /* + * The ADFS map is basically a set of sectors. Each sector is called a + * zone which contains a bitstream made up of variable sized fragments. + * Each bit refers to a set of bytes in the filesystem, defined by + * log2bpmb. This may be larger or smaller than the sector size, but + * the overall size it describes will always be a round number of + * sectors. A fragment id is always idlen bits long. + * + * < idlen > < n > <1> + * +---------+-------//---------+---+ + * | frag id | 0000....000000 | 1 | + * +---------+-------//---------+---+ + * + * The physical disk space used by a fragment is taken from the start of + * the fragment id up to and including the '1' bit - ie, idlen + n + 1 + * bits. + * + * A fragment id can be repeated multiple times in the whole map for + * large or fragmented files. The first map zone a fragment starts in + * is given by fragment id / ids_per_zone - this allows objects to start + * from any zone on the disk. + * + * Free space is described by a linked list of fragments. Each free + * fragment describes free space in the same way as the other fragments, + * however, the frag id specifies an offset (in map bits) from the end + * of this fragment to the start of the next free fragment. + * + * Objects stored on the disk are allocated object ids (we use these as + * our inode numbers.) Object ids contain a fragment id and an optional + * offset. This allows a directory fragment to contain small files + * associated with that directory. + */ + +/* * For the future... */ -static rwlock_t adfs_map_lock; +static rwlock_t adfs_map_lock = RW_LOCK_UNLOCKED; +/* + * This is fun. We need to load up to 19 bits from the map at an + * arbitary bit alignment. (We're limited to 19 bits by F+ version 2). + */ #define GET_FRAG_ID(_map,_start,_idmask) \ ({ \ - unsigned long _v2, _frag; \ - unsigned int _tmp; \ - _tmp = _start >> 5; \ - _frag = le32_to_cpu(_map[_tmp]); \ - _v2 = le32_to_cpu(_map[_tmp + 1]); \ - _tmp = start & 31; \ - _frag = (_frag >> _tmp) | (_v2 << (32 - _tmp)); \ + unsigned char *_m = _map + (_start >> 3); \ + u32 _frag = get_unaligned((u32 *)_m); \ + _frag >>= (_start & 7); \ _frag & _idmask; \ }) /* - * return the map bit offset of the fragment frag_id in - * the zone dm. - * Note that the loop is optimised for best asm code - - * look at the output of: + * return the map bit offset of the fragment frag_id in the zone dm. + * Note that the loop is optimised for best asm code - look at the + * output of: * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c */ static int @@ -44,14 +78,13 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, const unsigned int frag_id, unsigned int *offset) { const unsigned int mapsize = dm->dm_endbit; - const unsigned int idmask = (1 << idlen) - 1; - unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1; + const u32 idmask = (1 << idlen) - 1; + unsigned char *map = dm->dm_bh->b_data + 4; unsigned int start = dm->dm_startbit; unsigned int mapptr; + u32 frag; do { - unsigned long frag; - frag = GET_FRAG_ID(map, start, idmask); mapptr = start + idlen; @@ -59,15 +92,17 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, * find end of fragment */ { - unsigned long v2; + u32 v, *_map = (u32 *)map; - while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) { + v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31); + while (v == 0) { mapptr = (mapptr & ~31) + 32; if (mapptr >= mapsize) goto error; + v = le32_to_cpu(_map[mapptr >> 5]); } - mapptr += 1 + ffz(~v2); + mapptr += 1 + ffz(~v); } if (frag == frag_id) @@ -75,8 +110,11 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen, again: start = mapptr; } while (mapptr < mapsize); + return -1; error: + printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n", + frag, start, mapptr); return -1; found: @@ -102,10 +140,10 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) const unsigned int mapsize = dm->dm_endbit + 32; const unsigned int idlen = asb->s_idlen; const unsigned int frag_idlen = idlen <= 15 ? idlen : 15; - const unsigned int idmask = (1 << frag_idlen) - 1; - unsigned long *map = (unsigned long *)dm->dm_bh->b_data; + const u32 idmask = (1 << frag_idlen) - 1; + unsigned char *map = dm->dm_bh->b_data; unsigned int start = 8, mapptr; - unsigned long frag; + u32 frag; unsigned long total = 0; /* @@ -133,15 +171,17 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm) * find end of fragment */ { - unsigned long v2; + u32 v, *_map = (u32 *)map; - while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) { + v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31); + while (v == 0) { mapptr = (mapptr & ~31) + 32; if (mapptr >= mapsize) goto error; + v = le32_to_cpu(_map[mapptr >> 5]); } - mapptr += 1 + ffz(~v2); + mapptr += 1 + ffz(~v); } total += mapptr - start; @@ -212,7 +252,9 @@ adfs_map_free(struct super_block *sb) return signed_asl(total, asb->s_map2blk); } -int adfs_map_lookup (struct super_block *sb, int frag_id, int offset) +int +adfs_map_lookup(struct super_block *sb, unsigned int frag_id, + unsigned int offset) { struct adfs_sb_info *asb = &sb->u.adfs_sb; unsigned int zone, mapoff; @@ -245,12 +287,12 @@ int adfs_map_lookup (struct super_block *sb, int frag_id, int offset) return secoff + signed_asl(result, asb->s_map2blk); } - adfs_error(sb, "fragment %04X at offset %d not found in map", + adfs_error(sb, "fragment 0x%04x at offset %d not found in map", frag_id, offset); return 0; bad_fragment: - adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)", + adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)", frag_id, zone, asb->s_map_size); return 0; } diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 1164ce6d11d0..55309e14720c 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -64,43 +64,9 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr) if (dr->disc_size_high >> dr->log2secsize) return 1; - /* - * The following checks are not required for F+ - * stage 1. - */ -#if 0 - /* idlen must be smaller be no greater than 15 */ - if (dr->idlen > 15) - return 1; - - /* nzones must be less than 128 for the root - * directory to be addressable - */ - if (dr->nzones >= 128 && dr->nzones_high == 0) - return 1; - - /* root must be of the form 0x2.. */ - if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200) - return 1; -#else - /* - * Stage 2 F+ does not require the following check - */ -#if 0 - /* idlen must be no greater than 16 v2 [1.0] */ - if (dr->idlen > 16) - return 1; - - /* we can't handle F+ discs yet */ - if (dr->format_version || dr->root_size) - return 1; - -#else /* idlen must be no greater than 19 v2 [1.0] */ if (dr->idlen > 19) return 1; -#endif -#endif /* reserved bytes should be zero */ for (i = 0; i < sizeof(dr->unused52); i++) |
