/*
块的分配和销毁
在ext2文件系统,块的管理是通过位图来实现的,一个文件系统包含若干个块组,
每个块组有一个针对数据块的位图,还有一个针对inode的位图,文件系统的组描述符
在超级块的后边,每一个组描述符都有空闲块的数目记录,组描述符都会在挂载文件系统
的时候读到内存里。ext2_fill_super
*/
/*宏判断b是不是在一个以first为头指针,长度为len的内存里*/
#define in_range(b,first,len) ((b)>=(first)&&(b)<=(first)+(len)-1)
/*
我们读出超级块是要缓存在内存中的,而内存中的超级块结构需要在磁盘超级块结构上添加一些管理成员。
ext2内存超级块结构为struct ext2_sb_info。
ext2_fill_super所展示的{BANNED}中国第一阶段代码所做工作主要有:
分配ext2内存超级块结构struct ext2_sb_info。
确定逻辑磁盘块大小
根据上面计算的块大小确定超级块所在的逻辑块号和块内偏移,从磁盘上读出超级块,sbi->s_es=es
读出块组描述符信息
*/
/*获得组描述符,block_group代表第几个组,bh参数如果不为空,就把buffer_head形式的组描述符放在bh里*/
struct ext2_group_desc *ext2_get_group_desc(struct super_block *sb,unsigned int block_group,struct buffer_head **bh)
{
unsigned long group_desc;
unsigned long offset;
struct ext2_group_desc *desc;
struct ext2_sb_info *sbi=EXT2_SB(sb);
/*如果参数大于组的数目,说明参数有问题,报错,返回NULL*/
if(block_group>=sbi->s_groups_count)
{
ext2_error(sb,"ext2_get_group_desc block_group=%d,groups_count=%lu",block_group,sbi->s_groups_count);
return NULL;
}
/*EXT2_DESC_PER_BLOCK_BITS宏返回块拥有组描述符的数目转换成二进制位的位数*/
group_desc=block_group>>EXT2_DESC_PER_BLOCK_BITS(sb);
/*这里的offset就是在块内的第几个描述符*/
offset=block_group&(EXT2_DESC_PER_BLOCK(sb)-1);
/*sbi->s_group_desc存放这块组描述符好几个块,如果为空,说明ext2出问题了,而且很严重*/
if(!sbi->s_group_desc[group_desc])
{
ext2_error(sb,"ext2_get_group_desc Group descripter not found block_group=%d",block_group);
return NULL;
}
/*描述符指针指向对应的buffer_head->b_data就是组描述符所在组的{BANNED}中国第一个组描述符*/
desc=(struct ext2_group_desc *)sbi->s_group_desc[group_desc]->b_data;
/*如果参数bh不为空,就赋值组描述符的buffer_head给bh*/
if(bh)
*bh=sbi->s_group_desc[group_desc];
/*返回想要的组描述符*/
return desc+offset;
}
/*
struct ext2_group_desc
{
__le32 bg_block_bitmap; //块位图所在的{BANNED}中国第一个块的块ID
__le32 bg_inode_bitmap; //inode位图所在的{BANNED}中国第一个块的块ID
__le32 bg_inode_table; //inode表所在的{BANNED}中国第一个块的块ID
__le16 bg_free_blocks_count; //块组中未使用的块数
__le16 bg_free_inodes_count; //块组中未使用的inode数
__le16 bg_used_dirs_count; //块组分配的目录的inode数
__le16 bg_pad;
__le32 bg_reserved[3];
};
引导块+块组0+块组1+块组2+...+块组n
块组0:超级块(1)+组描述符(n)+数据块位图(1)+索引节点位图(1)+索引节点表(n)+数据块(n)
超级块 struct ext2_super_block
组描述 struct ext2_group_desc
索引节点 struct ext2_inode
目录结构 struct ext2_dir_entry_2
*/
/*读给定的块组的数据块位图,成功就返回位图的buffer_head*/
static struct buffer_head *read_block_bitmap(struct super_block *sb,unsigned int block_group)
{
struct ext2_group_desc *desc;
struct buffer_head *bh=NULL;
desc=ext2_get_group_desc(sb,block_group,NULL);
if(!desc)
goto error_out;
/*调用底层驱动函数,读取数据块位图,desc->bg_block_bitmap代表着数据块位图的
块号码,返回数据块位图的buffer_head格式,sb_bread是块设备驱动和文件系统的连接函数*/
bh=sb_bread(sb,le32_to_cpu(desc->bg_block_bitmap));
/*没读出来,报错*/
if(!bh)
ext2_error(sb,"read_block_bitmap cannot read block bitmap");
error_out:
return bh;
}
/*把空闲的一部分放到保留块里,保留块是为了防止ext2出问题,一般是5%左右*/
static int reserve_blocks(struct super_block *sb,int count)
{
struct ext2_sb_info *sbi=EXT2_SB(sb);
struct ext2_super_block *es=sbi->s_es;
unsigned free_blocks;
unsigned root_blocks;
/*percpu_counter_read_positive函数是为了防止多处理器并发导致的读取失败*
free_blocks=percpu_counter_read_positve(&sbi->s_freeblocks_counter);
/*从ext2_super_block得到保留的数据块数目*/
root_blocks=le32_to_cpu(es->s_r_blocks_count);
/*如果count超出了空闲数目,就把count变成现有的空闲块数目*/
if(free_blocks
count=free_blocks;
/*如果空闲块小于count+保留块,没有权限*/
if(free_blocks
{
/*如果还有不是保留块的空闲块,就有多少转化多少,否则不转化了*/
if(free_blocks>root_blocks)
count=free_blocks-root_blocks;
else
return 0;
}
percpu_counter_mod(&sbi->s_freeblocks_counter,-count);
/*标记超级块已经脏了*/
sb->s_dirt=1;
/*返回转换的块数目*/
return count;
}
/*释放count数目的块,把这些块加入到空闲块里*/
static void release_blocks(struct super_block *sb,int count)
{
if(count)
{
/*空闲块的数目记录在ext2_sb_info里边*/
struct ext2_sb_info *sbi=EXT2_SB(sb);
/*sbi->s_freeblocks_counter+=count*/
percpu_counter_mod(&sbi->s_freeblocks_counter,count);
/*和硬盘上不一致了,需要设置脏*/
sb->s_dirt=1;
}
}
/*在块组的层次进行保留块数目的更改*/
static int group_reserve_blocks(struct ext2_sb_info *sbi,int group_no,struct ext2_group_desc * desc
,struct buffer_head *bh,int count)
{
unsigned free_blocks;
/*如果这个块组已经没有空闲块了*/
if(!desc->bg_free_blocks_count)
return 0;
/*防止并发操作的自旋锁,访问某一个组的时候必须是互斥的*/
spin_lock(sb_bgl_lock(sbi,group_no));
/*ext2_group_desc里的是小端字节序,转化成内存计算的字节序,得到组内空闲块的数目*/
free_blocks=le16_to_cpu(desc->bg_free_blocks_count);
/*如果空闲块的数目小于要求保留的,就设置保留的数目位空闲块的数目*/
if(free_blocks>count)
count=free_blocks;
/*修改空闲块数目,把count个块保留起来*/
desc->bg_free_blocks_count=cpu_to_le16(free_blocks-count);
spin_unlock(sb_bgl_lock(sbi,group_no));
/*这个bh已经脏了*/
mark_buffer_dirty(bh);
return count;
}
/*组内释放一些块,并把这些块加入到组内的空闲块内,基本上就是只修改组描述符*/
static void group_release_blocks(struct super_block *sb,int group_no,struct ext2_group_desc *desc,
struct buffer_head *bh,int count)
{
if(count)
{
/*通过super_block得到内存内统计ext2文件系统的ext2_sb_info结构体*/
struct ext2_sb_info *sbi=EXT2_SB(sb);
unsigned free_blocks;
/*确保对于某一个组的访问是互斥的*/
spin_lock(sb_bgl_lock(sbi,group_no));
/*这个组的空闲块的数目*/
free_blocks=le16_to_cpu(desc->bg_free_blocks_count);
/*组空闲块数目加上参数count*/
desc->bg_free_blocks_count=cpu_to_le16(free_blocks+count);
spin_unlock(sb_bgl_lock(sbi,group_no));
/*标记超级块为脏*/
sb->s_dirt=1;
/*由于统计空闲块的组描述符是在buffer_head上存放的,所以这个buffer_head也要标记为脏*/
mark_buffer_dirty(bh);
}
}
/*把从block号块开始的count个块释放掉,并且修改quota字段和i_blocks字段*/
void ext2_free_blocks(struct inode *inode,unsigned long block,unsigned long count)
{
struct buffer_head *bitmap_bh=NULL;
struct buffer_head *bh2;
unsigned long block_group;
unsigned long bit,i,overflow;
struct super_block *sb=inode->i_sb;
struct ext2_sb_info *sbi=EXT2_SB(sb);
struct ext2_group_desc *desc;
struct ext2_super_block *es=sbi->s_es;
unsigned freed=0,group_freed;
/*es->s_first_data_block代表{BANNED}中国第一个数据块在的块号,如果block小于它,说明传入的参数错了*/
if(blocks_first_data_block)||block+countle32_to_cpu(es->s_blocks_count))
{
ext2_error(sb,"ext2_free_blocks Freeing blocks not in datazone");
goto error_return;
}
do_more:
overflow=0;
/*传来的block减去es->s_first_data_block就得到了要删除的{BANNED}中国第一个块是文件系统的第几个块*/
block_group=(block-le32_to_cpu(es->s_first_data_block))/EXT2_BLOCKS_PER_GROUP(sb);
/*得到要删除的{BANNED}中国第一个块在组内的偏移,即这个组内的第几个块*/
bit=(block-le32_to_cpu(es->s_first_data_block))%EXT2_BLOCK_PER_GROUP(sb);
/*检验我们要删除的块是不是超过了组的边界*/
if(bit+count>EXT2_BLOCKS_PER_GROUP(sb))
{
/*overflow得到超出这个组的块的数目*/
overflow=bit+count-EXT2_BLOCK_PER_GROUP(sb);
/*然后count减去overflow*/
count-=overflow;
}
/*因为bitmap_bh是NULL,所以这里不用管这个释放函数*/
brelse(bitmap_bh);
/*得到组的块位图的buffer_head*/
bitmap_bh=read_block_bitmap(sb,block_group);
if(!bitmap_bh)
goto error_return;
/*得到组描述符*/
desc=ext2_get_group_desc(sb,block_group,&bh2);
if(!desc)
goto error_return;
/*如果组描述符的数据块位图和inode位图也在删除的范围之内*/
if(in_range(le32_to_cpu(desc->bg_block_bitmap),block,count)||
in_range(le32_to_cpu(desc->bg_inode_bitmap),block,count)||
/*如果inode表区也在要删除的范围之内*/
in_range(block,le32_to_cpu(desc->bg_inode_table),sbi->s_itb_per_group)||
in_range(block+count-1,le32_to_cpu(desc->bg_inode_table),sbi->s_itb_per_group))
/*删除的数据有关键性数据,不可删除,报错*/
ext2_error(sb,"ext2_free_blocks Freeing blocks in system zones" );
/*先把要删除的块对应的位图置位为0*/
for(i=0,group_freed=0;i
{
/*从数据位图上把对应的数据块的位置为0,如果已经是0,说明出错了*/
if(!ext2_clear_bit_atomic(sb_bgl_lock(sbi,block_group),bit+i,bitmap_bh->b_data))
{
ext2_error(sb,__FUNCTION__,"bit already cleared for block %lu",block+i);
}
else
{
/*group_freed标示组的空闲块数目,每删除一个块,空闲块就多一个*/
group_freed++;
}
}
/*位图的buffer_head已经和硬盘上的不一致了,置位脏*/
mark_buffer_dirty(bitmap_bh);
/*如果超级块的标记位上标记写过立刻同步,就同步buffer*/
if(sb->s_flags&MS_SYNCHRONOUS)
sync_dirty_buffer(bitmap_bh);
/*把释放过的块数目在块组描述符上记录*/
group_release_blocks(sb,block_group,desc,bh2,group_freed);
/*freed是统计一共释放的块数目,group_freed是这个组释放的数目*/
freed+=group_freed;
/*如果还有没删除完的,就是超出这个块组的,跳转到do_more继续*/
if(overflow)
{
/*代表着开始块的block+=count*/
block+=count;
count=overflow;
goto do_more;
}
error_return:
/*减少位图缓冲区的引用计数*/
brelse(bitmap_bh);
/*在超级块上增加freed个空闲块*/
release_blocks(sb,freed);
DQUOT_FREE_BLOCK(inode,freed);
}
/*在组内找到一个空闲块,并获取它,goal参数就是想得到的块在组内的偏移,map是块位图,size就是这个组的大小*/
static int grab_block(spinlock_t *lock,char *map,unsigned size,int goal)
{
int k;
char *p,*r;
/*首先测试位图的第goal位,如果是0的话,就说明得到了这个块,跳转*/
if(!ext2_test_bit(goal,map))
goto got_it;
repeat:
/*如果goal参数不为0,说明有设置希望得到的块在组内的偏移*/
if(goal)
{
k=(goal+63)&~63;
/*在map位图上边从goal开始寻找在k个的范围内另一个为0的位*/
goal=ext2_find_next_zero_bit(map,k,goal);
if(goal
goto got_it;
}
}
/*使用一个goal来帮助分配块,如果goal块是空闲的,或者是在goal的前后32个块之内有空闲块,
那个块就会被分配,否则的话就会向前寻找空闲的块,开始的时候都会寻找空闲的字节,一次性分配8个块,
如果失败的话,就直接寻找为0的位,不去寻找连续的8个空闲块*/
int ext2_new_block(struct inode *inode,unsigned long goal,u32 *prealloc_count,u32 *prealloc_block,int *err)
{
/*数据块位图的缓冲区*/
struct buffer_head *bitmap_bh=NULL;
/*块组缓冲区*/
struct buffer_head *gdp_bh;
struct ext2_group_desc *desc;
int group_no,ret_block,group_idx,target_block,block=0;
struct super_block *sb=inode->i_sb;
struct ext2_sb_info *sbi=EXT2_SB(sb);
struct ext2_super_block *es=sbi->s_es;
unsigned group_size=EXT2_BLOCKS_PER_GROUP(sb);
/*预分配的块数目*/
unsigned prealloc_goal=es->s_prealloc_blocks;
unsigned group_alloc=0,es_alloc,dq_alloc;
int nr_scanned_groups;
/*在ext2文件系统里,有一个叫做预分配制度的,就是之前分配多一点,可以减少磁盘读写,
先预分配块减一,如果预分配块没了,如果预分配块没了,就先设置prealloc_goal为7,
就是再重新分配8个块的意思*/
if(!prealloc_goal--)
prealloc_goal=EXT2_DEFAULT_PREALLOC_BLOCKS-1;
/*获取一定的保留块,如果为0说明没有空间了给保留块,预分配不行了*/
es_alloc=reserve_blocks(sb,dq_alloc);
if(!es_alloc)
{
*err=-ENOSPC;
goto out_dquot;
}
/*group_no就是要分配的块在的组号*/
group_no=(goal-le32_to_cpu(es->s_first_data_block))/group_size;
/*获得组描述符*/
desc=ext2_get_group_desc(sb,group_no,&gdp_bh);
if(!desc)
{
/*IO出错了*/
goto io_error;
}
/*在这个组内预分配块,group_alloc得到的就是可以预分配的块的数目*/
group_alloc=group_reserve_blocks(sbi,group_no,desc,gdp_bh,es_alloc);
if(group_alloc)
{
}
}
阅读(596) | 评论(0) | 转发(0) |