map.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:6k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  *  linux/fs/adfs/map.c
  3.  *
  4.  *  Copyright (C) 1997-1999 Russell King
  5.  *
  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.
  9.  */
  10. #include <linux/version.h>
  11. #include <linux/errno.h>
  12. #include <linux/fs.h>
  13. #include <linux/adfs_fs.h>
  14. #include <linux/spinlock.h>
  15. #include <asm/unaligned.h>
  16. #include "adfs.h"
  17. /*
  18.  * For the future...
  19.  */
  20. static rwlock_t adfs_map_lock = RW_LOCK_UNLOCKED;
  21. /*
  22.  * This is fun.  We need to load up to 19 bits from the map at an
  23.  * arbitary bit alignment.  (We're limited to 19 bits by F+ version
  24.  * 2).
  25.  */
  26. #define GET_FRAG_ID(_map,_start,_idmask)
  27. ({
  28. unsigned char *_m = _map + (_start >> 3);
  29. u32 _frag = get_unaligned((u32 *)_m);
  30. _frag >>= (_start & 7);
  31. _frag & _idmask;
  32. })
  33. /*
  34.  * return the map bit offset of the fragment frag_id in
  35.  * the zone dm.
  36.  * Note that the loop is optimised for best asm code -
  37.  * look at the output of:
  38.  *  gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
  39.  */
  40. static int
  41. lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
  42.     const unsigned int frag_id, unsigned int *offset)
  43. {
  44. const unsigned int mapsize = dm->dm_endbit;
  45. const u32 idmask = (1 << idlen) - 1;
  46. unsigned char *map = dm->dm_bh->b_data + 4;
  47. unsigned int start = dm->dm_startbit;
  48. unsigned int mapptr;
  49. u32 frag;
  50. do {
  51. frag = GET_FRAG_ID(map, start, idmask);
  52. mapptr = start + idlen;
  53. /*
  54.  * find end of fragment
  55.  */
  56. {
  57. u32 v, *_map = (u32 *)map;
  58. v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
  59. while (v == 0) {
  60. mapptr = (mapptr & ~31) + 32;
  61. if (mapptr >= mapsize)
  62. goto error;
  63. v = le32_to_cpu(_map[mapptr >> 5]);
  64. }
  65. mapptr += 1 + ffz(~v);
  66. }
  67. if (frag == frag_id)
  68. goto found;
  69. again:
  70. start = mapptr;
  71. } while (mapptr < mapsize);
  72. return -1;
  73. error:
  74. printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%xn",
  75. frag, start, mapptr);
  76. return -1;
  77. found:
  78. {
  79. int length = mapptr - start;
  80. if (*offset >= length) {
  81. *offset -= length;
  82. goto again;
  83. }
  84. }
  85. return start + *offset;
  86. }
  87. /*
  88.  * Scan the free space map, for this zone, calculating the total
  89.  * number of map bits in each free space fragment.
  90.  *
  91.  * Note: idmask is limited to 15 bits [3.2]
  92.  */
  93. static unsigned int
  94. scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
  95. {
  96. const unsigned int mapsize = dm->dm_endbit + 32;
  97. const unsigned int idlen  = asb->s_idlen;
  98. const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
  99. const u32 idmask = (1 << frag_idlen) - 1;
  100. unsigned char *map = dm->dm_bh->b_data;
  101. unsigned int start = 8, mapptr;
  102. u32 frag;
  103. unsigned long total = 0;
  104. /*
  105.  * get fragment id
  106.  */
  107. frag = GET_FRAG_ID(map, start, idmask);
  108. /*
  109.  * If the freelink is null, then no free fragments
  110.  * exist in this zone.
  111.  */
  112. if (frag == 0)
  113. return 0;
  114. do {
  115. start += frag;
  116. /*
  117.  * get fragment id
  118.  */
  119. frag = GET_FRAG_ID(map, start, idmask);
  120. mapptr = start + idlen;
  121. /*
  122.  * find end of fragment
  123.  */
  124. {
  125. u32 v, *_map = (u32 *)map;
  126. v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
  127. while (v == 0) {
  128. mapptr = (mapptr & ~31) + 32;
  129. if (mapptr >= mapsize)
  130. goto error;
  131. v = le32_to_cpu(_map[mapptr >> 5]);
  132. }
  133. mapptr += 1 + ffz(~v);
  134. }
  135. total += mapptr - start;
  136. } while (frag >= idlen + 1);
  137. if (frag != 0)
  138. printk(KERN_ERR "adfs: undersized free fragmentn");
  139. return total;
  140. error:
  141. printk(KERN_ERR "adfs: oversized free fragmentn");
  142. return 0;
  143. }
  144. static int
  145. scan_map(struct adfs_sb_info *asb, unsigned int zone,
  146.  const unsigned int frag_id, unsigned int mapoff)
  147. {
  148. const unsigned int idlen = asb->s_idlen;
  149. struct adfs_discmap *dm, *dm_end;
  150. int result;
  151. dm = asb->s_map + zone;
  152. zone = asb->s_map_size;
  153. dm_end = asb->s_map + zone;
  154. do {
  155. result = lookup_zone(dm, idlen, frag_id, &mapoff);
  156. if (result != -1)
  157. goto found;
  158. dm ++;
  159. if (dm == dm_end)
  160. dm = asb->s_map;
  161. } while (--zone > 0);
  162. return -1;
  163. found:
  164. result -= dm->dm_startbit;
  165. result += dm->dm_startblk;
  166. return result;
  167. }
  168. /*
  169.  * calculate the amount of free blocks in the map.
  170.  *
  171.  *              n=1
  172.  *  total_free = E(free_in_zone_n)
  173.  *              nzones
  174.  */
  175. unsigned int
  176. adfs_map_free(struct super_block *sb)
  177. {
  178. struct adfs_sb_info *asb = &sb->u.adfs_sb;
  179. struct adfs_discmap *dm;
  180. unsigned int total = 0;
  181. unsigned int zone;
  182. dm   = asb->s_map;
  183. zone = asb->s_map_size;
  184. do {
  185. total += scan_free_map(asb, dm++);
  186. } while (--zone > 0);
  187. return signed_asl(total, asb->s_map2blk);
  188. }
  189. int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
  190. {
  191. struct adfs_sb_info *asb = &sb->u.adfs_sb;
  192. unsigned int zone, mapoff;
  193. int result;
  194. /*
  195.  * map & root fragment is special - it starts in the center of the
  196.  * disk.  The other fragments start at zone (frag / ids_per_zone)
  197.  */
  198. if (frag_id == ADFS_ROOT_FRAG)
  199. zone = asb->s_map_size >> 1;
  200. else
  201. zone = frag_id / asb->s_ids_per_zone;
  202. if (zone >= asb->s_map_size)
  203. goto bad_fragment;
  204. /* Convert sector offset to map offset */
  205. mapoff = signed_asl(offset, -asb->s_map2blk);
  206. read_lock(&adfs_map_lock);
  207. result = scan_map(asb, zone, frag_id, mapoff);
  208. read_unlock(&adfs_map_lock);
  209. if (result > 0) {
  210. unsigned int secoff;
  211. /* Calculate sector offset into map block */
  212. secoff = offset - signed_asl(mapoff, asb->s_map2blk);
  213. return secoff + signed_asl(result, asb->s_map2blk);
  214. }
  215. adfs_error(sb, "fragment %04X at offset %d not found in map",
  216.    frag_id, offset);
  217. return 0;
  218. bad_fragment:
  219. adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
  220.    frag_id, zone, asb->s_map_size);
  221. return 0;
  222. }