前面博客中我们描述了如何为文件分配一个预留窗口,而且我们知道,该预留窗口内的磁盘块有可能已经被占用了,而我们的最终目的是分配空闲磁盘块,而非预留窗口,所以我们这里描述的就是如何分配空闲磁盘块。
这里我们分配磁盘块有两种途径:第一是通过预留窗口,即在某个预留窗口内分配磁盘块;第二是普通分配方式,这个只是限制在特定块组内分配即可,没有第一种的情况要求那么苛刻。至于使用哪种分配方式,是由上层调用者来决定的。
对这个函数我们还必须明确一点:1. 分配的空闲磁盘块一定连续;2. 不一定能分配得到想要数量的磁盘块。
static int
ext2_try_to_allocate(struct super_block *sb, int group,
struct buffer_head *bitmap_bh, ext2_grpblk_t grp_goal,
unsigned long *count,
struct ext2_reserve_window *my_rsv)
{
ext2_fsblk_t group_first_block;
ext2_grpblk_t start, end;
unsigned long num = 0;
/* we do allocation within the reservation window if we have a window */
/* here what we should decide is the range(start ~ end) between which we allocate free block(s)
*/
/*
** 如果使用预留窗口,我们必须
** 保证分配的范围落在预留窗口
** 并且,要落在goal所在的块组中
*/
if (my_rsv) {
//好像这里面的设置有点儿复杂
group_first_block = ext2_group_first_block_no(sb, group);
if (my_rsv->_rsv_start >= group_first_block)
start = my_rsv->_rsv_start - group_first_block;
else
/* reservation window cross group boundary */
start = 0;
end = my_rsv->_rsv_end - group_first_block + 1;
if (end > EXT2_BLOCKS_PER_GROUP(sb))
/* reservation window crosses group boundary */
end = EXT2_BLOCKS_PER_GROUP(sb);
if ((start <= grp_goal) && (grp_goal < end))
start = grp_goal;
else
grp_goal = -1;
}
//如果不使用预留窗口,那么
//就直接从goal处开始分配好了
else {
if (grp_goal > 0)
start = grp_goal;
else
start = 0;
end = EXT2_BLOCKS_PER_GROUP(sb);
}
BUG_ON(start > EXT2_BLOCKS_PER_GROUP(sb));
repeat:
//如果grp_goal < 0 表示什么呢
if (grp_goal < 0) {
//找到grp_goal之后的第一个空闲磁盘块
//当然查找是有个范围的
grp_goal = find_next_usable_block(start, bitmap_bh, end);
if (grp_goal < 0)
goto fail_access;
if (!my_rsv) {
int i;
for (i = 0; i < 7 && grp_goal > start &&
!ext2_test_bit(grp_goal - 1,
bitmap_bh->b_data);
i++, grp_goal--)
;
}
}
start = grp_goal;
//如果当前的goal 被占用了该函数会执行失败
// 继续从下一个块开始,即转到repeat开始重试
if (ext2_set_bit_atomic(sb_bgl_lock(EXT2_SB(sb), group), grp_goal,
bitmap_bh->b_data)) {
/*
* The block was allocated by another thread, or it was
* allocated and then freed by another thread
*/
start++;
grp_goal++;
if (start >= end)
goto fail_access;
goto repeat;
}
/* 如果之前的goal是空闲的
** 那么再继续从下一个搜索
** 看看下一个连续的块是否也是空闲的
** 如果有,当然最好,我们可以多分配点
**
*/
num++;
grp_goal++;
while (num < *count && grp_goal < end
&& !ext2_set_bit_atomic(sb_bgl_lock(EXT2_SB(sb), group),
grp_goal, bitmap_bh->b_data)) {
num++;
grp_goal++;
}
*count = num;
return grp_goal - num;
fail_access:
*count = num;
return -1;
}
我们要明确该函数参数的意义:
- group:代表当前要分配的磁盘块所在的块组,无论我们使用的预留窗口有多大,我们最终的分配都必须限制在该块组之内;
- bitmap_bh:块组的位图信息,用于寻找空闲磁盘块;
- grp_goal:上层建议的从哪开始搜索分配,以达到最佳文件连续性;
- *count:上层传入的要分配磁盘块数量,同时这个也作为返回值告知调用者实际分配的磁盘块数量;
- my_rsv:是否使用预留窗口,如果其不为NULL,则在该预留窗口中进行分配。
首先,我们得判断本次分配是否使用预留窗口,如果使用,那么我们必须得计算好我们从哪开始分配,到哪里为止。至于其需要处理的原因在于预留窗口可能是跨块组的,所以我们必须得小心处理这种情况,如果真的跨块组,我们必须得截断,分配必须得在这个块组中进行。而且,我们得根据goal计算出其在块组内的偏移,即grp_goal,因为查找位图的时候是根据块组内偏移来查的。如果不适用预留窗口,那么就简单多了,如果设置了goal,直接从这里开始查找空闲块,否则从块组开始处查找。
完成了上面查找的设置,接下来我们就是搜索位图开始查找空闲磁盘块了。我们从grp_goal这个位置开始判断:如果这个块被占用了,那么我们得从后面一个块开始重试,所以有一个repeat;如果这个块没被占用,那我们就继续往后找,看看后面的块是否还空闲,如果是,那连这个块一并分配。一直找到不是空闲磁盘块为止,我们的策略是尽可能分配多的连续块。
最后,*count中保存了本次分配到的连续空闲磁盘块数量,而返回值则是分配的起始磁盘块号。