map.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:5k
源码类别:

Linux/Unix编程

开发平台:

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