⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lvm-snap.c.txt

📁 linux内核学习笔记 希望想看的人可以很快下载到
💻 TXT
字号:
any problems, send mails to sindybear@163.com

相关文件
	/driver/md/lvm.c
	/fs/iobuf.c


************************snap_shot型lv的生成和初始化**************************
(1)
int lvm_snapshot_alloc(lv_t * lv_snap)

	/*
	 * 为lv_iobuf分配页面和相应的结构
	 */
	ret = alloc_kiovec(1, &lv_snap->lv_iobuf);	//分配iobuf
	max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9)	//计算最大的io扇区数,也就使
	ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);

	/*
	 * 和上面的过程类似。
	 * 为lv_COW_table_iobuf分配页面和相应的结构
	 */
	ret = alloc_kiovec(1, &lv_snap->lv_COW_table_iobuf);
	ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_COW_table_iobuf,
                                            PAGE_SIZE/SECTOR_SIZE);	

	ret = lvm_snapshot_alloc_hash_table(lv_snap);
	……	//进行错误处理
	

(2)
int lvm_snapshot_alloc_iobuf_pages(struct kiobuf * iobuf, int sectors)	
	bytes = sectors * SECTOR_SIZE;			//先将需要进行iobuf的扇区换算成为字节数
	nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT;	//再计算出这些字节相当于多少页面

	err = expand_kiobuf(iobuf, nr_pages);		//扩展kiobuf结构中的maplist域

	err = -ENOMEM;
	iobuf->locked = 1;
	iobuf->nr_pages = 0;
	for (i = 0; i < nr_pages; i++)
	{
		struct page * page;

		page = alloc_page(GFP_KERNEL);		//分配一个页面
		if (!page)
			goto out;

		iobuf->maplist[i] = page;		//将这个页面放入iobuf中相应的maplist中
		LockPage(page);				//将页面锁住。
		iobuf->nr_pages++;			//页面计数加一
	}
	iobuf->offset = 0;				//???


(3)
int lvm_snapshot_alloc_hash_table(lv_t * lv)
	buckets = lv->lv_remap_end;			
	max_buckets = calc_max_buckets();		
	buckets = min(buckets, max_buckets);		//计算出哈希表的桶值
	
	size = buckets * sizeof(struct list_head);	//静态地分配list_head结构
	hash = vmalloc(size);				//分配相应的内存
	lv->lv_snapshot_hash_table = hash;		//将哈系表存入lv相应的域中
	lv->lv_snapshot_hash_table_size = size;		//保存哈希表的大小
	lv->lv_snapshot_hash_mask = buckets-1;		//保存哈希表的mask值
	while (buckets--)
		INIT_LIST_HEAD(hash+buckets);		//初始化这些结构
	


(4)
static int calc_max_buckets(void)
	unsigned long mem;

	mem = num_physpages << PAGE_SHIFT;
	mem /= 100;
	mem *= 2;
	mem /= sizeof(struct list_head);

	return mem;


(5)
int lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap)

	int id = 0, is = lv_snap->lv_remap_ptr;	

	/*
	 * 先得到lv_COW_table_iobuf结构
	 */	
	lv_COW_table_disk_t * lv_COW_table = (lv_COW_table_disk_t *)
               page_address(lv_snap->lv_COW_table_iobuf->maplist[0]);

	/*
	 * 如果lv_remap_ptr是0,说明还没有使用snapshot,就不用填充了
	 */
	if (is == 0)
               return 0;

	/*
	 * 如果lv_remap_ptr不是空,说明这个snapshot原来已经存在,从磁盘上读出来了
	 * 所以要进行初始化
	 */
	is--;
        blksize_snap =
               lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new);
        is -= is % (blksize_snap / sizeof(lv_COW_table_disk_t));

	memset(lv_COW_table, 0, blksize_snap);

	/*
	 * 其实这段函数的功能就是将lv_remap_ptr所指向的exception向前回溯到一个blksize
	 * 所能容纳的lv_COW_table_disk_t的数量。看来一次只能作一个blocksize的写操作。
	 */
	for ( ; is < lv_snap->lv_remap_ptr; is++, id++) {		
               lv_block_exception_t *be = lv_snap->lv_block_exception + is;
               if(_pv_get_number(vg, be->rdev_org, &pvn))
                       goto bad;

               lv_COW_table[id].pv_org_number = cpu_to_le64(pvn);
               lv_COW_table[id].pv_org_rsector = cpu_to_le64(be->rsector_org);
               if(_pv_get_number(vg, be->rdev_new, &pvn))
                       goto bad;

               lv_COW_table[id].pv_snap_number = cpu_to_le64(pvn);
               lv_COW_table[id].pv_snap_rsector =
                       cpu_to_le64(be->rsector_new);
	}

       return 0;
*************************************************************************************




**************************snap_shot设备的地址重映射过程*******************************
(1)
/*
 * 这个函数检查给定的设备块是否已经有一个snap_shot的映射
 *	1---表明已经有一个映射了
 *	0---copy on write这个块并且分配一个新的块
 *	-1--如果snap_shot没有地方的话,就丢弃
 */
int lvm_snapshot_remap_block(kdev_t * org_dev, unsigned long * org_sector,
			     unsigned long pe_start, lv_t * lv)
	
	if (!lv->lv_block_exception)	//说明这个snapshot已经disable了
		return -1;		//返回-1
	
	pe_off = pe_start % chunk_size;	//计算相对于chunk_size的偏移量
	pe_adjustment = (*org_sector-pe_off) % chunk_size;	//
	__org_start = *org_sector - pe_adjustment;
	__org_dev = *org_dev;
	ret = 0;
	exception = lvm_find_exception_table(__org_dev, __org_start, lv);//在哈希表中寻找
	if (exception)		//如果找到的话
	{
		*org_dev = exception->rdev_new;		//设备号设置为新的设备号
		*org_sector = exception->rsector_new + pe_adjustment;	//sector也从新设置
		ret = 1;
	}



(2)
/*
 * 注意这里从源设备读出旧数据,然后再写到snapshot盘上,这个过程是异步的。
 */
int lvm_snapshot_COW(kdev_t org_phys_dev,
		     unsigned long org_phys_sector,
		     unsigned long org_pe_start,
		     unsigned long org_virt_sector,
		     vg_t *vg, lv_t* lv_snap)
	
	int idx = lv_snap->lv_remap_ptr		//取出当前exception结构的标号。
	if (idx >= lv_snap->lv_remap_end)	//检查是否这个snap_shot设备已经用完了
		goto fail_out_of_space;
	
	/*
	 * 下面的这三行程序基本上没有什么作用。非常奇怪???
	 */
	pe_off = org_pe_start % chunk_size;
	org_start = org_phys_sector - ((org_phys_sector-pe_off) % chunk_size);
	virt_start = org_virt_sector - (org_phys_sector - org_start);

	snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;	//得到snapshot的设备号
	snap_start = lv_snap->lv_block_exception[idx].rsector_new;	//得到扇区号

	iobuf = lv_snap->lv_iobuf;		//得到snap_shot的iobuf结构
	
	/*
	 * 下面的函数就是将原先的数据存储到snap_shot的lv上。这个函数完成以后
	 * 数据已经物理地存在于磁盘上了。
	 */
	lvm_do_bulk_copy_iobuf(iobuf, org_phys_dev, snap_phys_dev,
			 org_start, snap_start, chunk_size)

	lv_snap->lv_block_exception[idx].rdev_org = org_phys_dev;	//设置snapshot的源盘
	lv_snap->lv_block_exception[idx].rsector_org = org_start;	//设置源起始扇区

	/*
	 * 将新生成的exception连接到哈希表中,这样才能被系统检索到
	 */
	lvm_hash_link(lv_snap->lv_block_exception + idx, org_phys_dev, org_start, lv_snap);
	lv_snap->lv_remap_ptr = idx + 1;	//

	if (lv_snap->lv_snapshot_use_rate > 0) {
		if (lv_snap->lv_remap_ptr * 100 / lv_snap->lv_remap_end >=
					 lv_snap->lv_snapshot_use_rate)
			wake_up_interruptible(&lv_snap->lv_snapshot_wait);
	}


	
(3)
static int lvm_do_bulk_copy_iobuf(struct kiobuf *iobuf,
				  kdev_t old_dev, kdev_t new_dev, 
				  uint old_start, uint new_start, 
				  uint total_sectors)
	old_blksize = get_hardsect_size(old_dev);
	new_blksize = get_hardsect_size(new_dev);

	max_blksize = max(old_blksize, new_blksize);
	min_blksize = min(old_blksize, new_blksize);
	
	if (total_sectors % (max_blksize>>9))		//如果读写的总扇区数目不是设备块的整数倍
		goto out;				//返回错误

	while (total_sectors) {
		/*
		 * 如果total_sectors比kiobuf所能做的最大的扇区数还大的话,就只能将
		 * 这些扇区分开来一次次作
		 */
		io_sectors = min(total_sectors, (uint) KIO_MAX_SECTORS);
		total_sectors -= io_sectors;
		
		iobuf->length = io_sectors << 9;	//设置kiobuf的读写长度



		/* 
		 * 以下两行主要是将指定的sectors从原来的设备上读出来。
		 */
		lvm_snapshot_prepare_blocks(iobuf->blocks, old_start, io_sectors, old_blksize)
		ret = brw_kiovec(READ, 1, &iobuf, old_dev,
				 iobuf->blocks, old_blksize) ;
		if (ret != (io_sectors<<9))	//如果读出来扇区数和参数要求的不一样。
			goto fail_io;		//返回错误


		
		/* 
		 * 以下两行主要是将读出来的数据写到snap_shot设备中。
		 */
		lvm_snapshot_prepare_blocks(iobuf->blocks, new_start,io_sectors, new_blksize)
		ret = brw_kiovec(WRITE, 1, &iobuf, new_dev, iobuf->blocks, new_blksize);
		if (ret != (io_sectors<<9))
			goto fail_io;
		
		old_start += io_sectors;	//移动指针
		new_start += io_sectors;	//移动指针
	}
		

(4)
/*
 * 这个函数主要是对kiobuf中的block进行设置,也就是将要读写的数据块的
 * 开始地址一一放入block数组中。
 */
static inline int lvm_snapshot_prepare_blocks(unsigned long *blocks,
					      unsigned long start,
					      int nr_sectors,
					      int blocksize)
	sectors_per_block = blocksize / SECTOR_SIZE;	//计算给定设备的一块包含多少个扇区
	if(start & (sectors_per_block - 1))		//如果给定的开始地址不是扇区的整数倍
		return 0;				//就返回错误

	nr_blocks = nr_sectors / sectors_per_block;	//计算要进行读写的扇区跨越几个块
	start /= sectors_per_block;			//计算起始块号

	for (i = 0; i < nr_blocks; i++)			//按照块号
		blocks[i] = start++;			//将要读写的块号分别放入block数组中




(5)
int lvm_write_COW_table_block(vg_t * vg, lv_t *lv_snap)
	int r;
	const char *err;
	if((r = _write_COW_table_block(vg, lv_snap,
				       lv_snap->lv_remap_ptr - 1, &err)))
		lvm_drop_snapshot(vg, lv_snap, err);
	return r;



(6)
static int _write_COW_table_block(vg_t *vg, lv_t *lv_snap,
				  int idx, const char **reason) 
	ulong blocks[1];

	//得到这个snapshot的kiobuf结构
	struct kiobuf * COW_table_iobuf = lv_snap->lv_COW_table_iobuf;

	//从这个kiobuf结构中取出lv_COW_table_t指针数组。	
	lv_COW_table_disk_t * lv_COW_table =
	   ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_iobuf->maplist[0]);

	COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap);
	COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap);

	//得到相应数据块的设备号
	snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;

	/*
	 * 这里的计算很有问题,其本意是想得到这个eception所在的pe的开始扇区号,
	 * 所以在减号后面的计算应该是(COW_chunks_per_pe - COW_entries_per_pe)
	 */
	snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size;

	/*
	 * 得到要写的snapshot设备的块大小。注意不是lvm设备的逻辑块大小,是本身的物理设备
	 * 的块大小。这里的cow_table的写出是按照物理设备的块大小写出的。
	 */
	blksize_snap = get_hardsect_size(snap_phys_dev);


        COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t);

	/*
	 * 他计算这个eception处于当前的COW_table的偏移量
	 */
        idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block;

	/*
	 * 如果偏移量是0,说明是一个新的COW_table。就将lv_COW_table全部置0
	 */
	if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap);

	/*
	 * 下面的代码计算这个eception在他所在的pe中按照sector计算的偏移量。
	 * 也就是在这个pe中位于第几个sector。
	 */
	COW_table_sector_offset = (idx % COW_entries_per_pe) / 
		(SECTOR_SIZE / sizeof(lv_COW_table_disk_t));

        //计算这个lv_COW_table所在的块号。
	blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10);

       be = lv_snap->lv_block_exception + idx;
       if(_pv_get_number(vg, be->rdev_org, &pvn))	//计算pv_number
               goto fail_pv_get_number;


       lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(pvn);	//设置COW_table的数据
       lv_COW_table[idx_COW_table].pv_org_rsector =
               cpu_to_le64(be->rsector_org);
       if(_pv_get_number(vg, snap_phys_dev, &pvn))
               goto fail_pv_get_number;
       lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(pvn);	//设置COW_table的数据
       lv_COW_table[idx_COW_table].pv_snap_rsector =
               cpu_to_le64(be->rsector_new);

	COW_table_iobuf->length = blksize_snap;		//将kiobuf的读写长度设置为设备的块大小

	if (brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev,	//进行写出操作
		       blocks, blksize_snap) != blksize_snap)
		goto fail_raw_write;

	//以下的代码是初始化下一个COW_table。
	/*
	 * 如果exception已经处于一个pe所能容纳的最後一个,则end_of_table是true
	 */
	end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1;

	/*
	 * 这里的计算分为两种情况,
	 * 1---当这个exception只是处于一个snap_chunk所能容纳的最后一个的情况
	 * 2---这个exception已经处于它所在的pe所能容纳的最后一个的情况。这个时候要改变设备号等
	 * 	因为可能虽然这些pe处于同一个snapshot设备中,但是他们来自不同的物理设备。
	 */	
	if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table)
	{
               if (idx + 1 >= lv_snap->lv_remap_end) goto out;	//如果超出了snapshot的范围,返回

		memset(lv_COW_table, 0, blksize_snap);	//两种情况都将lv_COW_table清空

		if (end_of_table)			//如果是第二种情况。
		{
			idx++;				//得到下一个pe的第一个exception的号码
			snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;//得到它的设备号
			snap_pe_start = lv_snap->lv_block_exception[idx - (idx % 	COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size;		//得到pe的开始扇区号
			blksize_snap = get_hardsect_size(snap_phys_dev);//得到设备的块大小
			blocks[0] = snap_pe_start >> (blksize_snap >> 10);//计算读写块号
		} else blocks[0]++;	//如果是第一种情况,不用改变别的,只用增加块号

		/* 
		 * 将lv_COW_table写出到snapshot设备上,因为这个时候lv_COW_table都是0
		 * 这个操作相当于清空下一个pe中存储lv_COW_table_t的扇区。
		 */
               if (brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev,
                                 blocks, blksize_snap) !=
                    blksize_snap)
			goto fail_raw_write;
	}
**************************************************************************************
















⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -