ext2_try_to_allocate_with_rsv解析

        之前分析的ext2_new_blocks()看似复杂,但其基本思想却比较简单,而且,它把最复杂的功能留给了本博客中需要讨论的ext2_try_to_allocate_with_rsv()函数中完成了。

看函数的名字便知道,如果上层调用者决定使用预留窗口机制,则该函数优先从文件的预留窗口中分配磁盘块,并且根据需要可能分配一个全新的预留窗口,或者有必要将原来

的预留窗口进行扩充。废话不多说,让我们先来看看代码吧:

static ext2_grpblk_t
ext2_try_to_allocate_with_rsv(struct super_block *sb, unsigned int group,
			struct buffer_head *bitmap_bh, ext2_grpblk_t grp_goal,
			struct ext2_reserve_window_node * my_rsv,
			unsigned long *count)
{
	ext2_fsblk_t group_first_block, group_last_block;
	ext2_grpblk_t ret = 0;
	unsigned long num = *count;

	/*
	 * we don't deal with reservation when
	 * filesystem is mounted without reservation
	 * or the file is not a regular file
	 * or last attempt to allocate a block with reservation turned on failed
	 */
	/* 如果不使用预留窗口机制,那么就使用
	** 传统的分配方式了
	*/
	if (my_rsv == NULL) {
		return ext2_try_to_allocate(sb, group, bitmap_bh,
						grp_goal, count, NULL);
	}
	/*
	 * grp_goal is a group relative block number (if there is a goal)
	 * 0 <= grp_goal < EXT2_BLOCKS_PER_GROUP(sb)
	 * first block is a filesystem wide block number
	 * first block is the block number of the first block in this group
	 */
	group_first_block = ext2_group_first_block_no(sb, group);
	group_last_block = group_first_block + (EXT2_BLOCKS_PER_GROUP(sb) - 1);

	/*
	 * Basically we will allocate a new block from inode's reservation
	 * window.
	 *
	 * We need to allocate a new reservation window, if:
	 * a) inode does not have a reservation window; or
	 * b) last attempt to allocate a block from existing reservation
	 *    failed; or
	 * c) we come here with a goal and with a reservation window
	 *
	 * We do not need to allocate a new reservation window if we come here
	 * at the beginning with a goal and the goal is inside the window, or
	 * we don't have a goal but already have a reservation window.
	 * then we could go to allocate from the reservation window directly.
	 */
	 /*
	  * 1. allocate a new reservation window or expand old reservation window if necessary;
	  * 2. allocate free block(s) from reservation window by calling ext2_try_to_allocate().
	  */
	/* 如果有以下情况发生,那么就分配一个全新的预留窗口
	** 1. 当前预留窗口为空,即还未分配物理磁盘块
	** 2. 如果建议块号不在预留窗口之内
	*/
	while (1) {
		if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) ||
			!goal_in_my_reservation(&my_rsv->rsv_window,
						grp_goal, group, sb)) {
			if (my_rsv->rsv_goal_size < *count)
				my_rsv->rsv_goal_size = *count;
			ret = alloc_new_reservation(my_rsv, grp_goal, sb,
							group, bitmap_bh);
			if (ret < 0)
				break;			/* failed */
			//we allocate new reservation window but the goal block is still out of this window
			// if so, we set the target block to -1 
			if (!goal_in_my_reservation(&my_rsv->rsv_window,
							grp_goal, group, sb))
				grp_goal = -1;
		/* 这里的else if 意味着:
		** 1. 预留窗口非空;
		** 2. ret = 0;
		** 3. goal 在预留窗口之中
		** 那么磁盘需要判断是否需要扩张预留窗口
		** 判断的条件: 从当前要分配的位置开始直至
		** 预留窗口结束,中间这么多的数据块是否
		** 少于我们需要分配的数量(*count)
		** -----------------------------------------
		**|                     |    |                                      |
		**------------------------------------------
		**start                 goal                                       end
		** goal:我要从这儿开始分配
		** end: 预留窗口结束的位置
		** curr = end - goal + 1: 从想要分配的地方到预留窗口
		** 结束的位置,我可以分配多少个空闲块
		** 如果curr 小于想要的数量,那么需要扩充该预留窗口了
		*/	
		} else if (grp_goal >= 0) {
			/*expand old reservation window
			**what is @current? current =? my_rsv->rsv_end - my_rsv->start + 1
			*/
			int curr = my_rsv->rsv_end -
					(grp_goal + group_first_block) + 1;

			if (curr < *count)
				try_to_extend_reservation(my_rsv, sb,
							*count - curr);
		}
		//if the reservation window is out of range
		if ((my_rsv->rsv_start > group_last_block) ||
				(my_rsv->rsv_end < group_first_block)) {
			rsv_window_dump(&EXT2_SB(sb)->s_rsv_window_root, 1);
			BUG();
		}
		/*allocate free block from reservation window
		*we get @num free blocks this time
		*/
		ret = ext2_try_to_allocate(sb, group, bitmap_bh, grp_goal,
					   &num, &my_rsv->rsv_window);
		if (ret >= 0) {
			my_rsv->rsv_alloc_hit += num;
			*count = num;
			break;				/* succeed */
		}
		num = *count;
	}
	return ret;
}
        首先,我们再次确认下该函数的功能:从预留窗口或者块组中分配若干数量的磁盘块,这里不保证能分配到想要数量的磁盘块(尽力而为而已),但能够保证分配的磁盘块一定连续。

        其次,我们来看看该函数的参数情况:

  1. sb:ext2文件系统内存超级块结构;
  2. group:从所在的块组分配磁盘块;
  3. bitmap_bh:块组的位图信息;
  4. grp_goal:调用者建议的最佳分配块号,快组内编号;
  5. my_rsv:文件的预留窗口,如果为NULL,则意味着不使用预留窗口;
  6. count:调用者传入的欲分配磁盘块数量,同时该参数也作为返回值,告知调用者时机分配的磁盘块数量。
        该函数的返回值情况:返回值为分配到的空闲磁盘块的起始块号,同时*count中记录了分配到的磁盘块数量。
        接下来,我们看看该函数的具体实现如何。
if (my_rsv == NULL) {
		return ext2_try_to_allocate(sb, group, bitmap_bh,
						grp_goal, count, NULL);
	}
        首先,我们判断调用者是否决定使用预留窗口,如果不使用预留窗口机制,那我们还是老老实实采用最传统的方法,调用ext2_try_to_allocate()来分配。这个函数我们会在接下来的博客中详细解析。
        如果,调用者决定采用预留窗口机制来分配的话,那么好,事情好像就不是这么简单了。下面的这个while循环就很能说明情况:
while (1) {
		if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) ||
			!goal_in_my_reservation(&my_rsv->rsv_window,
						grp_goal, group, sb)) {
			if (my_rsv->rsv_goal_size < *count)
				my_rsv->rsv_goal_size = *count;
			ret = alloc_new_reservation(my_rsv, grp_goal, sb,
							group, bitmap_bh);
			if (ret < 0)
				break;			/* failed */
			//we allocate new reservation window but the goal block is still out of this window
			// if so, we set the target block to -1 
			if (!goal_in_my_reservation(&my_rsv->rsv_window,
							grp_goal, group, sb))
				grp_goal = -1;
		/* 这里的else if 意味着:
		** 1. 预留窗口非空;
		** 2. ret = 0;
		** 3. goal 在预留窗口之中
		** 那么磁盘需要判断是否需要扩张预留窗口
		** 判断的条件: 从当前要分配的位置开始直至
		** 预留窗口结束,中间这么多的数据块是否
		** 少于我们需要分配的数量(*count)
		** -----------------------------------------
		**|                     |    |                                      |
		**------------------------------------------
		**start                 goal                                       end
		** goal:我要从这儿开始分配
		** end: 预留窗口结束的位置
		** curr = end - goal + 1: 从想要分配的地方到预留窗口
		** 结束的位置,我可以分配多少个空闲块
		** 如果curr 小于想要的数量,那么需要扩充该预留窗口了
		*/	
		} else if (grp_goal >= 0) {
			/*expand old reservation window
			**what is @current? current =? my_rsv->rsv_end - my_rsv->start + 1
			*/
			int curr = my_rsv->rsv_end -
					(grp_goal + group_first_block) + 1;

			if (curr < *count)
				try_to_extend_reservation(my_rsv, sb,
							*count - curr);
		}
		//if the reservation window is out of range
		if ((my_rsv->rsv_start > group_last_block) ||
				(my_rsv->rsv_end < group_first_block)) {
			rsv_window_dump(&EXT2_SB(sb)->s_rsv_window_root, 1);
			BUG();
		}
		/*allocate free block from reservation window
		*we get @num free blocks this time
		*/
		ret = ext2_try_to_allocate(sb, group, bitmap_bh, grp_goal,
					   &num, &my_rsv->rsv_window);
		if (ret >= 0) {
			my_rsv->rsv_alloc_hit += num;
			*count = num;
			break;				/* succeed */
		}
		num = *count;
	}
        在while()循环的一开始,就来了个比较恶心的判断,当前是否需要分配一个新的预留窗口,我们仔细考虑下,需要分配新的预留窗口的条件是:
  1. 文件当前预留窗口为空,即尚未分配物理磁盘块 或者;
  2. 当前建议最佳分配块号不在预留窗口之内,我们分配的准则是尽量从建议块那个地方或者附近开始分配,如果出现建议块不在预留窗口范围之内,我们毫不犹豫地丢弃老的预留窗口,并为之分配一个新的预留窗口(建议分配块号落在其中)。
       如果无需分配新的预留窗口,那我们还得考虑是否需要扩展当前的预留窗口,即当前预留窗口是否足够大,能够满足我的分配请求(即预留窗口的末端end >= goal + *count),如下图所示:
        等上面的准备工作都已经完成后,我们再来调用ext2_try_to_allocate()从我们已经分配或者扩展后的预留窗口中分配物理磁盘块,这也是我们下一篇博客需要重点分析的内容。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值