文档章节

LVM源码分析1-概览

LastRitter
 LastRitter
发布于 2017/09/07 22:13
字数 3892
阅读 130
收藏 1

获取CentOS LVM源码

$ which lvmetad
/usr/sbin/lvmetad
$ rpm -qf /usr/sbin/lvmetad
lvm2-2.02.130-5.el7_2.5.x86_64

$ which lvmpolld
/usr/sbin/lvmpolld
$ rpm -qf /usr/sbin/lvmpolld
lvm2-2.02.130-5.el7_2.5.x86_64

$ cd ~/rpmbuild
$ yumdownloader --source lvm2
$ rpm -ivh lvm2-2.02.130-5.el7_2.5.src.rpm

$ rpmbuild -bp SPECS/lvm2.spec
$ mkdir -pv /opt/lvm2
$ cp BUILD/LVM2.2.02.130/ /opt/lvm2/ -rv

$ rpmbuild -ba SPECS/lvm2.spec
$ ls RPMS/x86_64/lvm* -l | awk '{print $9}'
RPMS/x86_64/lvm2-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-cluster-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-cluster-standalone-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-debuginfo-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-devel-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-libs-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-lockd-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-python-libs-2.02.130-5.el7.centos.5.x86_64.rpm
RPMS/x86_64/lvm2-sysvinit-2.02.130-5.el7.centos.5.x86_64.rpm	

获取LVM官方源码

$ mkdir -pv /opt/lvm2
$ cd /opt/lvm2
$ git clone git://git.fedorahosted.org/git/lvm2.git src
$ mkdir -v build target
	
$ yum install dlm-devel pkgconfig systemd-units device-mapper-persistent-data \
corosynclib-devel libblkid-devel
$ cd build
$ ../src/configure --prefix=/opt/lvm2/target/ --enable-applib --enable-cmdlib \
--enable-lvmetad --enable-lvmpolld --enable-cmirrord --enable-debug --enable-lockd-dlm \
--enable-pkgconfig --enable-write_install --enable-dmeventd --enable-nls
	
$ make
$ make install
	
$ export LD_LIBRARY_PATH=/opt/lvm2/target/lib/:$LD_LIBRARY_PATH
$ export PATH=/opt/lvm2/target/sbin:$PATH
$ ./tools/lvm help

查看目标文件

$ cd ../target
$ tree .
.
├── include
│   ├── libdevmapper.h
│   ├── lvm2app.h
│   └── lvm2cmd.h
├── lib
│   ├── device-mapper
│   │   ├── libdevmapper-event-lvm2mirror.so
│   │   ├── libdevmapper-event-lvm2raid.so
│   │   ├── libdevmapper-event-lvm2snapshot.so
│   │   └── libdevmapper-event-lvm2thin.so	│   ├── libdevmapper.so.1.02
│   ├── libdevmapper-event-lvm2.so.2.02
│   ├── libdevmapper-event.so.1.02
│   ├── libdevmapper.so.1.02
│   ├── liblvm2app.so.2.2
│   ├── liblvm2cmd.so.2.02
│   └── ...
├── sbin
│   ├── blkdeactivate   # 去激活块设备
│   ├── cmirrord        # 记录日志
│   ├── dmeventd        # 接收DM事件(比如说精简卷已满的事件)
│   ├── dmsetup         # 配置DM设备用户工具
│   ├── fsadm           # 调整文件系统大小
│   ├── lvm             # LVM命令的最终执行程序
│   ├── lvmconf         # 修改LVM配置
│   ├── lvmdump         # 导出当前LVM信息
│   ├── lvmetad         # 元数据缓存守护进程
│   ├── lvmlockctl      # 操作lvmlockctl的命令
│   ├── lvmlockd        # LVM锁守护进程,可以使用sanlock或者dlm
│   ├── lvmpolld        # LVM命令后台执行进程
│   └── ...
└── share
    └── man
        └── man8
        ...
8 directories, 118 files

可见LVM不仅提供LVM命令,还提供与DM驱动进行通信和LVM的编程接口。

查看命令的库依赖

$ find sbin -type f | xargs ldd
sbin/lvmlockd:
	linux-vdso.so.1 =>  (0x00007fff3b197000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f83e7977000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007f83e7707000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f83e74ff000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f83e72e3000)
	libdlm_lt.so.3 => /lib64/libdlm_lt.so.3 (0x00007f83e70dd000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f83e6d1b000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f83e7bae000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f83e6af6000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007f83e68b0000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f83e65ae000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f83e634d000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f83e6127000)
sbin/lvmconf:
	不是动态可执行文件
sbin/lvmetad:
	linux-vdso.so.1 =>  (0x00007fff5af8c000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f84a23e9000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007f84a2179000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f84a1f71000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f84a1d55000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f84a1992000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f84a2834000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f84a176d000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007f84a1528000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f84a1225000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f84a0fc4000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f84a0d9f000)
	sbin/lvm:
	linux-vdso.so.1 =>  (0x00007ffe85933000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007ff248621000)
	libdevmapper-event.so.1.02 => /opt/lvm2/target/lib/libdevmapper-event.so.1.02 (0x00007ff248418000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007ff2481a9000)
	libreadline.so.6 => /lib64/libreadline.so.6 (0x00007ff247f63000)
	librt.so.1 => /lib64/librt.so.1 (0x00007ff247d5a000)
	libc.so.6 => /lib64/libc.so.6 (0x00007ff247998000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ff248c54000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007ff247773000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007ff24752d000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ff247311000)
	libm.so.6 => /lib64/libm.so.6 (0x00007ff24700f000)
	libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007ff246de4000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007ff246b83000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007ff24695e000)
sbin/lvmlockctl:
	linux-vdso.so.1 =>  (0x00007ffeca352000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f7b56c09000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007f7b56999000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f7b565d7000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f7b56e40000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f7b563cf000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f7b561a9000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007f7b55f64000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f7b55d48000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f7b55a45000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f7b557e4000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f7b555bf000)
sbin/blkdeactivate:
	不是动态可执行文件
sbin/lvmpolld:
	linux-vdso.so.1 =>  (0x00007ffc0df4d000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f36a272f000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007f36a24bf000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f36a22b7000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f36a209b000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f36a1cd8000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f36a2966000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f36a1ab3000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007f36a186e000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f36a156b000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f36a130a000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f36a10e5000)
sbin/fsadm:
	不是动态可执行文件
sbin/dmsetup:
	linux-vdso.so.1 =>  (0x00007ffd7b9d4000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007efe39d14000)
	librt.so.1 => /lib64/librt.so.1 (0x00007efe39ad9000)
	libc.so.6 => /lib64/libc.so.6 (0x00007efe39717000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007efe394f2000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007efe392ac000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007efe39090000)
	libm.so.6 => /lib64/libm.so.6 (0x00007efe38d8e000)
	/lib64/ld-linux-x86-64.so.2 (0x00007efe3a1ad000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007efe38b2c000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007efe38907000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007efe38703000)
sbin/lvmdump:
	不是动态可执行文件
sbin/dmeventd:
	linux-vdso.so.1 =>  (0x00007ffd2c59b000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007fecfe2a3000)
	libdevmapper-event.so.1.02 => /opt/lvm2/target/lib/libdevmapper-event.so.1.02 (0x00007fecfe09a000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fecfde7e000)
	librt.so.1 => /lib64/librt.so.1 (0x00007fecfdc76000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007fecfda06000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fecfd644000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fecfe6e8000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fecfd41f000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007fecfd1d9000)
	libm.so.6 => /lib64/libm.so.6 (0x00007fecfced7000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007fecfcc76000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007fecfca50000)
sbin/cmirrord:
	linux-vdso.so.1 =>  (0x00007ffe1adf1000)
	libcpg.so.4 => /lib64/libcpg.so.4 (0x00007f3d3bdd0000)
	librt.so.1 => /lib64/librt.so.1 (0x00007f3d3bbc7000)
	libdevmapper.so.1.02 => /opt/lvm2/target/lib/libdevmapper.so.1.02 (0x00007f3d3b958000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f3d3b596000)
	libcorosync_common.so.4 => /lib64/libcorosync_common.so.4 (0x00007f3d3b392000)
	libqb.so.0 => /lib64/libqb.so.0 (0x00007f3d3b12d000)
	libdl.so.2 => /lib64/libdl.so.2 (0x00007f3d3af29000)
	libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f3d3ad0c000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f3d3c22f000)
	libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f3d3aae7000)
	libsepol.so.1 => /lib64/libsepol.so.1 (0x00007f3d3a8a2000)
	libm.so.6 => /lib64/libm.so.6 (0x00007f3d3a59f000)
	libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f3d3a33e000)
	liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f3d3a119000)

他们全部依赖libdevmapper库来与DM驱动进行通信。

查看CentOS LVM的服务

$ systemctl list-units | grep lvm
lvm2-lvmetad.service                                            
							loaded active running   LVM2 metadata daemon
lvm2-monitor.service                                            
							loaded active exited    Monitoring of LVM2 mirrors, 
							snapshots etc. using  dmeventd or progress polling
lvm2-pvscan@253:0.service                                       
							loaded active exited    LVM2 PV scan on device 253:0
system-lvm2\x2dpvscan.slice                                     
							loaded active active    system-lvm2\x2dpvscan.slice
lvm2-lvmetad.socket                                              
							loaded active running   LVM2 metadata daemon socket
lvm2-lvmpolld.socket                                            
							loaded active listening LVM2 poll daemon socket

$ ls /usr/lib/systemd/system/*lvm*
/usr/lib/systemd/system/lvm2-lvmetad.service  
/usr/lib/systemd/system/lvm2-lvmpolld.service  
/usr/lib/systemd/system/lvm2-monitor.service
/usr/lib/systemd/system/lvm2-lvmetad.socket   
/usr/lib/systemd/system/lvm2-lvmpolld.socket   
/usr/lib/systemd/system/lvm2-pvscan@.service

$ find /usr/lib/systemd/system -name *lvm* -exec cat {} \;
[Unit]
Description=Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
Documentation=man:dmeventd(8) man:lvcreate(8) man:lvchange(8) man:vgchange(8)
Requires=dm-event.socket lvm2-lvmetad.socket
After=dm-event.socket dm-event.service lvm2-lvmetad.socket lvm2-activation.service lvm2-lvmetad.service
Before=local-fs.target
DefaultDependencies=no
Conflicts=shutdown.target

[Service]
Type=oneshot
Environment=LVM_SUPPRESS_LOCKING_FAILURE_MESSAGES=1
ExecStart=/usr/sbin/lvm vgchange --monitor y --ignoreskippedcluster
ExecStop=/usr/sbin/lvm vgchange --monitor n --ignoreskippedcluster
RemainAfterExit=yes

[Install]
WantedBy=sysinit.target


[Unit]
Description=LVM2 poll daemon socket
Documentation=man:lvmpolld(8)
DefaultDependencies=no

[Socket]
ListenStream=/run/lvm/lvmpolld.socket
SocketMode=0600
RemoveOnStop=true

[Install]
WantedBy=sysinit.target


[Unit]
Description=LVM2 PV scan on device %i
Documentation=man:pvscan(8)
DefaultDependencies=no
BindsTo=dev-block-%i.device
Requires=lvm2-lvmetad.socket
After=lvm2-lvmetad.socket lvm2-lvmetad.service
Before=shutdown.target
Conflicts=shutdown.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/lvm pvscan --cache --activate ay %i
ExecStop=/usr/sbin/lvm pvscan --cache %i


[Unit]
Description=LVM2 poll daemon
Documentation=man:lvmpolld(8)
Requires=lvm2-lvmpolld.socket
After=lvm2-lvmpolld.socket
DefaultDependencies=no
Conflicts=shutdown.target

[Service]
Type=simple
NonBlocking=true
ExecStart=/usr/sbin/lvmpolld -t 60 -f
Environment=SD_ACTIVATION=1
PIDFile=/run/lvmpolld.pid

[Install]
WantedBy=sysinit.target


[Unit]
Description=LVM2 metadata daemon
Documentation=man:lvmetad(8)
Requires=lvm2-lvmetad.socket
After=lvm2-lvmetad.socket
DefaultDependencies=no
Conflicts=shutdown.target

[Service]
Type=simple
NonBlocking=true
ExecStart=/usr/sbin/lvmetad -f
Environment=SD_ACTIVATION=1
Restart=on-abort
PIDFile=/run/lvmetad.pid

[Install]
WantedBy=sysinit.target


[Unit]
Description=LVM2 metadata daemon socket
Documentation=man:lvmetad(8)
DefaultDependencies=no

[Socket]
ListenStream=/run/lvm/lvmetad.socket
SocketMode=0600
RemoveOnStop=true

[Install]
WantedBy=sysinit.target

这些服务主要使用了如下命令:vgchange、lvmpolld、pvscan、lvmetad。

简要分析lvscan命令

tools/lvscan.c

	#include "tools.h"
	// 使用lvmetad处理单个LV信息。
	static int _lvscan_single_lvmetad(struct cmd_context *cmd, struct logical_volume *lv)
	{
		struct pv_list *pvl;
		struct dm_list all_pvs;
		char pvid_s[64] __attribute__((aligned(8)));
	
		if (!lvmetad_used())
			return ECMD_PROCESSED;
	
		dm_list_init(&all_pvs);
	
		if (!get_pv_list_for_lv(lv->vg->vgmem, lv, &all_pvs))
			return ECMD_FAILED;
	
		dm_list_iterate_items(pvl, &all_pvs) {
			if (!pvl->pv->dev) {
				if (!id_write_format(&pvl->pv->id, pvid_s, sizeof(pvid_s)))
					stack;
				else
					log_warn("WARNING: Device for PV %s already missing, skipping.",
						 pvid_s);
				continue;
			}
			if (!lvmetad_pvscan_single(cmd, pvl->pv->dev, NULL, NULL))
				return ECMD_FAILED;
		}
	
		return ECMD_PROCESSED;
	}
	
	// 不使用lvmetad处理单个LV信息。
	static int lvscan_single(struct cmd_context *cmd, struct logical_volume *lv,
				 struct processing_handle *handle __attribute__((unused)))
	{
		struct lvinfo info;
		int inkernel, snap_active = 1;
		dm_percent_t snap_percent;     /* fused, fsize; */
	
		const char *active_str, *snapshot_str;
	
		if (arg_is_set(cmd, cache_long_ARG))
			return _lvscan_single_lvmetad(cmd, lv);
	
		if (!arg_is_set(cmd, all_ARG) && !lv_is_visible(lv))
			return ECMD_PROCESSED;
	
	// 获取LV信息
		inkernel = lv_info(cmd, lv, 0, &info, 0, 0) && info.exists;
		if (lv_is_cow(lv)) {
			if (inkernel &&
			    (snap_active = lv_snapshot_percent(lv, &snap_percent)))
				if (snap_percent == DM_PERCENT_INVALID)
					snap_active = 0;
		}
	
	// 打印LV信息
	/* FIXME Add -D arg to skip this! */
		if (inkernel && snap_active)
			active_str = "ACTIVE   ";
		else
			active_str = "inactive ";
	
		if (lv_is_origin(lv))
			snapshot_str = "Original";
		else if (lv_is_cow(lv))
			snapshot_str = "Snapshot";
		else
			snapshot_str = "        ";
	
		log_print_unless_silent("%s%s '%s%s/%s' [%s] %s", active_str, snapshot_str,
					cmd->dev_dir, lv->vg->name, lv->name,
					display_size(cmd, lv->size),
					get_alloc_string(lv->alloc));
	
		return ECMD_PROCESSED;
	}
	
	int lvscan(struct cmd_context *cmd, int argc, char **argv)
	{
		const char *reason = NULL;
	
		if (argc && !arg_is_set(cmd, cache_long_ARG)) {
			log_error("No additional command line arguments allowed");
			return EINVALID_CMD_LINE;
		}
	
		if (!lvmetad_used() && arg_is_set(cmd, cache_long_ARG))
			log_verbose("Ignoring lvscan --cache because lvmetad is not in use.");
	
		/* Needed because this command has NO_LVMETAD_AUTOSCAN. */
		if (lvmetad_used() && (!lvmetad_token_matches(cmd) || lvmetad_is_disabled(cmd, &reason))) {
			if (lvmetad_used() && !lvmetad_pvscan_all_devs(cmd, 0)) {
				log_warn("WARNING: Not using lvmetad because cache update failed.");
				lvmetad_make_unused(cmd);
			}
	
			if (lvmetad_used() && lvmetad_is_disabled(cmd, &reason)) {
				log_warn("WARNING: Not using lvmetad because %s.", reason);
				lvmetad_make_unused(cmd);
			}
	
			/*
			 * FIXME: doing lvscan --cache after a full scan is pointless.
			 * Should the cache case just exit here?
			 */
		}
	
		return process_each_lv(cmd, argc, argv, NULL, NULL, 0, NULL, &lvscan_single);
	}

tools/command.h

	xx(lvscan,
	   "List all logical volumes in all volume groups",
	   PERMITTED_READ_ONLY | ALL_VGS_IS_DEFAULT | LOCKD_VG_SH | NO_LVMETAD_AUTOSCAN,
	   "lvscan\n"
	   "\t[-a|--all]\n"
	   "\t[-b|--blockdevice]\n"
	   "\t[--cache]\n"
	   "\t[--commandprofile ProfileName]\n"
	   "\t[-d|--debug]\n"
	   "\t[-h|-?|--help]\n"
	   "\t[--ignorelockingfailure]\n"
	   "\t[-P|--partial]\n"
	   "\t[--readonly]\n"
	   "\t[--reportformat {basic|json}]\n"
	   "\t[-v|--verbose]\n"
	   "\t[--version]\n",
	
	   all_ARG, blockdevice_ARG, ignorelockingfailure_ARG, partial_ARG,
	   readonly_ARG, reportformat_ARG, cache_long_ARG)

tools/lvmcmdline.c

	void lvm_register_commands(void)
	{
	#define xx(a, b, c, d...) _register_command(# a, a, b, c, ## d, \
						    driverloaded_ARG, \
						    debug_ARG, help_ARG, help2_ARG, \
						    version_ARG, verbose_ARG, \
						    yes_ARG, \
						    quiet_ARG, config_ARG, \
						    commandprofile_ARG, \
						    profile_ARG, -1);
	#include "commands.h"
	#undef xx
	}

tools/cmdnames.h

#define xx(a, b, c...) a

#include "commands.h"

tools/Makefile.in

install_tools_dynamic: lvm .commands
	$(INSTALL_PROGRAM) -D lvm $(sbindir)/lvm
	@echo Creating symbolic links for individual commands in $(sbindir)
	@for v in `cat .commands`; do \
		echo "$(LN_S) -f lvm $(sbindir)/$$v"; \
		$(LN_S) -f lvm $(sbindir)/$$v; \
	done;

.commands: $(srcdir)/commands.h $(srcdir)/cmdnames.h Makefile
	$(CC) -E -P $(srcdir)/cmdnames.h 2> /dev/null | \
	egrep -v '^ *(|#.*|config|devtypes|dumpconfig|formats|fullreport|help\
	|lastlog|lvpoll|pvdata|segtypes|systemid|tags|version) *$$' > .commands

跟踪process_each_lv函数

根据命令行参数,遍历所有LV (tools/toollib.c : process_single_lv)

	/*
	 * Call process_single_lv() for each LV selected by the command line arguments.
	 */
	int process_each_lv(struct cmd_context *cmd,
			    int argc, char **argv,
			    const char *one_vgname, const char *one_lvname,
			    uint32_t read_flags,
			    struct processing_handle *handle,
			    process_single_lv_fn_t process_single_lv)
	{
		log_report_t saved_log_report_state = log_get_report_state();
		int handle_supplied = handle != NULL;
		struct dm_list arg_tags;		/* str_list */
		struct dm_list arg_vgnames;		/* str_list */
		struct dm_list arg_lvnames;		/* str_list */
		struct dm_list vgnameids_on_system;	/* vgnameid_list */
		struct dm_list vgnameids_to_process;	/* vgnameid_list */
		int enable_all_vgs = (cmd->command->flags & ALL_VGS_IS_DEFAULT);
		int process_all_vgs_on_system = 0;
		int ret_max = ECMD_PROCESSED;
		int ret;
	
		log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_LV);
	
		/* Disable error in vg_read so we can print it from ignore_vg. */
		cmd->vg_read_print_access_error = 0;
	
		dm_list_init(&arg_tags);
		dm_list_init(&arg_vgnames);
		dm_list_init(&arg_lvnames);
		dm_list_init(&vgnameids_on_system);
		dm_list_init(&vgnameids_to_process);
	
		// 根据命令行参数生成VG列表
		/*
		 * Find any LVs, VGs or tags explicitly provided on the command line.
		 */
		if ((ret = _get_arg_lvnames(cmd, argc, argv, one_vgname, one_lvname, &arg_vgnames, 
				&arg_lvnames, &arg_tags) != ECMD_PROCESSED)){
			ret_max = ret;
			goto_out;
		}
	
		if (!handle && !(handle = init_processing_handle(cmd, NULL))) {
			ret_max = ECMD_FAILED;
			goto_out;
		}
	
		if (handle->internal_report_for_select && !handle->selection_handle &&
		    !init_selection_handle(cmd, handle, LVS)) {
			ret_max = ECMD_FAILED;
			goto_out;
		}
	
		/*
		 * Process all VGs on the system when:
		 * . tags are specified and all VGs need to be read to
		 *   look for matching tags.
		 * . no VG names are specified and the command defaults
		 *   to processing all VGs when none are specified.
		 * . no VG names are specified and the select option needs
		 *   resolving.
		 */
		if (!dm_list_empty(&arg_tags))
			process_all_vgs_on_system = 1;
		else if (dm_list_empty(&arg_vgnames) && enable_all_vgs)
			process_all_vgs_on_system = 1;
		else if (dm_list_empty(&arg_vgnames) && handle->internal_report_for_select)
			process_all_vgs_on_system = 1;
	
		/*
		 * Needed for a current listing of the global VG namespace.
		 */
		if (process_all_vgs_on_system && !lockd_gl(cmd, "sh", 0)) {
			ret_max = ECMD_FAILED;
			goto_out;
		}
	
		/*
		 * A list of all VGs on the system is needed when:
		 * . processing all VGs on the system
		 * . A VG name is specified which may refer to one
		 *   of multiple VGs on the system with that name.
		 */
		log_debug("Get list of VGs on system");
	
		// 从DM获取VG列表
		if (!get_vgnameids(cmd, &vgnameids_on_system, NULL, 0)) {
			ret_max = ECMD_FAILED;
			goto_out;
		}
		
		if (!dm_list_empty(&arg_vgnames)) {
			/* This may remove entries from arg_vgnames or vgnameids_on_system. */
			ret = _resolve_duplicate_vgnames(cmd, &arg_vgnames, &vgnameids_on_system);
			if (ret > ret_max)
				ret_max = ret;
			if (dm_list_empty(&arg_vgnames) && dm_list_empty(&arg_tags)) {
				ret_max = ECMD_FAILED;
				goto out;
			}
		}
	
		if (dm_list_empty(&arg_vgnames) && dm_list_empty(&vgnameids_on_system)) {
			/* FIXME Should be log_print, but suppressed for reporting cmds */
			log_verbose("No volume groups found.");
			ret_max = ECMD_PROCESSED;
			goto out;
		}
	
		if (dm_list_empty(&arg_vgnames))
			read_flags |= READ_OK_NOTFOUND;
	
		/*
		 * When processing all VGs, vgnameids_on_system simply becomes
		 * vgnameids_to_process.
		 * When processing only specified VGs, then for each item in
		 * arg_vgnames, move the corresponding entry from
		 * vgnameids_on_system to vgnameids_to_process.
		 */
		if (process_all_vgs_on_system)	// 把系统所有的VG列表添加进待处理的VG列表。
			dm_list_splice(&vgnameids_to_process, &vgnameids_on_system);
		else
			_choose_vgs_to_process(cmd, &arg_vgnames, &vgnameids_on_system, &vgnameids_to_process);
	
		// 遍历VG列表
		ret = _process_lv_vgnameid_list(cmd, read_flags, &vgnameids_to_process, &arg_vgnames, &arg_lvnames,
						&arg_tags, handle, process_single_lv);
	
		if (ret > ret_max)
			ret_max = ret;
	out:
		if (!handle_supplied)
			destroy_processing_handle(cmd, handle);
	
		log_restore_report_state(saved_log_report_state);
		return ret_max;
	}

获取系统中的VG列表 (lib/metadata/metadata.c)

	int get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids,
			  const char *only_this_vgname, int include_internal)
	{
		struct vgnameid_list *vgnl;
		struct format_type *fmt;
	
		if (only_this_vgname) {
			if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
				log_error("vgnameid_list allocation failed.");
				return 0;
			}
	
			vgnl->vg_name = dm_pool_strdup(cmd->mem, only_this_vgname);
			vgnl->vgid = NULL;
			dm_list_add(vgnameids, &vgnl->list);
			return 1;
		}
	
		// 从lvmetad获取元数据
		if (lvmetad_used()) {
			/*
			 * This just gets the list of names/ids from lvmetad
			 * and does not populate lvmcache.
			 */
			lvmetad_get_vgnameids(cmd, vgnameids);
	
			if (include_internal) {
				dm_list_iterate_items(fmt, &cmd->formats) {
					if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
						log_error("vgnameid_list allocation failed.");
						return 0;
					}
	
					vgnl->vg_name = dm_pool_strdup(cmd->mem, fmt->orphan_vg_name);
					vgnl->vgid = NULL;
					dm_list_add(vgnameids, &vgnl->list);
				}
			}
		} else {
			// 从缓存获取元数据
			/*
			 * The non-lvmetad case. This function begins by calling
			 * lvmcache_label_scan() to populate lvmcache.
			 */
			lvmcache_get_vgnameids(cmd, include_internal, vgnameids);
		}
	
		return 1;
	}

从缓存获取元数据 (lib/cache/lvmcache.c)

	int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
				   struct dm_list *vgnameids)
	{
		struct vgnameid_list *vgnl;
		struct lvmcache_vginfo *vginfo;
	
		lvmcache_label_scan(cmd);
		
		// 遍历元数据缓存
		dm_list_iterate_items(vginfo, &_vginfos) {
			if (!include_internal && is_orphan_vg(vginfo->vgname))
				continue;
	
			if (!(vgnl = dm_pool_alloc(cmd->mem, sizeof(*vgnl)))) {
				log_error("vgnameid_list allocation failed.");
				return 0;
			}
	
			vgnl->vgid = dm_pool_strdup(cmd->mem, vginfo->vgid);
			vgnl->vg_name = dm_pool_strdup(cmd->mem, vginfo->vgname);
	
			if (!vgnl->vgid || !vgnl->vg_name) {
				log_error("vgnameid_list member allocation failed.");
				return 0;
			}
	
			dm_list_add(vgnameids, &vgnl->list);
		}
	
		return 1;
	}

根据VG遍历每个LV (tools/toollib.c : _process_lv_vgnameid_list)

	static int _process_lv_vgnameid_list(struct cmd_context *cmd, uint32_t read_flags,
					     struct dm_list *vgnameids_to_process,
					     struct dm_list *arg_vgnames,
					     struct dm_list *arg_lvnames,
					     struct dm_list *arg_tags,
					     struct processing_handle *handle,
					     process_single_lv_fn_t process_single_lv)
	{
		log_report_t saved_log_report_state = log_get_report_state();
		char uuid[64] __attribute__((aligned(8)));
		struct volume_group *vg;
		struct vgnameid_list *vgnl;
		struct dm_str_list *sl;
		struct dm_list *tags_arg;
		struct dm_list lvnames;
		uint32_t lockd_state = 0;
		const char *vg_name;
		const char *vg_uuid;
		const char *vgn;
		const char *lvn;
		int ret_max = ECMD_PROCESSED;
		int ret;
		int skip;
		int notfound;
		int already_locked;
		int do_report_ret_code = 1;
	
		log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_VG);
	
		// 遍历每个VG
		dm_list_iterate_items(vgnl, vgnameids_to_process) {
			vg_name = vgnl->vg_name;
			vg_uuid = vgnl->vgid;
			skip = 0;
			notfound = 0;
	
			uuid[0] = '\0';
			if (vg_uuid && !id_write_format((const struct id*)vg_uuid, uuid, sizeof(uuid)))
				stack;
	
			log_set_report_object_name_and_id(vg_name, uuid);
	
			if (sigint_caught()) {
				ret_max = ECMD_FAILED;
				goto_out;
			}
	
			/*
			 * arg_lvnames contains some elements that are just "vgname"
			 * which means process all lvs in the vg.  Other elements
			 * are "vgname/lvname" which means process only the select
			 * lvs in the vg.
			 */
			tags_arg = arg_tags;
			dm_list_init(&lvnames);	/* LVs to be processed in this VG */
	
			dm_list_iterate_items(sl, arg_lvnames) {
				vgn = sl->str;
				lvn = strchr(vgn, '/');
	
				if (!lvn && !strcmp(vgn, vg_name)) {
					/* Process all LVs in this VG */
					tags_arg = NULL;
					dm_list_init(&lvnames);
					break;
				}
				
				if (lvn && !strncmp(vgn, vg_name, strlen(vg_name)) &&
				    strlen(vg_name) == (size_t) (lvn - vgn)) {
					if (!str_list_add(cmd->mem, &lvnames,
							  dm_pool_strdup(cmd->mem, lvn + 1))) {
						log_error("strlist allocation failed.");
						ret_max = ECMD_FAILED;
						goto out;
					}
				}
			}
	
			log_very_verbose("Processing VG %s %s", vg_name, vg_uuid ? uuid : "");
	
			if (!lockd_vg(cmd, vg_name, NULL, 0, &lockd_state)) {
				ret_max = ECMD_FAILED;
				report_log_ret_code(ret_max);
				continue;
			}
	
			already_locked = lvmcache_vgname_is_locked(vg_name);
	
			// 读取VG的元数据
			vg = vg_read(cmd, vg_name, vg_uuid, read_flags, lockd_state);
			if (_ignore_vg(vg, vg_name, arg_vgnames, read_flags, &skip, &notfound)) {
				stack;
				ret_max = ECMD_FAILED;
				report_log_ret_code(ret_max);
				goto endvg;
			}
			if (skip || notfound)
				goto endvg;
	
			// 遍历并处理每个LV
			ret = process_each_lv_in_vg(cmd, vg, &lvnames, tags_arg, 0,
						    handle, process_single_lv);
			if (ret != ECMD_PROCESSED)
				stack;
			report_log_ret_code(ret);
			if (ret > ret_max)
				ret_max = ret;
	
			if (!already_locked)
				unlock_vg(cmd, vg, vg_name);
	endvg:
			release_vg(vg);
			if (!lockd_vg(cmd, vg_name, "un", 0, &lockd_state))
				stack;
			log_set_report_object_name_and_id(NULL, NULL);
		}
		do_report_ret_code = 0;
	out:
		if (do_report_ret_code)
			report_log_ret_code(ret_max);
		log_restore_report_state(saved_log_report_state);
		return ret_max;
	}

读取VG元数据 (lib/metadata/metadata.c)

	/*
	 * vg_read: High-level volume group metadata read function.
	 *
	 * vg_read_error() must be used on any handle returned to check for errors.
	 *
	 *  - metadata inconsistent and automatic correction failed: FAILED_INCONSISTENT
	 *  - VG is read-only: FAILED_READ_ONLY
	 *  - VG is EXPORTED, unless flags has READ_ALLOW_EXPORTED: FAILED_EXPORTED
	 *  - VG is not RESIZEABLE: FAILED_RESIZEABLE
	 *  - locking failed: FAILED_LOCKING
	 *
	 * On failures, all locks are released, unless one of the following applies:
	 *  - vgname_is_locked(lock_name) is true
	 * FIXME: remove the above 2 conditions if possible and make an error always
	 * release the lock.
	 *
	 * Volume groups are opened read-only unless flags contains READ_FOR_UPDATE.
	 *
	 * Checking for VG existence:
	 *
	 * FIXME: We want vg_read to attempt automatic recovery after acquiring a
	 * temporary write lock: if that fails, we bail out as usual, with failed &
	 * FAILED_INCONSISTENT. If it works, we are good to go. Code that's been in
	 * toollib just set lock_flags to LCK_VG_WRITE and called vg_read_internal with
	 * *consistent = 1.
	 */
	struct volume_group *vg_read(struct cmd_context *cmd, const char *vg_name,
				     const char *vgid, uint32_t read_flags, uint32_t lockd_state)
	{
		uint64_t status_flags = UINT64_C(0);
		uint32_t lock_flags = LCK_VG_READ;
	
		if (read_flags & READ_FOR_UPDATE) {
			status_flags |= EXPORTED_VG | LVM_WRITE;
			lock_flags = LCK_VG_WRITE;
		}
	
		if (read_flags & READ_ALLOW_EXPORTED)
			status_flags &= ~EXPORTED_VG;
	
		// 解析PV和VG的元数据,并更新缓存。
		return _vg_lock_and_read(cmd, vg_name, vgid, lock_flags, status_flags, read_flags, lockd_state);
	}

_vg_lock_and_read最终会调用_vg_read进行相关的源数据解析 (lib/metadata/metadata.c)

关键数据结构

format_type (lib/metadata/metadata-export.h)

	struct format_type {
		struct dm_list list;
		struct cmd_context *cmd;
		struct format_handler *ops;
		struct dm_list mda_ops; /* List of permissible mda ops. */
		struct labeller *labeller;
		const char *name;
		const char *alias;
		const char *orphan_vg_name;
		struct volume_group *orphan_vg; /* Only one ever exists. */
		uint32_t features;
		void *library;
		void *private;
	};

format_handler (lib/metadata/metadata.h)

	/*
	 * Ownership of objects passes to caller.
	 */
	struct format_handler {
		/*
		 * Scan any metadata areas that aren't referenced in PV labels
		 */
		int (*scan) (const struct format_type * fmt, const char *vgname);
	
		/*
		 * Return PV with given path.
		 */
		int (*pv_read) (const struct format_type * fmt, const char *pv_name,
				struct physical_volume * pv, int scan_label_only);
	
		/*
		 * Initialise a new PV.
		 */
		int (*pv_initialise) (const struct format_type * fmt,
				      struct pv_create_args *pva,
				      struct physical_volume * pv);
	
		/*
		 * Tweak an already filled out a pv ready for importing into a
		 * vg.  eg. pe_count is format specific.
		 */
		int (*pv_setup) (const struct format_type * fmt,
				 struct physical_volume * pv,
				 struct volume_group * vg);
	
		/*
		 * Add metadata area to a PV. Changes will take effect on pv_write.
		 */
		int (*pv_add_metadata_area) (const struct format_type * fmt,
					     struct physical_volume * pv,
					     int pe_start_locked,
					     unsigned metadata_index,
					     uint64_t metadata_size,
					     unsigned metadata_ignored);
	
		/*
		 * Remove metadata area from a PV. Changes will take effect on pv_write.
		 */
		int (*pv_remove_metadata_area) (const struct format_type *fmt,
						struct physical_volume *pv,
						unsigned metadata_index);
	
		/*
		 * Recalculate the PV size taking into account any existing metadata areas.
		 */
		int (*pv_resize) (const struct format_type *fmt,
				  struct physical_volume *pv,
				  struct volume_group *vg,
				  uint64_t size);
	
		/*
		 * Write a PV structure to disk. Fails if the PV is in a VG ie
		 * pv->vg_name must be a valid orphan VG name
		 */
		int (*pv_write) (const struct format_type * fmt,
				 struct physical_volume * pv);
	
		/*
		 * Check if PV needs rewriting. This is used to check whether there are any
		 * format-specific changes  before actually writing the PV (by calling pv_write).
		 * With this, we can call pv_write conditionally only if it's really needed.
		 */
		int (*pv_needs_rewrite) (const struct format_type *fmt,
					 struct physical_volume *pv,
					 int *needs_rewrite);
	
		/*
		 * Tweak an already filled out a lv eg, check there
		 * aren't too many extents.
		 */
		int (*lv_setup) (struct format_instance * fi,
				 struct logical_volume * lv);
	
		/*
		 * Tweak an already filled out vg.  eg, max_pv is format
		 * specific.
		 */
		int (*vg_setup) (struct format_instance * fi, struct volume_group * vg);
	
		/*
		 * Check whether particular segment type is supported.
		 */
		int (*segtype_supported) (struct format_instance *fid,
					  const struct segment_type *segtype);
	
		/*
		 * Create format instance with a particular metadata area
		 */
		struct format_instance *(*create_instance) (const struct format_type *fmt,
							    const struct format_instance_ctx *fic);
	
		/*
		 * Destructor for format instance
		 */
		void (*destroy_instance) (struct format_instance * fid);
	
		/*
		 * Destructor for format type
		 */
		void (*destroy) (struct format_type * fmt);
	};

format_instance (lib/metadata/metadata-export.h)

	struct format_instance {
		unsigned ref_count;	/* Refs to this fid from VG and PV structs */
		struct dm_pool *mem;
	
		uint32_t type;
		const struct format_type *fmt;
	
		/*
		 * Each mda in a vg is on exactly one of the below lists.
		 * MDAs on the 'in_use' list will be read from / written to
		 * disk, while MDAs on the 'ignored' list will not be read
		 * or written to.
		 */
		/* FIXME: Try to use the index only. Remove these lists. */
		struct dm_list metadata_areas_in_use;
		struct dm_list metadata_areas_ignored;
		struct dm_hash_table *metadata_areas_index;
	
		void *private;
	};

© 著作权归作者所有

共有 人打赏支持
LastRitter
粉丝 36
博文 37
码字总数 171159
作品 0
武汉
高级程序员
Raid5两块硬盘离线解决方案 -阵列数据恢复案例

#服务器数据恢复背景描述: 需要进行数据恢复的磁盘阵列是两组分别由4块600G容量的SAS硬盘组成的raid5磁盘阵列,ext3文件系统、lvm结构。 磁盘阵列中1号硬盘离线,热备盘启动同步,在同步过程...

宋国建
04/11
0
0
通用线程: 学习 Linux LVM

通用线程: 学习 Linux LVM “逻辑卷管理”为存储器管理带来的魔力 Daniel Robbins (drobbins@gentoo.org), 总裁兼 CEO, Gentoo Technologies, Inc. 简介: 在本文中,Daniel 向您介绍了 Linu...

Start-up
2012/05/28
0
0
服务器raid5两块硬盘离线vxfs文件系统恢复数据方法

服务器数据恢复故障描述 客户的服务器共有8块450GB SAS硬盘,其中7块硬盘组成一个RAID5阵列,1块热备盘。阵列中2块硬盘损坏并离线,导致RAID5阵列瘫痪,进而影响上层LUN无法正常使用。硬盘无...

宋国建
04/19
0
0
linux磁盘管理系列三:LVM的使用

linux磁盘管理系列三:LVM的使用 LVM是什么 LVM是Linux操作系统的逻辑卷管理器。 现在有两个Linux版本的LVM,分别是 LVM1,LVM2。LVM1是一种已经被认为稳定了几年的成熟产品,LVM2 是最新最好的...

阿晨993
08/31
0
0
通过拼数据库碎片的方式恢复虚拟机磁盘文件丢失问题

背景概述 由于服务器突然断电,造成我公司Xen Server服务器中一台VPS(即Xen Server虚拟机)不可用,虚拟磁盘文件丢失。硬件环境是Dell 720服务器配戴一张H710P的RAID卡,由4块希捷2T STAT硬...

宋国建
2017/08/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Bash脚本输入参数的使用

Bash脚本输入参数的使用 Bash脚本传入的参数,在脚本中以位置的形式进行访问,即 $n 。例如,$1 为执行脚本的第一个参数,$2 为执行脚本的第二个参数。另外Bash中还提供了一些其它的便捷操作...

小陶小陶
30分钟前
2
0
多线程场景下,触发OOM的线程是否会影响其他线程的工作

public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { int i = 0;......

麦馍
34分钟前
2
0
模拟Dubbo的zookeeper一致性Hash发现

接之前一篇<手写zookeeper来模拟dubbo的注册/发现>,使用一致性Hash来进行查找需要寻找的服务. Hash处理接口 public interface HashFunc { public Long hash(Object key);} 一致性Has...

算法之名
46分钟前
13
0
#mysql50# not Exist

data 目录有中文名称,可能是复制的"复件...."

少年不搬砖老大徒伤悲
59分钟前
2
0
在yii2中,让你action参数支持POST数据的小方法

我们先来看一段代码 class RaController extends Controller { public $enableCsrfValidation = false; public function actionSay($username = '',$city = ''){ echo "{$......

阿北2017
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部