获取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, ¬found)) {
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;
};