📄 lvm-snap.c.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 + -