转载的,找不到原创了varyoffvg命令的流程和理解一、varyoffvgvaryoffvg脚本实际上是对lvaryoffvg命令的封装,以varyoffvg datavg为例,脚本主体流程如下:流程命令注释获取VGIDVGID=`getlvodm -v $VGNAME`通过ODM,将VGName转为VGID加锁putlvodm -k $VGID禁止其他影响VG的操作,包...
显示全部转载的,找不到原创了
varyoffvg命令的流程和理解
一、varyoffvg
varyoffvg脚本实际上是对lvaryoffvg命令的封装,以varyoffvg datavg为例,脚本主体流程如下:
流程
命令
注释
获取VGID
VGID=`getlvodm -v $VGNAME`
通过ODM,将VGName转为VGID
加锁
putlvodm -k $VGID
禁止其他影响VG的操作,包括lsvg
调用lvaryoffvg
lvaryoffvg -g $VGID
lvaryoffvg执行实际的varyoff工作
解锁
putlvodm -K $VGID
其中lvaryoffvg和putlvodm都是所谓的intermediate-level command。
二、lvaryoffvg
lvaryoffvg是二进制可执行文件,看看它调用了哪些库文件。
dump -Tv /usr/sbin/lvaryoffvg
...........
[27]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_varyoffvg
............
感觉lvm_varyoffvg这个函数是lvaryoffvg命令的核心。这是个公开的函数,在/usr/include/lvm.h头文件中有定义。
struct varyoffvg
{
struct unique_id vg_id;
/* volume group id */
long
lvs_only;
/* flag to varyoff only logical volumes */
};
extern int lvm_varyoffvg(struct varyoffvg *varyoffvg);
我们可以写一个自己的命令来替代lvaryoffvg,并且让varyoffvg调用我们提供的命令。
#include
#include
struct varyoffvg
{
struct unique_id vg_id;
long
lvs_only;
};
extern int lvm_varyoffvg(struct varyoffvg *varyoffvg);
__ulong32_t str2hex(char *str)
{
__ulong32_t value=0;
unsigned int i,j;
for(i=0;i<8;i++)
{
j=0;
if(str>='0'&&str<='9')
{
j=str-'0';
}
else if(str>='a'&&str<='f')
{
j=str-'a'+0xa;
}
else if(str>='A'&&str<='F')
{
j=str-'A'+0xA;
}
j=(j<<((7-i)*4));
value|=j;
}
return value;
}
int main(int argc,char *argv[])
{
int result=0;
struct varyoffvg Varyoffvg;
char *cmd=argv[0];
if(argc!=2)
{
printf("usage:%s vgid\n",cmd);
exit(-1);
}
Varyoffvg.vg_id.word4=str2hex(argv[1]+24);
Varyoffvg.vg_id.word3=str2hex(argv[1]+16);
Varyoffvg.vg_id.word2=str2hex(argv[1]+8);
Varyoffvg.vg_id.word1=str2hex(argv[1]+0);
Varyoffvg.lvs_only=0;
result=lvm_varyoffvg(&Varyoffvg);
return result;
}
ü
编译这个程序,得到my_lvaryoffvg文件,假设为/tmp/my_lvaryoffvg,权限为750。
ü
创建testvg,并且varyon。为了简单起见,可以不用创建LV和FS
ü
编辑/usr/sbin/varyoffvg脚本,将lvaryoffvg -g $VGID $SFLAG替换为/tmp/my_lvaryoffvg $VGID
ü
执行varyoffvg testvg,发现testvg被成功varyoff
ü
编辑/usr/sbin/varyoffvg脚本,改回原来的命令。
三、putlvodm
putlvodm具有很多参数,可以参考lvm cmds from a to z中的介绍,我们现在只关心和锁住卷组有关的内容。首先看一下它调用了哪些库文件
#dump -Tv /usr/sbin/putlvodm | grep lock
[40]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_cfglock
[41]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_free_cfg_lock
[42]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_get_cfg_lock
[43]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_free_sync_lock
[44]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_get_sync_lock
[47]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_cfglock_query
[55]
0x00000000
undef
IMP
DS EXTref libodm.a(shr.o) odm_unlock
[56]
0x00000000
undef
IMP
DS EXTref libodm.a(shr.o) odm_lock
看上去有两种锁,一种是和LVM相关的,另一种是和ODM相关。接下来再看一下putlvodm -k的效果。首先执行putlvodm -k ,然后在其他窗口中执行lsvg 。我们发现lsvg命令被hang住,过一段时间产生0516-1201报错,提示我们用chvg -u解锁。我们执行putlvodm -K ,则很快lsvg命令就恢复执行。有此可见putlvodm和lsvg争夺相同的锁。那么看看lsvg命令调用了哪些库文件
#dump -Tv /usr/sbin/lsvg | grep lock
[4]
0x00000000
undef
IMP
RW EXTref
libc.a(shr.o) _libc_lock_funcs
[6]
0x00000000
undef
IMP
DS EXTref
libc.a(shr.o) _rec_mutex_unlock
[11]
0x00000000
undef
IMP
DS EXTref
libc.a(shr.o) _rec_mutex_lock
[66]
0x00000000
undef
IMP
DS EXTref liblvm.a(shr.o) lvm_cfglock
和putlvodm的相比较,我们发现两个都调用了lvm_cfglock。看来就是这个子例程给vg上了锁。不过这个是未公开的子例程,我们无法直接调用。接下来看看通过kdb能观察到什么。
(0)> ttid 2dac3
SLOT NAME
STATE
TID PRI
RQ CPUID
CL WCHAN
pvthread+016D00
730 lsvg
SLEEP 02DAC3 03C
0
0 35DEBE98
NAME................ lsvg
WTYPE............... WEVENT
...............state :00000003
...............wtype :00000001
...............flags :00000000
..............flags2 :00000000
2dac3是lsvg的线程ID。显然这个线程正处于sleep状态,原因应该是等待某个event。这个event的地址是0x35DEBE98,并且这个地址还会改变。(有哪位高人能通过这个地址还原event的信息?或者我理解有误,这个并不是event的地址?)
四、总结
varyoffvg的流程就是首先给VG加锁,然后调用lvaryoffvg命令执行实际操作,最后解锁。lvaryoffvg是对子例程lvm_varyoffvg的封装,我们用自己的实现替换掉lvaryoffvg命令后varyoffvg仍然可以正常工作。putlvodm执行加锁和解锁。在加锁后涉及VG的操作都hang住,包括lsvg命令,在解锁后才能继续运行。
chvg -u命令也可以为VG解锁。chvg是个脚本,可以发现-u参数最终也调用putlvodm。
orian
lvm config lock是lvm meta data的修改的时候为保证一致性做的内存锁,就是任何时候只有一个线程可以修改同一vg结构。vg结构本身作为数据,在内存中有保留,做为vg信息,在odm中也有。odm锁是为了修改odm时的原子操作。总之,这里的两个锁都是为了保证在修改vg信息的时候,odm和vg数据的一致。varyoffvg 其
实并不做太多的修改,但要更新一个时间戳,以便下次varyonvg的时候对比vg信息是否和odm中一致。单从一
个地址是反向推不出来是什么event的,但根据上下文,应当是查询vg是否free或者odm是否free的东西。
mike79
你有lvm_cfglck函数的使用案例吗?我先前怀疑和/etc/vg/vg文件有关,但是这个文件好像用于
其他的LVM锁。ODM锁通常和$OBJDIR/config_lock有关,不过这个似乎不是强制锁,而是建议锁。但我没
有验证过
sinister
牵扯到 ODM 配置修改时 odm_lock() 加的是 $OBJDIR/config_lock 锁。但牵扯到 LVM
操作时则是 $OBJDIR/lvm_lock 锁。关于 vg 锁,是使用 getattr() 函数并以 "lock"
得到相应的 CuAt,其中的 CuAt->value 字段状态为 'n' 或 'y',表示当前的 vg 锁状态,
改变其状态就可达到互斥效果。修改的话直接 putattr() 即可。putlvodm 这个命令的
最终实现应该就是这么做的。某些函数应该仅是个包装而已。几乎所有有关 vg 操作的
命令基本都是先获得 vg 锁,这也就是 lsvg 与 putlvodm 的互斥结果。而他们关于 vg
锁的最终实现就是我上面介绍的那样。
mike79
就是说加锁的函数调用是相同的,只是针对不同的文件?这个有出处么?能否share?这句话是
说VG锁其实是记录在CuAt中的?那是CuAt中的哪个attribute?
sinister
牵扯到修改 ODM 配置时,加锁函数是相同的,只是针对不同文件。注意:我这里说
的仅是 ODM 中的。系统没必要提供两套ODM LOCK函数。出处是反汇编得到的。可
以尝试下先 odm_lock( lvm_lock ) 然后在不 odm_unlock 时进行两次 ODM lvm 配置
操作,只有第一次是生效的。也就是说必须解锁 lvm_lock 才有效。不过这仅是针对
lvm 的 ODM 配置操作有效。关于 VG lock 我这里也指的是 lvm 在 ODM 中的锁而言。
而且这个跟 OS 版本有关,早期版本 putlvodm 仅是对这个进行操作,我又看了下 5.3
版本的 putlvodm 那个 lvm_cfglock(),这个不是外包装函数,上面我忽略了。这个函
数,是直接与 lvm device driver 通讯的,用 sysconfig() 直接发了个 HD_CFGLOCK 的
I/O CONTROL。然后 lvm device driver 进行了某些操作,后面的就没再看下去。这个
和 lvm 在 ODM 中的 VG lock 还不一样。那个的 CuAt 的 attribute 是 "lock" 不过在为
'n' 时不会显示。写了段代码可以打开和关闭它,然后再用 odmget -q attribute=lock
CuAt 就可以看到了。早期的版本 VG lock 仅是用这个,后面的在 lvm device driver 中
应该又加了一个 IO CONTROL 用于同步一些 lvm 的写操作。
//
// code by sinister
//
#include
#include
#include
#include
#include
#include
#define ODMERROR -1
#define CFGPATH "/etc/objrepos"
int
odm_vg_lock( char* vgname, int lock )
{
struct CuAt* pcuat;
char* odm_errmsg;
char* odm_path;
int odm_lock_id;
int odm_rc;
int temp;
if ( vgname == NULL || lock < 0 )
{
printf( "params error!\n" );
return ODMERROR;
}
odm_rc = odm_initialize();
if ( odm_rc )
{
( void ) odm_err_msg( odm_rc, &odm_errmsg );
printf( "odmrc = %d,%s\n", odm_rc, odm_errmsg );
return ODMERROR;
}
odm_path = odm_set_path( CFGPATH );
odm_lock_id = odm_lock( "/etc/objrepos/lvm_lock", ODM_WAIT );
if ( odm_lock_id == ODMERROR )
{
printf( "odm_lock() to failed!\n" );
odm_rc = ODMERROR;
goto exit;
}
pcuat = getattr( vgname, "lock", FALSE, &temp );
if ( pcuat == NULL || ( int ) pcuat == -1 )
{
printf( "getattr() to failed!\n" );
odm_rc = ODMERROR;
goto exit;
}
if ( lock == TRUE && pcuat->value[0] == 'n' )
{
strcpy( pcuat->value, "y" );
}
else if ( lock == FALSE && pcuat->value[0] == 'y' )
{
strcpy( pcuat->value, "n" );
}
else
{
odm_rc = ODMERROR;
goto exit;
}
odm_rc = putattr( pcuat );
if ( odm_rc == ODMERROR )
{
printf( "putattr() to failed!\n" );
odm_rc = ODMERROR;
}
exit : free( odm_path );
if ( odm_lock_id != ODMERROR )
{
odm_unlock( odm_lock_id );
}
( void ) odm_terminate();
return odm_rc;
}
int
main( int argc, char** argv )
{
char* vgname;
int c; char opt;
int rc;
if ( argc != 3 )
{
printf( "params error!\n" );
return -1;
}
opterr = 0;
while ( ( c = getopt( argc, argv, "lLuU" ) ) != EOF )
{
switch ( c )
{
case 'l':
opt = 'l';
break;
case 'L':
opt = 'l';
break;
case 'u':
opt = 'u';
break;
case 'U':
opt = 'u';
break;
default:
printf( "Unknow\n" );
break;
}
}
if ( argc - optind != 1 )
{
exit( -1 );
}
vgname = argv[optind];
if ( opt == 'l' )
{
rc = odm_vg_lock( vgname, TRUE );
}
else if ( opt == 'u' )
{
rc = odm_vg_lock( vgname, FALSE );
}
}收起