mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-29 13:44:18 +03:00
Compare commits
4 Commits
2018-05-17
...
dev-mcsont
Author | SHA1 | Date | |
---|---|---|---|
|
604b4bef61 | ||
|
9e6313074a | ||
|
ee7bf5634e | ||
|
a85387696e |
51
.gitignore
vendored
51
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
*.5
|
||||
*.7
|
||||
*.8
|
||||
*.8_gen
|
||||
*.a
|
||||
*.d
|
||||
*.o
|
||||
@@ -31,53 +30,3 @@ make.tmpl
|
||||
/cscope.out
|
||||
/tags
|
||||
/tmp/
|
||||
|
||||
|
||||
tools/man-generator
|
||||
tools/man-generator.c
|
||||
|
||||
test/lib/lvchange
|
||||
test/lib/lvconvert
|
||||
test/lib/lvcreate
|
||||
test/lib/lvdisplay
|
||||
test/lib/lvextend
|
||||
test/lib/lvmconfig
|
||||
test/lib/lvmdiskscan
|
||||
test/lib/lvmsadc
|
||||
test/lib/lvmsar
|
||||
test/lib/lvreduce
|
||||
test/lib/lvremove
|
||||
test/lib/lvrename
|
||||
test/lib/lvresize
|
||||
test/lib/lvs
|
||||
test/lib/lvscan
|
||||
test/lib/pvchange
|
||||
test/lib/pvck
|
||||
test/lib/pvcreate
|
||||
test/lib/pvdisplay
|
||||
test/lib/pvmove
|
||||
test/lib/pvremove
|
||||
test/lib/pvresize
|
||||
test/lib/pvs
|
||||
test/lib/pvscan
|
||||
test/lib/vgcfgbackup
|
||||
test/lib/vgcfgrestore
|
||||
test/lib/vgchange
|
||||
test/lib/vgck
|
||||
test/lib/vgconvert
|
||||
test/lib/vgcreate
|
||||
test/lib/vgdisplay
|
||||
test/lib/vgexport
|
||||
test/lib/vgextend
|
||||
test/lib/vgimport
|
||||
test/lib/vgimportclone
|
||||
test/lib/vgmerge
|
||||
test/lib/vgmknodes
|
||||
test/lib/vgreduce
|
||||
test/lib/vgremove
|
||||
test/lib/vgrename
|
||||
test/lib/vgs
|
||||
test/lib/vgscan
|
||||
test/lib/vgsplit
|
||||
|
||||
test/unit/unit-test
|
||||
|
40
Makefile.in
40
Makefile.in
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -18,7 +18,7 @@ top_builddir = @top_builddir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
|
||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts device_mapper tools
|
||||
SUBDIRS = conf daemons include lib libdaemon libdm man scripts tools
|
||||
|
||||
ifeq ("@UDEV_RULES@", "yes")
|
||||
SUBDIRS += udev
|
||||
@@ -43,7 +43,8 @@ endif
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = conf include man test scripts \
|
||||
libdaemon lib tools daemons libdm \
|
||||
udev po liblvm python device_mapper
|
||||
udev po liblvm python \
|
||||
unit-tests/datastruct unit-tests/mm unit-tests/regex
|
||||
tools.distclean: test.distclean
|
||||
endif
|
||||
DISTCLEAN_DIRS += lcov_reports*
|
||||
@@ -61,9 +62,6 @@ po: tools daemons
|
||||
man: tools
|
||||
all_man: tools
|
||||
scripts: liblvm libdm
|
||||
test: tools daemons
|
||||
unit-test: lib
|
||||
run-unit-test: unit-test
|
||||
|
||||
lib.device-mapper: include.device-mapper
|
||||
libdm.device-mapper: include.device-mapper
|
||||
@@ -99,7 +97,7 @@ endif
|
||||
DISTCLEAN_TARGETS += cscope.out
|
||||
CLEAN_DIRS += autom4te.cache
|
||||
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit-test run-unit-test: test
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all
|
||||
$(MAKE) -C test $(@)
|
||||
|
||||
conf.generate man.generate: tools
|
||||
@@ -148,7 +146,7 @@ install_system_dirs:
|
||||
$(INSTALL_ROOT_DIR) $(DESTDIR)$(DEFAULT_RUN_DIR)
|
||||
$(INSTALL_ROOT_DATA) /dev/null $(DESTDIR)$(DEFAULT_CACHE_DIR)/.cache
|
||||
|
||||
install_initscripts:
|
||||
install_initscripts:
|
||||
$(MAKE) -C scripts install_initscripts
|
||||
|
||||
install_systemd_generators:
|
||||
@@ -171,7 +169,6 @@ install_tmpfiles_configuration:
|
||||
|
||||
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
|
||||
libdaemon/client.info libdaemon/server.info \
|
||||
test/unit.info \
|
||||
daemons/clvmd.info \
|
||||
daemons/dmeventd.info \
|
||||
daemons/lvmetad.info \
|
||||
@@ -214,6 +211,31 @@ endif
|
||||
|
||||
endif
|
||||
|
||||
ifeq ("$(TESTING)", "yes")
|
||||
# testing and report generation
|
||||
RUBY=ruby1.9 -Ireport-generators/lib -Ireport-generators/test
|
||||
|
||||
.PHONY: unit-test ruby-test test-programs
|
||||
|
||||
# FIXME: put dependencies on libdm and liblvm
|
||||
# FIXME: Should be handled by Makefiles in subdirs, not here at top level.
|
||||
test-programs:
|
||||
cd unit-tests/regex && $(MAKE)
|
||||
cd unit-tests/datastruct && $(MAKE)
|
||||
cd unit-tests/mm && $(MAKE)
|
||||
|
||||
unit-test: test-programs
|
||||
$(RUBY) report-generators/unit_test.rb $(shell find . -name TESTS)
|
||||
$(RUBY) report-generators/title_page.rb
|
||||
|
||||
memcheck: test-programs
|
||||
$(RUBY) report-generators/memcheck.rb $(shell find . -name TESTS)
|
||||
$(RUBY) report-generators/title_page.rb
|
||||
|
||||
ruby-test:
|
||||
$(RUBY) report-generators/test/ts.rb
|
||||
endif
|
||||
|
||||
ifneq ($(shell which ctags),)
|
||||
.PHONY: tags
|
||||
tags:
|
||||
|
52
WHATS_NEW
52
WHATS_NEW
@@ -1,57 +1,5 @@
|
||||
Version 2.02.178 -
|
||||
=====================================
|
||||
Remove lvm1 and pool format handling and add filter to ignore them.
|
||||
Move some filter checks to after disks are read.
|
||||
Rework disk scanning and when it is used.
|
||||
Add new io layer and shift code to using it.
|
||||
lvconvert: don't return success on degraded -m raid1 conversion
|
||||
--enable-testing switch for ./configure has been removed.
|
||||
--with-snapshots switch for ./configure has been removed.
|
||||
--with-mirrors switch for ./configure has been removed.
|
||||
--with-raid switch for ./configure has been removed.
|
||||
--with-thin switch for ./configure has been removed.
|
||||
--with-cache switch for ./configure has been removed.
|
||||
Extend validation of region_size for mirror segment.
|
||||
Reload whole device stack when reinitilizing mirror log.
|
||||
Mirrors without monitoring are WARNING and not blocking on error.
|
||||
Detect too big region_size with clustered mirrors.
|
||||
Fix evaluation of maximal region size for mirror log.
|
||||
Enhance mirror log size estimation and use smaller size when possible.
|
||||
Fix incorrect mirror log size calculation on 32bit arch.
|
||||
Enhnace preloading tree creating.
|
||||
Fix regression on acceptance of any LV on lvconvert.
|
||||
Restore usability of thin LV to be again external origin for another thin.
|
||||
Keep systemd vars on change event in 69-dm-lvm-metad.rules for systemd reload.
|
||||
Write systemd and non-systemd rule in 69-dm-lvm-metad.rules, GOTO active one.
|
||||
Add test for activation/volume_list (Sub)LV remnants.
|
||||
Disallow usage of cache format 2 with mq cache policy.
|
||||
Again accept striped LV as COW LV with lvconvert -s (2.02.169).
|
||||
Fix raid target version testing for supported features.
|
||||
Allow activation of pools when thin/cache_check tool is missing.
|
||||
Remove RaidLV on creation failure when rmeta devices can't be activated.
|
||||
Add prioritized_section() to restore cookie boundaries (2.02.177).
|
||||
Enhance error messages when read error happens.
|
||||
Enhance mirror log initialization for old mirror target.
|
||||
Skip private crypto and stratis devices.
|
||||
Skip frozen raid devices from scanning.
|
||||
Activate RAID SubLVs on read_only_volume_list readwrite
|
||||
Offer convenience type raid5_n converting to raid10
|
||||
Automatically avoid reading invalid snapshots during device scan.
|
||||
Ensure COW device is writable even for read-only thick snapshots.
|
||||
Support activation of component LVs in read-only mode.
|
||||
Extend internal library to recognize and work with component LV.
|
||||
Skip duplicate check for active LV when prompting for its removal.
|
||||
Activate correct lock holding LV when it is cached.
|
||||
Do not modify archived metadata when removing striped raid.
|
||||
Fix memleak on error path when obtaining lv_raid_data_offset.
|
||||
Fix compatibility size test of extended external origin.
|
||||
Add external_origin visiting in for_each_sub_lv().
|
||||
Ensure cluster commands drop their device cache before locking VG.
|
||||
Do not report LV as remotely active when it's locally exclusive in cluster.
|
||||
Add deprecate messages for usage of mirrors with mirrorlog.
|
||||
Separate reporting of monitoring status and error status.
|
||||
Improve validation of created strings in vgimportclone.
|
||||
Add missing initialisation of mem pool in systemd generator.
|
||||
Do not reopen output streams for multithreaded users of liblvm.
|
||||
Use versionsort to fix archive file expiry beyond 100000 files.
|
||||
Add devices/use_aio, aio_max, aio_memory to configure AIO limits.
|
||||
|
@@ -1,12 +1,5 @@
|
||||
Version 1.02.147 -
|
||||
=====================================
|
||||
Reuse uname() result for mirror target.
|
||||
Recognize also mounted btrfs through dm_device_has_mounted_fs().
|
||||
Add missing log_error() into dm_stats_populate() returning 0.
|
||||
Avoid calling dm_stats_populat() for DM devices without any stats regions.
|
||||
Support DM_DEBUG_WITH_LINE_NUMBERS envvar for debug msg with source:line.
|
||||
Configured command for thin pool threshold handling gets whole environment.
|
||||
Fix tests for failing dm_snprintf() in stats code.
|
||||
Parsing mirror status accepts 'userspace' keyword in status.
|
||||
Introduce dm_malloc_aligned for page alignment of buffers.
|
||||
|
||||
|
@@ -155,7 +155,7 @@ AC_DEFUN([AC_TRY_LDFLAGS],
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
serial 3
|
||||
#serial 3
|
||||
|
||||
AC_DEFUN([AX_GCC_BUILTIN], [
|
||||
AS_VAR_PUSHDEF([ac_var], [ax_cv_have_$1])
|
||||
|
210
aclocal.m4
vendored
210
aclocal.m4
vendored
@@ -69,63 +69,32 @@ AC_DEFUN([AX_PYTHON_MODULE],[
|
||||
fi
|
||||
])
|
||||
|
||||
dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
dnl serial 11 (pkg-config-0.29)
|
||||
dnl
|
||||
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
|
||||
dnl
|
||||
dnl This program is free software; you can redistribute it and/or modify
|
||||
dnl it under the terms of the GNU General Public License as published by
|
||||
dnl the Free Software Foundation; either version 2 of the License, or
|
||||
dnl (at your option) any later version.
|
||||
dnl
|
||||
dnl This program is distributed in the hope that it will be useful, but
|
||||
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
dnl General Public License for more details.
|
||||
dnl
|
||||
dnl You should have received a copy of the GNU General Public License
|
||||
dnl along with this program; if not, write to the Free Software
|
||||
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
dnl 02111-1307, USA.
|
||||
dnl
|
||||
dnl As a special exception to the GNU General Public License, if you
|
||||
dnl distribute this file as part of a program that contains a
|
||||
dnl configuration script generated by Autoconf, you may include it under
|
||||
dnl the same distribution terms that you use for the rest of that
|
||||
dnl program.
|
||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
# serial 1 (pkg-config-0.24)
|
||||
#
|
||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
dnl PKG_PREREQ(MIN-VERSION)
|
||||
dnl -----------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Verify that the version of the pkg-config macros are at least
|
||||
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
|
||||
dnl installed version of pkg-config, this checks the developer's version
|
||||
dnl of pkg.m4 when generating configure.
|
||||
dnl
|
||||
dnl To ensure that this macro is defined, also add:
|
||||
dnl m4_ifndef([PKG_PREREQ],
|
||||
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
|
||||
dnl
|
||||
dnl See the "Since" comment for each macro you use to see what version
|
||||
dnl of the macros you require.
|
||||
m4_defun([PKG_PREREQ],
|
||||
[m4_define([PKG_MACROS_VERSION], [0.29])
|
||||
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
|
||||
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
||||
])dnl PKG_PREREQ
|
||||
|
||||
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
dnl ----------------------------------
|
||||
dnl Since: 0.16
|
||||
dnl
|
||||
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
|
||||
dnl first found in the path. Checks that the version of pkg-config found
|
||||
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
|
||||
dnl used since that's the first version where most current features of
|
||||
dnl pkg-config existed.
|
||||
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
# ----------------------------------
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||
@@ -147,19 +116,18 @@ if test -n "$PKG_CONFIG"; then
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
fi[]dnl
|
||||
])dnl PKG_PROG_PKG_CONFIG
|
||||
])# PKG_PROG_PKG_CONFIG
|
||||
|
||||
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------------------------------
|
||||
dnl Since: 0.18
|
||||
dnl
|
||||
dnl Check to see whether a particular set of modules exists. Similar to
|
||||
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
dnl
|
||||
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
dnl only at the first occurence in configure.ac, so if the first place
|
||||
dnl it's called might be skipped (such as if it is within an "if", you
|
||||
dnl have to call PKG_CHECK_EXISTS manually
|
||||
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# Check to see whether a particular set of modules exists. Similar
|
||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
#
|
||||
# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
# only at the first occurence in configure.ac, so if the first place
|
||||
# it's called might be skipped (such as if it is within an "if", you
|
||||
# have to call PKG_CHECK_EXISTS manually
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
@@ -169,10 +137,8 @@ m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
dnl ---------------------------------------------
|
||||
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
|
||||
dnl pkg_failed based on the result.
|
||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
# ---------------------------------------------
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
@@ -184,11 +150,10 @@ m4_define([_PKG_CONFIG],
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])dnl _PKG_CONFIG
|
||||
])# _PKG_CONFIG
|
||||
|
||||
dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
dnl ---------------------------
|
||||
dnl Internal check to see if pkg-config supports short errors.
|
||||
# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
# -----------------------------
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
@@ -196,17 +161,19 @@ if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||
])# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl --------------------------------------------------------------
|
||||
dnl Since: 0.4.0
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
# [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
#
|
||||
# Note that if there is a possibility the first call to
|
||||
# PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
#
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
@@ -260,40 +227,16 @@ else
|
||||
AC_MSG_RESULT([yes])
|
||||
$3
|
||||
fi[]dnl
|
||||
])dnl PKG_CHECK_MODULES
|
||||
])# PKG_CHECK_MODULES
|
||||
|
||||
|
||||
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
dnl [ACTION-IF-NOT-FOUND])
|
||||
dnl ---------------------------------------------------------------------
|
||||
dnl Since: 0.29
|
||||
dnl
|
||||
dnl Checks for existence of MODULES and gathers its build flags with
|
||||
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
|
||||
dnl and VARIABLE-PREFIX_LIBS from --libs.
|
||||
dnl
|
||||
dnl Note that if there is a possibility the first call to
|
||||
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
|
||||
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
|
||||
dnl configure.ac.
|
||||
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
_save_PKG_CONFIG=$PKG_CONFIG
|
||||
PKG_CONFIG="$PKG_CONFIG --static"
|
||||
PKG_CHECK_MODULES($@)
|
||||
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
|
||||
])dnl PKG_CHECK_MODULES_STATIC
|
||||
|
||||
|
||||
dnl PKG_INSTALLDIR([DIRECTORY])
|
||||
dnl -------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable pkgconfigdir as the location where a module
|
||||
dnl should install pkg-config .pc files. By default the directory is
|
||||
dnl $libdir/pkgconfig, but the default can be changed by passing
|
||||
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
dnl parameter.
|
||||
# PKG_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable pkgconfigdir as the location where a module
|
||||
# should install pkg-config .pc files. By default the directory is
|
||||
# $libdir/pkgconfig, but the default can be changed by passing
|
||||
# DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||
# parameter.
|
||||
AC_DEFUN([PKG_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
@@ -304,18 +247,16 @@ AC_ARG_WITH([pkgconfigdir],
|
||||
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
])dnl PKG_INSTALLDIR
|
||||
]) dnl PKG_INSTALLDIR
|
||||
|
||||
|
||||
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
|
||||
dnl --------------------------------
|
||||
dnl Since: 0.27
|
||||
dnl
|
||||
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
dnl module should install arch-independent pkg-config .pc files. By
|
||||
dnl default the directory is $datadir/pkgconfig, but the default can be
|
||||
dnl changed by passing DIRECTORY. The user can override through the
|
||||
dnl --with-noarch-pkgconfigdir parameter.
|
||||
# PKG_NOARCH_INSTALLDIR(DIRECTORY)
|
||||
# -------------------------
|
||||
# Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||
# module should install arch-independent pkg-config .pc files. By
|
||||
# default the directory is $datadir/pkgconfig, but the default can be
|
||||
# changed by passing DIRECTORY. The user can override through the
|
||||
# --with-noarch-pkgconfigdir parameter.
|
||||
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||
m4_pushdef([pkg_description],
|
||||
@@ -326,15 +267,13 @@ AC_ARG_WITH([noarch-pkgconfigdir],
|
||||
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||
m4_popdef([pkg_default])
|
||||
m4_popdef([pkg_description])
|
||||
])dnl PKG_NOARCH_INSTALLDIR
|
||||
]) dnl PKG_NOARCH_INSTALLDIR
|
||||
|
||||
|
||||
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
dnl -------------------------------------------
|
||||
dnl Since: 0.28
|
||||
dnl
|
||||
dnl Retrieves the value of the pkg-config variable for the given module.
|
||||
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# -------------------------------------------
|
||||
# Retrieves the value of the pkg-config variable for the given module.
|
||||
AC_DEFUN([PKG_CHECK_VAR],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||
@@ -343,7 +282,7 @@ _PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])dnl PKG_CHECK_VAR
|
||||
])# PKG_CHECK_VAR
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
@@ -597,4 +536,5 @@ AC_DEFUN([AM_RUN_LOG],
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
||||
(exit $ac_status); }])
|
||||
|
||||
|
||||
m4_include([acinclude.m4])
|
||||
|
@@ -1,570 +0,0 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#include "radix-tree.h"
|
||||
|
||||
#include "base/memory/container_of.h"
|
||||
#include "base/memory/zalloc.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
enum node_type {
|
||||
UNSET = 0,
|
||||
VALUE,
|
||||
VALUE_CHAIN,
|
||||
PREFIX_CHAIN,
|
||||
NODE4,
|
||||
NODE16,
|
||||
NODE48,
|
||||
NODE256
|
||||
};
|
||||
|
||||
struct value {
|
||||
enum node_type type;
|
||||
union radix_value value;
|
||||
};
|
||||
|
||||
// This is used for entries that have a key which is a prefix of another key.
|
||||
struct value_chain {
|
||||
union radix_value value;
|
||||
struct value child;
|
||||
};
|
||||
|
||||
struct prefix_chain {
|
||||
struct value child;
|
||||
unsigned len;
|
||||
uint8_t prefix[0];
|
||||
};
|
||||
|
||||
struct node4 {
|
||||
uint32_t nr_entries;
|
||||
uint8_t keys[4];
|
||||
struct value values[4];
|
||||
};
|
||||
|
||||
struct node16 {
|
||||
uint32_t nr_entries;
|
||||
uint8_t keys[16];
|
||||
struct value values[16];
|
||||
};
|
||||
|
||||
struct node48 {
|
||||
uint32_t nr_entries;
|
||||
uint8_t keys[256];
|
||||
struct value values[48];
|
||||
};
|
||||
|
||||
struct node256 {
|
||||
struct value values[256];
|
||||
};
|
||||
|
||||
struct radix_tree {
|
||||
unsigned nr_entries;
|
||||
struct value root;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct radix_tree *radix_tree_create(void)
|
||||
{
|
||||
struct radix_tree *rt = malloc(sizeof(*rt));
|
||||
|
||||
if (rt) {
|
||||
rt->nr_entries = 0;
|
||||
rt->root.type = UNSET;
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
static void _free_node(struct value v, radix_value_dtr dtr, void *context)
|
||||
{
|
||||
unsigned i;
|
||||
struct value_chain *vc;
|
||||
struct prefix_chain *pc;
|
||||
struct node4 *n4;
|
||||
struct node16 *n16;
|
||||
struct node48 *n48;
|
||||
struct node256 *n256;
|
||||
|
||||
switch (v.type) {
|
||||
case UNSET:
|
||||
break;
|
||||
|
||||
case VALUE:
|
||||
if (dtr)
|
||||
dtr(context, v.value);
|
||||
break;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = v.value.ptr;
|
||||
if (dtr)
|
||||
dtr(context, vc->value);
|
||||
_free_node(vc->child, dtr, context);
|
||||
free(vc);
|
||||
break;
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
pc = v.value.ptr;
|
||||
_free_node(pc->child, dtr, context);
|
||||
free(pc);
|
||||
break;
|
||||
|
||||
case NODE4:
|
||||
n4 = (struct node4 *) v.value.ptr;
|
||||
for (i = 0; i < n4->nr_entries; i++)
|
||||
_free_node(n4->values[i], dtr, context);
|
||||
free(n4);
|
||||
break;
|
||||
|
||||
case NODE16:
|
||||
n16 = (struct node16 *) v.value.ptr;
|
||||
for (i = 0; i < n16->nr_entries; i++)
|
||||
_free_node(n16->values[i], dtr, context);
|
||||
free(n16);
|
||||
break;
|
||||
|
||||
case NODE48:
|
||||
n48 = (struct node48 *) v.value.ptr;
|
||||
for (i = 0; i < n48->nr_entries; i++)
|
||||
_free_node(n48->values[i], dtr, context);
|
||||
free(n48);
|
||||
break;
|
||||
|
||||
case NODE256:
|
||||
n256 = (struct node256 *) v.value.ptr;
|
||||
for (i = 0; i < 256; i++)
|
||||
_free_node(n256->values[i], dtr, context);
|
||||
free(n256);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void radix_tree_destroy(struct radix_tree *rt, radix_value_dtr dtr, void *context)
|
||||
{
|
||||
_free_node(rt->root, dtr, context);
|
||||
free(rt);
|
||||
}
|
||||
|
||||
static bool _insert(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv);
|
||||
|
||||
static bool _insert_unset(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
unsigned len = ke - kb;
|
||||
|
||||
if (!len) {
|
||||
// value
|
||||
v->type = VALUE;
|
||||
v->value = rv;
|
||||
} else {
|
||||
// prefix -> value
|
||||
struct prefix_chain *pc = zalloc(sizeof(*pc) + len);
|
||||
if (!pc)
|
||||
return false;
|
||||
|
||||
pc->child.type = VALUE;
|
||||
pc->child.value = rv;
|
||||
pc->len = len;
|
||||
memcpy(pc->prefix, kb, len);
|
||||
v->type = PREFIX_CHAIN;
|
||||
v->value.ptr = pc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_value(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
unsigned len = ke - kb;
|
||||
|
||||
if (!len)
|
||||
// overwrite
|
||||
v->value = rv;
|
||||
|
||||
else {
|
||||
// value_chain -> value
|
||||
struct value_chain *vc = zalloc(sizeof(*vc));
|
||||
if (!vc)
|
||||
return false;
|
||||
|
||||
vc->value = v->value;
|
||||
if (!_insert(&vc->child, kb, ke, rv)) {
|
||||
free(vc);
|
||||
return false;
|
||||
}
|
||||
|
||||
v->type = VALUE_CHAIN;
|
||||
v->value.ptr = vc;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_value_chain(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct value_chain *vc = v->value.ptr;
|
||||
return _insert(&vc->child, kb, ke, rv);
|
||||
}
|
||||
|
||||
static unsigned min(unsigned lhs, unsigned rhs)
|
||||
{
|
||||
if (lhs <= rhs)
|
||||
return lhs;
|
||||
else
|
||||
return rhs;
|
||||
}
|
||||
|
||||
static bool _insert_prefix_chain(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct prefix_chain *pc = v->value.ptr;
|
||||
|
||||
if (*kb == pc->prefix[0]) {
|
||||
// There's a common prefix let's split the chain into two and
|
||||
// recurse.
|
||||
struct prefix_chain *pc2;
|
||||
unsigned i, len = min(pc->len, ke - kb);
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (kb[i] != pc->prefix[i])
|
||||
break;
|
||||
|
||||
pc2 = zalloc(sizeof(*pc2) + pc->len - i);
|
||||
pc2->len = pc->len - i;
|
||||
memmove(pc2->prefix, pc->prefix + i, pc2->len);
|
||||
pc2->child = pc->child;
|
||||
|
||||
// FIXME: this trashes pc so we can't back out
|
||||
pc->child.type = PREFIX_CHAIN;
|
||||
pc->child.value.ptr = pc2;
|
||||
pc->len = i;
|
||||
|
||||
if (!_insert(&pc->child, kb + i, ke, rv)) {
|
||||
free(pc2);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Stick an n4 in front.
|
||||
struct node4 *n4 = zalloc(sizeof(*n4));
|
||||
if (!n4)
|
||||
return false;
|
||||
|
||||
n4->keys[0] = *kb;
|
||||
if (!_insert(n4->values, kb + 1, ke, rv)) {
|
||||
free(n4);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pc->len) {
|
||||
n4->keys[1] = pc->prefix[0];
|
||||
if (pc->len == 1) {
|
||||
n4->values[1] = pc->child;
|
||||
free(pc);
|
||||
} else {
|
||||
memmove(pc->prefix, pc->prefix + 1, pc->len - 1);
|
||||
pc->len--;
|
||||
n4->values[1] = *v;
|
||||
}
|
||||
n4->nr_entries = 2;
|
||||
} else
|
||||
n4->nr_entries = 1;
|
||||
|
||||
v->type = NODE4;
|
||||
v->value.ptr = n4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node4(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node4 *n4 = v->value.ptr;
|
||||
if (n4->nr_entries == 4) {
|
||||
struct node16 *n16 = zalloc(sizeof(*n16));
|
||||
if (!n16)
|
||||
return false;
|
||||
|
||||
n16->nr_entries = 5;
|
||||
memcpy(n16->keys, n4->keys, sizeof(n4->keys));
|
||||
memcpy(n16->values, n4->values, sizeof(n4->values));
|
||||
|
||||
n16->keys[4] = *kb;
|
||||
if (!_insert(n16->values + 4, kb + 1, ke, rv)) {
|
||||
free(n16);
|
||||
return false;
|
||||
}
|
||||
free(n4);
|
||||
v->type = NODE16;
|
||||
v->value.ptr = n16;
|
||||
} else {
|
||||
n4 = v->value.ptr;
|
||||
if (!_insert(n4->values + n4->nr_entries, kb + 1, ke, rv))
|
||||
return false;
|
||||
|
||||
n4->keys[n4->nr_entries] = *kb;
|
||||
n4->nr_entries++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node16(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node16 *n16 = v->value.ptr;
|
||||
|
||||
if (n16->nr_entries == 16) {
|
||||
unsigned i;
|
||||
struct node48 *n48 = zalloc(sizeof(*n48));
|
||||
|
||||
if (!n48)
|
||||
return false;
|
||||
|
||||
n48->nr_entries = 17;
|
||||
memset(n48->keys, 48, sizeof(n48->keys));
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
n48->keys[n16->keys[i]] = i;
|
||||
n48->values[i] = n16->values[i];
|
||||
}
|
||||
|
||||
n48->keys[*kb] = 16;
|
||||
if (!_insert(n48->values + 16, kb + 1, ke, rv)) {
|
||||
free(n48);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(n16);
|
||||
v->type = NODE48;
|
||||
v->value.ptr = n48;
|
||||
} else {
|
||||
if (!_insert(n16->values + n16->nr_entries, kb + 1, ke, rv))
|
||||
return false;
|
||||
n16->keys[n16->nr_entries] = *kb;
|
||||
n16->nr_entries++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node48(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node48 *n48 = v->value.ptr;
|
||||
if (n48->nr_entries == 48) {
|
||||
unsigned i;
|
||||
struct node256 *n256 = zalloc(sizeof(*n256));
|
||||
if (!n256)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (n48->keys[i] >= 48)
|
||||
continue;
|
||||
|
||||
n256->values[i] = n48->values[n48->keys[i]];
|
||||
}
|
||||
|
||||
if (!_insert(n256->values + *kb, kb + 1, ke, rv)) {
|
||||
free(n256);
|
||||
return false;
|
||||
}
|
||||
|
||||
free(n48);
|
||||
v->type = NODE256;
|
||||
v->value.ptr = n256;
|
||||
|
||||
} else {
|
||||
if (!_insert(n48->values + n48->nr_entries, kb + 1, ke, rv))
|
||||
return false;
|
||||
|
||||
n48->keys[*kb] = n48->nr_entries;
|
||||
n48->nr_entries++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _insert_node256(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct node256 *n256 = v->value.ptr;
|
||||
if (!_insert(n256->values + *kb, kb + 1, ke, rv)) {
|
||||
n256->values[*kb].type = UNSET;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: the tree should not be touched if insert fails (eg, OOM)
|
||||
static bool _insert(struct value *v, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
if (kb == ke) {
|
||||
if (v->type == UNSET) {
|
||||
v->type = VALUE;
|
||||
v->value = rv;
|
||||
|
||||
} else if (v->type == VALUE) {
|
||||
v->value = rv;
|
||||
|
||||
} else {
|
||||
struct value_chain *vc = zalloc(sizeof(*vc));
|
||||
if (!vc)
|
||||
return false;
|
||||
|
||||
vc->value = rv;
|
||||
vc->child = *v;
|
||||
v->type = VALUE_CHAIN;
|
||||
v->value.ptr = vc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (v->type) {
|
||||
case UNSET:
|
||||
return _insert_unset(v, kb, ke, rv);
|
||||
|
||||
case VALUE:
|
||||
return _insert_value(v, kb, ke, rv);
|
||||
|
||||
case VALUE_CHAIN:
|
||||
return _insert_value_chain(v, kb, ke, rv);
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
return _insert_prefix_chain(v, kb, ke, rv);
|
||||
|
||||
case NODE4:
|
||||
return _insert_node4(v, kb, ke, rv);
|
||||
|
||||
case NODE16:
|
||||
return _insert_node16(v, kb, ke, rv);
|
||||
|
||||
case NODE48:
|
||||
return _insert_node48(v, kb, ke, rv);
|
||||
|
||||
case NODE256:
|
||||
return _insert_node256(v, kb, ke, rv);
|
||||
}
|
||||
|
||||
// can't get here
|
||||
return false;
|
||||
}
|
||||
|
||||
struct lookup_result {
|
||||
struct value *v;
|
||||
uint8_t *kb;
|
||||
};
|
||||
|
||||
static struct lookup_result _lookup_prefix(struct value *v, uint8_t *kb, uint8_t *ke)
|
||||
{
|
||||
unsigned i;
|
||||
struct value_chain *vc;
|
||||
struct prefix_chain *pc;
|
||||
struct node4 *n4;
|
||||
struct node16 *n16;
|
||||
struct node48 *n48;
|
||||
struct node256 *n256;
|
||||
|
||||
if (kb == ke)
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
|
||||
switch (v->type) {
|
||||
case UNSET:
|
||||
case VALUE:
|
||||
break;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = v->value.ptr;
|
||||
return _lookup_prefix(&vc->child, kb, ke);
|
||||
|
||||
case PREFIX_CHAIN:
|
||||
pc = v->value.ptr;
|
||||
if (ke - kb < pc->len)
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
|
||||
for (i = 0; i < pc->len; i++)
|
||||
if (kb[i] != pc->prefix[i])
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
|
||||
return _lookup_prefix(&pc->child, kb + pc->len, ke);
|
||||
|
||||
case NODE4:
|
||||
n4 = v->value.ptr;
|
||||
for (i = 0; i < n4->nr_entries; i++)
|
||||
if (n4->keys[i] == *kb)
|
||||
return _lookup_prefix(n4->values + i, kb + 1, ke);
|
||||
break;
|
||||
|
||||
case NODE16:
|
||||
// FIXME: use binary search or simd?
|
||||
n16 = v->value.ptr;
|
||||
for (i = 0; i < n16->nr_entries; i++)
|
||||
if (n16->keys[i] == *kb)
|
||||
return _lookup_prefix(n16->values + i, kb + 1, ke);
|
||||
break;
|
||||
|
||||
case NODE48:
|
||||
n48 = v->value.ptr;
|
||||
i = n48->keys[*kb];
|
||||
if (i < 48)
|
||||
return _lookup_prefix(n48->values + i, kb + 1, ke);
|
||||
break;
|
||||
|
||||
case NODE256:
|
||||
n256 = v->value.ptr;
|
||||
return _lookup_prefix(n256->values + *kb, kb + 1, ke);
|
||||
}
|
||||
|
||||
return (struct lookup_result) {.v = v, .kb = kb};
|
||||
}
|
||||
|
||||
bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value rv)
|
||||
{
|
||||
struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
|
||||
if (_insert(lr.v, lr.kb, ke, rv)) {
|
||||
rt->nr_entries++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void radix_tree_delete(struct radix_tree *rt, uint8_t *key_begin, uint8_t *key_end)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
bool radix_tree_lookup(struct radix_tree *rt,
|
||||
uint8_t *kb, uint8_t *ke, union radix_value *result)
|
||||
{
|
||||
struct value_chain *vc;
|
||||
struct lookup_result lr = _lookup_prefix(&rt->root, kb, ke);
|
||||
if (lr.kb == ke) {
|
||||
switch (lr.v->type) {
|
||||
case VALUE:
|
||||
*result = lr.v->value;
|
||||
return true;
|
||||
|
||||
case VALUE_CHAIN:
|
||||
vc = lr.v->value.ptr;
|
||||
*result = vc->value;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
@@ -1,43 +0,0 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_DATA_STRUCT_RADIX_TREE_H
|
||||
#define BASE_DATA_STRUCT_RADIX_TREE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct radix_tree;
|
||||
|
||||
union radix_value {
|
||||
void *ptr;
|
||||
uint64_t n;
|
||||
};
|
||||
|
||||
struct radix_tree *radix_tree_create(void);
|
||||
|
||||
typedef void (*radix_value_dtr)(void *context, union radix_value v);
|
||||
|
||||
// dtr may be NULL
|
||||
void radix_tree_destroy(struct radix_tree *rt, radix_value_dtr dtr, void *context);
|
||||
|
||||
unsigned radix_tree_size(struct radix_tree *rt);
|
||||
bool radix_tree_insert(struct radix_tree *rt, uint8_t *kb, uint8_t *ke, union radix_value v);
|
||||
void radix_tree_delete(struct radix_tree *rt, uint8_t *kb, uint8_t *ke);
|
||||
bool radix_tree_lookup(struct radix_tree *rt,
|
||||
uint8_t *kb, uint8_t *ke, union radix_value *result);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
@@ -1,23 +0,0 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_MEMORY_CONTAINER_OF_H
|
||||
#define BASE_MEMORY_CONTAINER_OF_H
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#define container_of(v, t, head) \
|
||||
((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
@@ -1,31 +0,0 @@
|
||||
// Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
//
|
||||
// This file is part of LVM2.
|
||||
//
|
||||
// This copyrighted material is made available to anyone wishing to use,
|
||||
// modify, copy, or redistribute it subject to the terms and conditions
|
||||
// of the GNU Lesser General Public License v.2.1.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
#ifndef BASE_MEMORY_ZALLOC_H
|
||||
#define BASE_MEMORY_ZALLOC_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static inline void *zalloc(size_t len)
|
||||
{
|
||||
void *ptr = malloc(len);
|
||||
if (ptr)
|
||||
memset(ptr, 0, len);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
@@ -59,6 +59,22 @@ devices {
|
||||
# This configuration option is advanced.
|
||||
scan = [ "/dev" ]
|
||||
|
||||
# Configuration option devices/use_aio.
|
||||
# Use linux asynchronous I/O for parallel device access where possible.
|
||||
# This configuration option has an automatic default value.
|
||||
# use_aio = 1
|
||||
|
||||
# Configuration option devices/aio_max.
|
||||
# Maximum number of asynchronous I/Os to issue concurrently.
|
||||
# This configuration option has an automatic default value.
|
||||
# aio_max = 128
|
||||
|
||||
# Configuration option devices/aio_memory.
|
||||
# Approximate maximum total amount of memory (in MB) used
|
||||
# for asynchronous I/O buffers.
|
||||
# This configuration option has an automatic default value.
|
||||
# aio_memory = 10
|
||||
|
||||
# Configuration option devices/obtain_device_list_from_udev.
|
||||
# Obtain the list of available devices from udev.
|
||||
# This avoids opening or using any inapplicable non-block devices or
|
||||
@@ -114,8 +130,8 @@ devices {
|
||||
# device path names. Each regex is delimited by a vertical bar '|'
|
||||
# (or any character) and is preceded by 'a' to accept the path, or
|
||||
# by 'r' to reject the path. The first regex in the list to match the
|
||||
# path is used, producing the 'a' or 'r' result for the device.
|
||||
# When multiple path names exist for a block device, if any path name
|
||||
# path is used, producing the 'a' or 'r' result for that path.
|
||||
# If any of multiple existing path names for a block device
|
||||
# matches an 'a' pattern before an 'r' pattern, then the device is
|
||||
# accepted. If all the path names match an 'r' pattern first, then the
|
||||
# device is rejected. Unmatching path names do not affect the accept
|
||||
@@ -821,6 +837,13 @@ global {
|
||||
# encountered the internal error. Please only enable for debugging.
|
||||
abort_on_internal_errors = 0
|
||||
|
||||
# Configuration option global/detect_internal_vg_cache_corruption.
|
||||
# Internal verification of VG structures.
|
||||
# Check if CRC matches when a parsed VG is used multiple times. This
|
||||
# is useful to catch unexpected changes to cached VG structures.
|
||||
# Please only enable for debugging.
|
||||
detect_internal_vg_cache_corruption = 0
|
||||
|
||||
# Configuration option global/metadata_read_only.
|
||||
# No operations that change on-disk metadata are permitted.
|
||||
# Additionally, read-only commands that encounter metadata in need of
|
||||
|
250
configure
vendored
250
configure
vendored
@@ -653,6 +653,7 @@ UDEV_RULES
|
||||
UDEV_PC
|
||||
THIN
|
||||
TESTSUITE_DATA
|
||||
TESTING
|
||||
STATIC_LINK
|
||||
STATICDIR
|
||||
SNAPSHOTS
|
||||
@@ -663,6 +664,7 @@ SBINDIR
|
||||
REPLICATORS
|
||||
READLINE_LIBS
|
||||
RT_LIBS
|
||||
RAID
|
||||
PYTHON3DIR
|
||||
PYTHON2DIR
|
||||
PYTHON3_LIBDIRS
|
||||
@@ -705,7 +707,9 @@ FSADM
|
||||
ELDFLAGS
|
||||
DM_LIB_PATCHLEVEL
|
||||
DMEVENTD_PATH
|
||||
AIO_LIBS
|
||||
DL_LIBS
|
||||
AIO
|
||||
DEVMAPPER
|
||||
DEFAULT_USE_LVMLOCKD
|
||||
DEFAULT_USE_LVMPOLLD
|
||||
@@ -779,6 +783,8 @@ LOCKD_SANLOCK_LIBS
|
||||
LOCKD_SANLOCK_CFLAGS
|
||||
VALGRIND_LIBS
|
||||
VALGRIND_CFLAGS
|
||||
CUNIT_LIBS
|
||||
CUNIT_CFLAGS
|
||||
GENPNG
|
||||
GENHTML
|
||||
LCOV
|
||||
@@ -880,7 +886,6 @@ infodir
|
||||
docdir
|
||||
oldincludedir
|
||||
includedir
|
||||
runstatedir
|
||||
localstatedir
|
||||
sharedstatedir
|
||||
sysconfdir
|
||||
@@ -918,6 +923,7 @@ with_pool
|
||||
with_cluster
|
||||
with_snapshots
|
||||
with_mirrors
|
||||
with_raid
|
||||
with_default_mirror_segtype
|
||||
with_default_raid10_segtype
|
||||
with_default_sparse_segtype
|
||||
@@ -947,8 +953,10 @@ with_cmirrord_pidfile
|
||||
enable_debug
|
||||
with_optimisation
|
||||
enable_profiling
|
||||
enable_testing
|
||||
enable_valgrind_pool
|
||||
enable_devmapper
|
||||
enable_aio
|
||||
enable_lvmetad
|
||||
enable_lvmpolld
|
||||
enable_lvmlockd_sanlock
|
||||
@@ -1038,6 +1046,8 @@ DLM_CFLAGS
|
||||
DLM_LIBS
|
||||
SACKPT_CFLAGS
|
||||
SACKPT_LIBS
|
||||
CUNIT_CFLAGS
|
||||
CUNIT_LIBS
|
||||
VALGRIND_CFLAGS
|
||||
VALGRIND_LIBS
|
||||
LOCKD_SANLOCK_CFLAGS
|
||||
@@ -1091,7 +1101,6 @@ datadir='${datarootdir}'
|
||||
sysconfdir='${prefix}/etc'
|
||||
sharedstatedir='${prefix}/com'
|
||||
localstatedir='${prefix}/var'
|
||||
runstatedir='${localstatedir}/run'
|
||||
includedir='${prefix}/include'
|
||||
oldincludedir='/usr/include'
|
||||
docdir='${datarootdir}/doc/${PACKAGE}'
|
||||
@@ -1344,15 +1353,6 @@ do
|
||||
| -silent | --silent | --silen | --sile | --sil)
|
||||
silent=yes ;;
|
||||
|
||||
-runstatedir | --runstatedir | --runstatedi | --runstated \
|
||||
| --runstate | --runstat | --runsta | --runst | --runs \
|
||||
| --run | --ru | --r)
|
||||
ac_prev=runstatedir ;;
|
||||
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
|
||||
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
|
||||
| --run=* | --ru=* | --r=*)
|
||||
runstatedir=$ac_optarg ;;
|
||||
|
||||
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||
ac_prev=sbindir ;;
|
||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||
@@ -1490,7 +1490,7 @@ fi
|
||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||
libdir localedir mandir runstatedir
|
||||
libdir localedir mandir
|
||||
do
|
||||
eval ac_val=\$$ac_var
|
||||
# Remove trailing slashes.
|
||||
@@ -1643,7 +1643,6 @@ Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||
--includedir=DIR C header files [PREFIX/include]
|
||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||
@@ -1693,8 +1692,10 @@ Optional Features:
|
||||
--enable-cmirrord enable the cluster mirror log daemon
|
||||
--enable-debug enable debugging
|
||||
--enable-profiling gather gcov profiling data
|
||||
--enable-testing enable testing targets in the makefile
|
||||
--enable-valgrind-pool enable valgrind awareness of pools
|
||||
--disable-devmapper disable LVM2 device-mapper interaction
|
||||
--disable-aio disable asynchronous I/O
|
||||
--enable-lvmetad enable the LVM Metadata Daemon
|
||||
--enable-lvmpolld enable the LVM Polling Daemon
|
||||
--enable-lvmlockd-sanlock
|
||||
@@ -1756,6 +1757,7 @@ Optional Packages:
|
||||
[internal]
|
||||
--with-snapshots=TYPE snapshot support: internal/shared/none [internal]
|
||||
--with-mirrors=TYPE mirror support: internal/shared/none [internal]
|
||||
--with-raid=TYPE raid support: internal/shared/none [internal]
|
||||
--with-default-mirror-segtype=TYPE
|
||||
default mirror segtype: raid1/mirror [raid1]
|
||||
--with-default-raid10-segtype=TYPE
|
||||
@@ -1880,6 +1882,9 @@ Some influential environment variables:
|
||||
SACKPT_CFLAGS
|
||||
C compiler flags for SACKPT, overriding pkg-config
|
||||
SACKPT_LIBS linker flags for SACKPT, overriding pkg-config
|
||||
CUNIT_CFLAGS
|
||||
C compiler flags for CUNIT, overriding pkg-config
|
||||
CUNIT_LIBS linker flags for CUNIT, overriding pkg-config
|
||||
VALGRIND_CFLAGS
|
||||
C compiler flags for VALGRIND, overriding pkg-config
|
||||
VALGRIND_LIBS
|
||||
@@ -3178,6 +3183,7 @@ case "$host_os" in
|
||||
LDDEPS="$LDDEPS .export.sym"
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
AIO=yes
|
||||
BUILD_LVMETAD=no
|
||||
BUILD_LVMPOLLD=no
|
||||
LOCKDSANLOCK=no
|
||||
@@ -3197,6 +3203,7 @@ case "$host_os" in
|
||||
CLDNOWHOLEARCHIVE=
|
||||
LIB_SUFFIX=dylib
|
||||
DEVMAPPER=yes
|
||||
AIO=no
|
||||
ODIRECT=no
|
||||
DM_IOCTLS=no
|
||||
SELINUX=no
|
||||
@@ -6154,7 +6161,7 @@ fi
|
||||
|
||||
|
||||
for ac_header in assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
||||
getopt.h inttypes.h langinfo.h libaio.h libgen.h limits.h locale.h paths.h \
|
||||
getopt.h inttypes.h langinfo.h libgen.h limits.h locale.h paths.h \
|
||||
signal.h stdarg.h stddef.h stdio.h stdlib.h string.h sys/file.h \
|
||||
sys/ioctl.h syslog.h sys/mman.h sys/param.h sys/resource.h sys/stat.h \
|
||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||
@@ -8521,6 +8528,19 @@ $as_echo "#define MIRRORED_INTERNAL 1" >>confdefs.h
|
||||
esac
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to include raid" >&5
|
||||
$as_echo_n "checking whether to include raid... " >&6; }
|
||||
|
||||
# Check whether --with-raid was given.
|
||||
if test "${with_raid+set}" = set; then :
|
||||
withval=$with_raid; RAID=$withval
|
||||
else
|
||||
RAID=internal
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $RAID" >&5
|
||||
$as_echo "$RAID" >&6; }
|
||||
|
||||
|
||||
# Check whether --with-default-mirror-segtype was given.
|
||||
if test "${with_default_mirror_segtype+set}" = set; then :
|
||||
@@ -8537,10 +8557,15 @@ else
|
||||
DEFAULT_RAID10_SEGTYPE="raid10"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
case "$RAID" in
|
||||
none) test "$DEFAULT_MIRROR_SEGTYPE" = "raid1" && DEFAULT_MIRROR_SEGTYPE="mirror"
|
||||
test "$DEFAULT_RAID10_SEGTYPE" = "raid10" && DEFAULT_RAID10_SEGTYPE="mirror" ;;
|
||||
shared) ;;
|
||||
internal)
|
||||
$as_echo "#define RAID_INTERNAL 1" >>confdefs.h
|
||||
|
||||
;;
|
||||
*) as_fn_error $? "--with-raid parameter invalid" "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
@@ -11573,6 +11598,114 @@ $as_echo "$as_me: WARNING: GD.pm perl module is not installed" >&2;}
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable unit testing" >&5
|
||||
$as_echo_n "checking whether to enable unit testing... " >&6; }
|
||||
# Check whether --enable-testing was given.
|
||||
if test "${enable_testing+set}" = set; then :
|
||||
enableval=$enable_testing; TESTING=$enableval
|
||||
else
|
||||
TESTING=no
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $TESTING" >&5
|
||||
$as_echo "$TESTING" >&6; }
|
||||
|
||||
if test "$TESTING" = yes; then
|
||||
pkg_config_init
|
||||
|
||||
pkg_failed=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CUNIT" >&5
|
||||
$as_echo_n "checking for CUNIT... " >&6; }
|
||||
|
||||
if test -n "$CUNIT_CFLAGS"; then
|
||||
pkg_cv_CUNIT_CFLAGS="$CUNIT_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "cunit >= 2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_CUNIT_CFLAGS=`$PKG_CONFIG --cflags "cunit >= 2.0" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
if test -n "$CUNIT_LIBS"; then
|
||||
pkg_cv_CUNIT_LIBS="$CUNIT_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cunit >= 2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "cunit >= 2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_CUNIT_LIBS=`$PKG_CONFIG --libs "cunit >= 2.0" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
CUNIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cunit >= 2.0" 2>&1`
|
||||
else
|
||||
CUNIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cunit >= 2.0" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$CUNIT_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (cunit >= 2.0) were not met:
|
||||
|
||||
$CUNIT_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
Alternatively, you may set the environment variables CUNIT_CFLAGS
|
||||
and CUNIT_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details." "$LINENO" 5
|
||||
elif test $pkg_failed = untried; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
Alternatively, you may set the environment variables CUNIT_CFLAGS
|
||||
and CUNIT_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
|
||||
See \`config.log' for more details" "$LINENO" 5; }
|
||||
else
|
||||
CUNIT_CFLAGS=$pkg_cv_CUNIT_CFLAGS
|
||||
CUNIT_LIBS=$pkg_cv_CUNIT_LIBS
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
TESTSUITE_DATA='${datarootdir}/lvm2-testsuite'
|
||||
# double eval needed ${datarootdir} -> ${prefix}/share -> real path
|
||||
@@ -11699,6 +11832,67 @@ $as_echo "#define DEVMAPPER_SUPPORT 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use asynchronous I/O" >&5
|
||||
$as_echo_n "checking whether to asynchronous I/O... " >&6; }
|
||||
# Check whether --enable-aio was given.
|
||||
if test "${enable_aio+set}" = set; then :
|
||||
enableval=$enable_aio; AIO=$enableval
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AIO" >&5
|
||||
$as_echo "$AIO" >&6; }
|
||||
|
||||
if test "$AIO" = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for io_setup in -laio" >&5
|
||||
$as_echo_n "checking for io_setup in -laio... " >&6; }
|
||||
if ${ac_cv_lib_aio_io_setup+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-laio $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char io_setup ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return io_setup ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_aio_io_setup=yes
|
||||
else
|
||||
ac_cv_lib_aio_io_setup=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_aio_io_setup" >&5
|
||||
$as_echo "$ac_cv_lib_aio_io_setup" >&6; }
|
||||
if test "x$ac_cv_lib_aio_io_setup" = xyes; then :
|
||||
|
||||
$as_echo "#define AIO_SUPPORT 1" >>confdefs.h
|
||||
|
||||
AIO_LIBS="-laio"
|
||||
AIO_SUPPORT=yes
|
||||
else
|
||||
AIO_LIBS=
|
||||
AIO_SUPPORT=no
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build LVMetaD" >&5
|
||||
$as_echo_n "checking whether to build LVMetaD... " >&6; }
|
||||
@@ -12773,7 +12967,7 @@ if ${am_cv_pathless_PYTHON+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
|
||||
for am_cv_pathless_PYTHON in python python2 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
|
||||
test "$am_cv_pathless_PYTHON" = none && break
|
||||
prog="import sys
|
||||
# split strings by '.' and convert to numeric. Append some zeros
|
||||
@@ -13341,7 +13535,7 @@ if ${am_cv_pathless_PYTHON+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
|
||||
for am_cv_pathless_PYTHON in python python2 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
|
||||
test "$am_cv_pathless_PYTHON" = none && break
|
||||
prog="import sys
|
||||
# split strings by '.' and convert to numeric. Append some zeros
|
||||
@@ -13947,6 +14141,8 @@ fi
|
||||
|
||||
################################################################################
|
||||
if [ \( "$LVM1" = shared -o "$POOL" = shared -o "$CLUSTER" = shared \
|
||||
-o "$SNAPSHOTS" = shared -o "$MIRRORS" = shared \
|
||||
-o "$RAID" = shared -o "$CACHE" = shared \
|
||||
\) -a "$STATIC_LINK" = yes ]; then
|
||||
as_fn_error $? "Features cannot be 'shared' when building statically" "$LINENO" 5
|
||||
fi
|
||||
@@ -15642,13 +15838,17 @@ _ACEOF
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile device_mapper/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile include/lvm-version.h libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/api/python_lvm_unit.py test/unit/Makefile tools/Makefile udev/Makefile"
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/dmfilemapd/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/lvmdbusd daemons/lvmdbusd/lvmdb.py daemons/lvmdbusd/lvm_shell_proxy.py daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@@ -16365,7 +16565,6 @@ do
|
||||
"daemons/lvmetad/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmetad/Makefile" ;;
|
||||
"daemons/lvmpolld/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmpolld/Makefile" ;;
|
||||
"daemons/lvmlockd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmlockd/Makefile" ;;
|
||||
"device_mapper/Makefile") CONFIG_FILES="$CONFIG_FILES device_mapper/Makefile" ;;
|
||||
"conf/Makefile") CONFIG_FILES="$CONFIG_FILES conf/Makefile" ;;
|
||||
"conf/example.conf") CONFIG_FILES="$CONFIG_FILES conf/example.conf" ;;
|
||||
"conf/lvmlocal.conf") CONFIG_FILES="$CONFIG_FILES conf/lvmlocal.conf" ;;
|
||||
@@ -16377,7 +16576,12 @@ do
|
||||
"lib/format1/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format1/Makefile" ;;
|
||||
"lib/format_pool/Makefile") CONFIG_FILES="$CONFIG_FILES lib/format_pool/Makefile" ;;
|
||||
"lib/locking/Makefile") CONFIG_FILES="$CONFIG_FILES lib/locking/Makefile" ;;
|
||||
"lib/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES lib/mirror/Makefile" ;;
|
||||
"include/lvm-version.h") CONFIG_FILES="$CONFIG_FILES include/lvm-version.h" ;;
|
||||
"lib/raid/Makefile") CONFIG_FILES="$CONFIG_FILES lib/raid/Makefile" ;;
|
||||
"lib/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES lib/snapshot/Makefile" ;;
|
||||
"lib/thin/Makefile") CONFIG_FILES="$CONFIG_FILES lib/thin/Makefile" ;;
|
||||
"lib/cache_segtype/Makefile") CONFIG_FILES="$CONFIG_FILES lib/cache_segtype/Makefile" ;;
|
||||
"libdaemon/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/Makefile" ;;
|
||||
"libdaemon/client/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/client/Makefile" ;;
|
||||
"libdaemon/server/Makefile") CONFIG_FILES="$CONFIG_FILES libdaemon/server/Makefile" ;;
|
||||
@@ -16418,10 +16622,12 @@ do
|
||||
"scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
|
||||
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
|
||||
"test/api/Makefile") CONFIG_FILES="$CONFIG_FILES test/api/Makefile" ;;
|
||||
"test/api/python_lvm_unit.py") CONFIG_FILES="$CONFIG_FILES test/api/python_lvm_unit.py" ;;
|
||||
"test/unit/Makefile") CONFIG_FILES="$CONFIG_FILES test/unit/Makefile" ;;
|
||||
"tools/Makefile") CONFIG_FILES="$CONFIG_FILES tools/Makefile" ;;
|
||||
"udev/Makefile") CONFIG_FILES="$CONFIG_FILES udev/Makefile" ;;
|
||||
"unit-tests/datastruct/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/datastruct/Makefile" ;;
|
||||
"unit-tests/regex/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/regex/Makefile" ;;
|
||||
"unit-tests/mm/Makefile") CONFIG_FILES="$CONFIG_FILES unit-tests/mm/Makefile" ;;
|
||||
|
||||
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
|
||||
esac
|
||||
|
@@ -39,6 +39,7 @@ case "$host_os" in
|
||||
LDDEPS="$LDDEPS .export.sym"
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
AIO=yes
|
||||
BUILD_LVMETAD=no
|
||||
BUILD_LVMPOLLD=no
|
||||
LOCKDSANLOCK=no
|
||||
@@ -58,6 +59,7 @@ case "$host_os" in
|
||||
CLDNOWHOLEARCHIVE=
|
||||
LIB_SUFFIX=dylib
|
||||
DEVMAPPER=yes
|
||||
AIO=no
|
||||
ODIRECT=no
|
||||
DM_IOCTLS=no
|
||||
SELINUX=no
|
||||
@@ -103,7 +105,7 @@ AC_HEADER_SYS_WAIT
|
||||
AC_HEADER_TIME
|
||||
|
||||
AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
||||
getopt.h inttypes.h langinfo.h libaio.h libgen.h limits.h locale.h paths.h \
|
||||
getopt.h inttypes.h langinfo.h libgen.h limits.h locale.h paths.h \
|
||||
signal.h stdarg.h stddef.h stdio.h stdlib.h string.h sys/file.h \
|
||||
sys/ioctl.h syslog.h sys/mman.h sys/param.h sys/resource.h sys/stat.h \
|
||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||
@@ -384,6 +386,13 @@ esac
|
||||
|
||||
################################################################################
|
||||
dnl -- raid inclusion type
|
||||
AC_MSG_CHECKING(whether to include raid)
|
||||
AC_ARG_WITH(raid,
|
||||
AC_HELP_STRING([--with-raid=TYPE],
|
||||
[raid support: internal/shared/none [internal]]),
|
||||
RAID=$withval, RAID=internal)
|
||||
AC_MSG_RESULT($RAID)
|
||||
|
||||
AC_ARG_WITH(default-mirror-segtype,
|
||||
AC_HELP_STRING([--with-default-mirror-segtype=TYPE],
|
||||
[default mirror segtype: raid1/mirror [raid1]]),
|
||||
@@ -392,9 +401,14 @@ AC_ARG_WITH(default-raid10-segtype,
|
||||
AC_HELP_STRING([--with-default-raid10-segtype=TYPE],
|
||||
[default mirror segtype: raid10/mirror [raid10]]),
|
||||
DEFAULT_RAID10_SEGTYPE=$withval, DEFAULT_RAID10_SEGTYPE="raid10")
|
||||
|
||||
AC_DEFINE([RAID_INTERNAL], 1,
|
||||
[Define to 1 to include built-in support for raid.])
|
||||
case "$RAID" in
|
||||
none) test "$DEFAULT_MIRROR_SEGTYPE" = "raid1" && DEFAULT_MIRROR_SEGTYPE="mirror"
|
||||
test "$DEFAULT_RAID10_SEGTYPE" = "raid10" && DEFAULT_RAID10_SEGTYPE="mirror" ;;
|
||||
shared) ;;
|
||||
internal) AC_DEFINE([RAID_INTERNAL], 1,
|
||||
[Define to 1 to include built-in support for raid.]) ;;
|
||||
*) AC_MSG_ERROR([--with-raid parameter invalid]) ;;
|
||||
esac
|
||||
|
||||
AC_DEFINE_UNQUOTED([DEFAULT_MIRROR_SEGTYPE], ["$DEFAULT_MIRROR_SEGTYPE"],
|
||||
[Default segtype used for mirror volumes.])
|
||||
@@ -1055,6 +1069,20 @@ if test "$PROFILING" = yes; then
|
||||
fi
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable testing
|
||||
AC_MSG_CHECKING(whether to enable unit testing)
|
||||
AC_ARG_ENABLE(testing,
|
||||
AC_HELP_STRING([--enable-testing],
|
||||
[enable testing targets in the makefile]),
|
||||
TESTING=$enableval, TESTING=no)
|
||||
AC_MSG_RESULT($TESTING)
|
||||
|
||||
if test "$TESTING" = yes; then
|
||||
pkg_config_init
|
||||
PKG_CHECK_MODULES(CUNIT, cunit >= 2.0)
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Set LVM2 testsuite data
|
||||
TESTSUITE_DATA='${datarootdir}/lvm2-testsuite'
|
||||
@@ -1096,6 +1124,24 @@ if test "$DEVMAPPER" = yes; then
|
||||
AC_DEFINE([DEVMAPPER_SUPPORT], 1, [Define to 1 to enable LVM2 device-mapper interaction.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Disable aio
|
||||
AC_MSG_CHECKING(whether to use asynchronous I/O)
|
||||
AC_ARG_ENABLE(aio,
|
||||
AC_HELP_STRING([--disable-aio],
|
||||
[disable asynchronous I/O]),
|
||||
AIO=$enableval)
|
||||
AC_MSG_RESULT($AIO)
|
||||
|
||||
if test "$AIO" = yes; then
|
||||
AC_CHECK_LIB(aio, io_setup,
|
||||
[AC_DEFINE([AIO_SUPPORT], 1, [Define to 1 if aio is available.])
|
||||
AIO_LIBS="-laio"
|
||||
AIO_SUPPORT=yes],
|
||||
[AIO_LIBS=
|
||||
AIO_SUPPORT=no ])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmetad
|
||||
AC_MSG_CHECKING(whether to build LVMetaD)
|
||||
@@ -1591,6 +1637,8 @@ AC_CHECK_LIB(dl, dlopen,
|
||||
################################################################################
|
||||
dnl -- Check for shared/static conflicts
|
||||
if [[ \( "$LVM1" = shared -o "$POOL" = shared -o "$CLUSTER" = shared \
|
||||
-o "$SNAPSHOTS" = shared -o "$MIRRORS" = shared \
|
||||
-o "$RAID" = shared -o "$CACHE" = shared \
|
||||
\) -a "$STATIC_LINK" = yes ]]; then
|
||||
AC_MSG_ERROR([Features cannot be 'shared' when building statically])
|
||||
fi
|
||||
@@ -2033,9 +2081,11 @@ AC_SUBST(DEFAULT_USE_LVMETAD)
|
||||
AC_SUBST(DEFAULT_USE_LVMPOLLD)
|
||||
AC_SUBST(DEFAULT_USE_LVMLOCKD)
|
||||
AC_SUBST(DEVMAPPER)
|
||||
AC_SUBST(AIO)
|
||||
AC_SUBST(DLM_CFLAGS)
|
||||
AC_SUBST(DLM_LIBS)
|
||||
AC_SUBST(DL_LIBS)
|
||||
AC_SUBST(AIO_LIBS)
|
||||
AC_SUBST(DMEVENTD_PATH)
|
||||
AC_SUBST(DM_LIB_PATCHLEVEL)
|
||||
AC_SUBST(ELDFLAGS)
|
||||
@@ -2084,6 +2134,7 @@ AC_SUBST(PYTHON2DIR)
|
||||
AC_SUBST(PYTHON3DIR)
|
||||
AC_SUBST(QUORUM_CFLAGS)
|
||||
AC_SUBST(QUORUM_LIBS)
|
||||
AC_SUBST(RAID)
|
||||
AC_SUBST(RT_LIBS)
|
||||
AC_SUBST(READLINE_LIBS)
|
||||
AC_SUBST(REPLICATORS)
|
||||
@@ -2099,6 +2150,7 @@ AC_SUBST(SYSTEMD_LIBS)
|
||||
AC_SUBST(SNAPSHOTS)
|
||||
AC_SUBST(STATICDIR)
|
||||
AC_SUBST(STATIC_LINK)
|
||||
AC_SUBST(TESTING)
|
||||
AC_SUBST(TESTSUITE_DATA)
|
||||
AC_SUBST(THIN)
|
||||
AC_SUBST(THIN_CHECK_CMD)
|
||||
@@ -2164,7 +2216,6 @@ daemons/lvmdbusd/path.py
|
||||
daemons/lvmetad/Makefile
|
||||
daemons/lvmpolld/Makefile
|
||||
daemons/lvmlockd/Makefile
|
||||
device_mapper/Makefile
|
||||
conf/Makefile
|
||||
conf/example.conf
|
||||
conf/lvmlocal.conf
|
||||
@@ -2176,7 +2227,12 @@ lib/Makefile
|
||||
lib/format1/Makefile
|
||||
lib/format_pool/Makefile
|
||||
lib/locking/Makefile
|
||||
lib/mirror/Makefile
|
||||
include/lvm-version.h
|
||||
lib/raid/Makefile
|
||||
lib/snapshot/Makefile
|
||||
lib/thin/Makefile
|
||||
lib/cache_segtype/Makefile
|
||||
libdaemon/Makefile
|
||||
libdaemon/client/Makefile
|
||||
libdaemon/server/Makefile
|
||||
@@ -2217,10 +2273,12 @@ scripts/lvmdump.sh
|
||||
scripts/Makefile
|
||||
test/Makefile
|
||||
test/api/Makefile
|
||||
test/api/python_lvm_unit.py
|
||||
test/unit/Makefile
|
||||
tools/Makefile
|
||||
udev/Makefile
|
||||
unit-tests/datastruct/Makefile
|
||||
unit-tests/regex/Makefile
|
||||
unit-tests/mm/Makefile
|
||||
])
|
||||
AC_OUTPUT
|
||||
|
@@ -74,7 +74,7 @@ TARGETS = \
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS) -laio
|
||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
||||
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
INSTALL_TARGETS = \
|
||||
|
@@ -1999,9 +1999,6 @@ static int send_message(void *buf, int msglen, const char *csid, int fd,
|
||||
return clops->cluster_send_message(buf, msglen, csid, errtext);
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
return 0;
|
||||
|
||||
/* Make sure it all goes */
|
||||
for (ptr = 0; ptr < msglen;) {
|
||||
if ((len = write(fd, (char*)buf + ptr, msglen - ptr)) <= 0) {
|
||||
|
@@ -661,9 +661,10 @@ int do_refresh_cache(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_full_scan_done(0);
|
||||
init_ignore_suspended_devices(1);
|
||||
lvmcache_force_next_label_scan();
|
||||
lvmcache_label_scan(cmd);
|
||||
label_scan_destroy(cmd); /* destroys bcache (to close devs), keeps lvmcache */
|
||||
dm_pool_empty(cmd->mem);
|
||||
|
||||
pthread_mutex_unlock(&lvm_lock);
|
||||
@@ -806,7 +807,8 @@ static void lvm2_log_fn(int level, const char *file, int line, int dm_errno,
|
||||
if (level != _LOG_ERR && level != _LOG_FATAL)
|
||||
return;
|
||||
|
||||
(void) dm_strncpy(last_error, message, sizeof(last_error));
|
||||
strncpy(last_error, message, sizeof(last_error));
|
||||
last_error[sizeof(last_error)-1] = '\0';
|
||||
}
|
||||
|
||||
/* This checks some basic cluster-LVM configuration stuff */
|
||||
|
@@ -166,9 +166,6 @@ int cluster_send(struct clog_request *rq)
|
||||
{
|
||||
int r;
|
||||
int found = 0;
|
||||
#if CMIRROR_HAS_CHECKPOINT
|
||||
int count = 0;
|
||||
#endif
|
||||
struct iovec iov;
|
||||
struct clog_cpg *entry;
|
||||
|
||||
@@ -206,6 +203,8 @@ int cluster_send(struct clog_request *rq)
|
||||
|
||||
#if CMIRROR_HAS_CHECKPOINT
|
||||
do {
|
||||
int count = 0;
|
||||
|
||||
r = cpg_mcast_joined(entry->handle, CPG_TYPE_AGREED, &iov, 1);
|
||||
if (r != SA_AIS_ERR_TRY_AGAIN)
|
||||
break;
|
||||
@@ -1631,7 +1630,7 @@ int create_cluster_cpg(char *uuid, uint64_t luid)
|
||||
|
||||
size = ((strlen(uuid) + 1) > CPG_MAX_NAME_LENGTH) ?
|
||||
CPG_MAX_NAME_LENGTH : (strlen(uuid) + 1);
|
||||
(void) dm_strncpy(new->name.value, uuid, size);
|
||||
strncpy(new->name.value, uuid, size);
|
||||
new->name.length = (uint32_t)size;
|
||||
new->luid = luid;
|
||||
|
||||
|
@@ -451,19 +451,15 @@ static int _clog_ctr(char *uuid, uint64_t luid,
|
||||
lc->skip_bit_warning = region_count;
|
||||
lc->disk_fd = -1;
|
||||
lc->log_dev_failed = 0;
|
||||
if (!dm_strncpy(lc->uuid, uuid, DM_UUID_LEN)) {
|
||||
LOG_ERROR("Cannot use too long UUID %s.", uuid);
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
strncpy(lc->uuid, uuid, DM_UUID_LEN);
|
||||
lc->luid = luid;
|
||||
|
||||
if (get_log(lc->uuid, lc->luid) ||
|
||||
get_pending_log(lc->uuid, lc->luid)) {
|
||||
LOG_ERROR("[%s/%" PRIu64 "u] Log already exists, unable to create.",
|
||||
SHORT_UUID(lc->uuid), lc->luid);
|
||||
r = -EINVAL;
|
||||
goto fail;
|
||||
dm_free(lc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dm_list_init(&lc->mark_list);
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#define _LVM_CLOG_LOGGING_H
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include "configure.h"
|
||||
#include <stdio.h>
|
||||
|
@@ -754,10 +754,11 @@ int dm_event_get_registered_device(struct dm_event_handler *dmevh, int next)
|
||||
uuid = dm_task_get_uuid(dmt);
|
||||
|
||||
/* FIXME Distinguish errors connecting to daemon */
|
||||
if ((ret = _do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
|
||||
&msg, dmevh->dso, uuid, dmevh->mask, 0))) {
|
||||
if (_do_event(next ? DM_EVENT_CMD_GET_NEXT_REGISTERED_DEVICE :
|
||||
DM_EVENT_CMD_GET_REGISTERED_DEVICE, dmevh->dmeventd_path,
|
||||
&msg, dmevh->dso, uuid, dmevh->mask, 0)) {
|
||||
log_debug("%s: device not registered.", dm_task_get_name(dmt));
|
||||
ret = -ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@@ -16,7 +16,23 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SUBDIRS += lvm2 snapshot raid thin mirror
|
||||
SUBDIRS += lvm2
|
||||
|
||||
ifneq ("@MIRRORS@", "none")
|
||||
SUBDIRS += mirror
|
||||
endif
|
||||
|
||||
ifneq ("@SNAPSHOTS@", "none")
|
||||
SUBDIRS += snapshot
|
||||
endif
|
||||
|
||||
ifneq ("@RAID@", "none")
|
||||
SUBDIRS += raid
|
||||
endif
|
||||
|
||||
ifneq ("@THIN@", "none")
|
||||
SUBDIRS += thin
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = lvm2 mirror snapshot raid thin
|
||||
|
@@ -64,23 +64,23 @@ DM_EVENT_LOG_FN("thin")
|
||||
|
||||
static int _run_command(struct dso_state *state)
|
||||
{
|
||||
char val[16];
|
||||
char val[3][36];
|
||||
char *env[] = { val[0], val[1], val[2], NULL };
|
||||
int i;
|
||||
|
||||
/* Mark for possible lvm2 command we are running from dmeventd
|
||||
* lvm2 will not try to talk back to dmeventd while processing it */
|
||||
(void) setenv("LVM_RUN_BY_DMEVENTD", "1", 1);
|
||||
(void) dm_snprintf(val[0], sizeof(val[0]), "LVM_RUN_BY_DMEVENTD=1");
|
||||
|
||||
if (state->data_percent) {
|
||||
/* Prepare some known data to env vars for easy use */
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->data_percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_THIN_POOL_DATA", val, 1);
|
||||
if (dm_snprintf(val, sizeof(val), "%d",
|
||||
state->metadata_percent / DM_PERCENT_1) != -1)
|
||||
(void) setenv("DMEVENTD_THIN_POOL_METADATA", val, 1);
|
||||
(void) dm_snprintf(val[1], sizeof(val[1]), "DMEVENTD_THIN_POOL_DATA=%d",
|
||||
state->data_percent / DM_PERCENT_1);
|
||||
(void) dm_snprintf(val[2], sizeof(val[2]), "DMEVENTD_THIN_POOL_METADATA=%d",
|
||||
state->metadata_percent / DM_PERCENT_1);
|
||||
} else {
|
||||
/* For an error event it's for a user to check status and decide */
|
||||
env[1] = NULL;
|
||||
log_debug("Error event processing.");
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ static int _run_command(struct dso_state *state)
|
||||
/* child */
|
||||
(void) close(0);
|
||||
for (i = 3; i < 255; ++i) (void) close(i);
|
||||
execvp(state->argv[0], state->argv);
|
||||
execve(state->argv[0], state->argv, env);
|
||||
_exit(errno);
|
||||
} else if (state->pid == -1) {
|
||||
log_error("Can't fork command %s.", state->cmd_str);
|
||||
|
@@ -802,7 +802,7 @@ bad:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char * const _mode_names[] = {
|
||||
static const char * _mode_names[] = {
|
||||
"inode",
|
||||
"path"
|
||||
};
|
||||
@@ -827,10 +827,8 @@ int main(int argc, char **argv)
|
||||
"mode=%s, path=%s", fm.fd, fm.group_id,
|
||||
_mode_names[fm.mode], fm.path);
|
||||
|
||||
if (!_foreground && !_daemonise(&fm)) {
|
||||
dm_free(fm.path);
|
||||
if (!_foreground && !_daemonise(&fm))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return _dmfilemapd(&fm);
|
||||
}
|
||||
|
@@ -2421,15 +2421,18 @@ static response set_global_info(lvmetad_state *s, request r)
|
||||
|
||||
static response get_global_info(lvmetad_state *s, request r)
|
||||
{
|
||||
/* This buffer should be large enough to hold all the possible reasons. */
|
||||
char reason[REASON_BUF_SIZE] = { 0 };
|
||||
char reason[REASON_BUF_SIZE];
|
||||
char flag_str[64];
|
||||
int pid;
|
||||
|
||||
/* This buffer should be large enough to hold all the possible reasons. */
|
||||
|
||||
memset(reason, 0, sizeof(reason));
|
||||
|
||||
pid = (int)daemon_request_int(r, "pid", 0);
|
||||
|
||||
if (s->flags & GLFL_DISABLE) {
|
||||
snprintf(reason, REASON_BUF_SIZE, "%s%s%s%s%s",
|
||||
snprintf(reason, REASON_BUF_SIZE - 1, "%s%s%s%s%s",
|
||||
(s->flags & GLFL_DISABLE_REASON_DIRECT) ? LVMETAD_DISABLE_REASON_DIRECT "," : "",
|
||||
(s->flags & GLFL_DISABLE_REASON_REPAIR) ? LVMETAD_DISABLE_REASON_REPAIR "," : "",
|
||||
(s->flags & GLFL_DISABLE_REASON_LVM1) ? LVMETAD_DISABLE_REASON_LVM1 "," : "",
|
||||
@@ -2528,8 +2531,10 @@ inval:
|
||||
|
||||
info = dm_hash_lookup(s->vgid_to_info, uuid);
|
||||
if (!info) {
|
||||
if (!(info = dm_zalloc(sizeof(struct vg_info))))
|
||||
info = malloc(sizeof(struct vg_info));
|
||||
if (!info)
|
||||
goto bad;
|
||||
memset(info, 0, sizeof(struct vg_info));
|
||||
if (!dm_hash_insert(s->vgid_to_info, uuid, (void*)info))
|
||||
goto bad;
|
||||
}
|
||||
@@ -2700,8 +2705,9 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
if (!prev_in_progress && this_in_progress) {
|
||||
/* New update is starting (filter token is replaced by update token) */
|
||||
|
||||
(void) dm_strncpy(prev_token, state->token, sizeof(prev_token));
|
||||
(void) dm_strncpy(state->token, token, sizeof(state->token));
|
||||
memcpy(prev_token, state->token, 128);
|
||||
strncpy(state->token, token, 128);
|
||||
state->token[127] = 0;
|
||||
state->update_begin = _monotonic_seconds();
|
||||
state->update_timeout = update_timeout;
|
||||
state->update_pid = pid;
|
||||
@@ -2722,8 +2728,9 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
(int)(_monotonic_seconds() - state->update_begin),
|
||||
state->update_cmd);
|
||||
|
||||
(void) dm_strncpy(prev_token, state->token, sizeof(prev_token));
|
||||
(void) dm_strncpy(state->token, token, sizeof(state->token));
|
||||
memcpy(prev_token, state->token, 128);
|
||||
strncpy(state->token, token, 128);
|
||||
state->token[127] = 0;
|
||||
state->update_begin = _monotonic_seconds();
|
||||
state->update_timeout = update_timeout;
|
||||
state->update_pid = pid;
|
||||
@@ -2755,8 +2762,9 @@ static response handler(daemon_state s, client_handle h, request r)
|
||||
(int)(_monotonic_seconds() - state->update_begin),
|
||||
state->update_pid, token);
|
||||
|
||||
(void) dm_strncpy(prev_token, state->token, sizeof(prev_token));
|
||||
(void) dm_strncpy(state->token, token, sizeof(state->token));
|
||||
memcpy(prev_token, state->token, 128);
|
||||
strncpy(state->token, token, 128);
|
||||
state->token[127] = 0;
|
||||
state->update_begin = 0;
|
||||
state->update_timeout = 0;
|
||||
state->update_pid = 0;
|
||||
|
@@ -27,8 +27,6 @@ ifeq ("@BUILD_LOCKDDLM@", "yes")
|
||||
LOCK_LIBS += -ldlm_lt
|
||||
endif
|
||||
|
||||
SOURCES2 = lvmlockctl.c
|
||||
|
||||
TARGETS = lvmlockd lvmlockctl
|
||||
|
||||
.PHONY: install_lvmlockd
|
||||
|
@@ -699,7 +699,7 @@ int lm_hosts_dlm(struct lockspace *ls, int notify)
|
||||
return 0;
|
||||
|
||||
memset(ls_nodes_path, 0, sizeof(ls_nodes_path));
|
||||
snprintf(ls_nodes_path, PATH_MAX, "%s/%s/nodes",
|
||||
snprintf(ls_nodes_path, PATH_MAX-1, "%s/%s/nodes",
|
||||
DLM_LOCKSPACES_PATH, ls->name);
|
||||
|
||||
if (!(ls_dir = opendir(ls_nodes_path)))
|
||||
|
@@ -294,36 +294,6 @@ out:
|
||||
return host_id;
|
||||
}
|
||||
|
||||
/* Prepare valid /dev/mapper/vgname-lvname with all the mangling */
|
||||
static int build_dm_path(char *path, size_t path_len,
|
||||
const char *vg_name, const char *lv_name)
|
||||
{
|
||||
struct dm_pool *mem;
|
||||
char *dm_name;
|
||||
int rv = 0;
|
||||
|
||||
if (!(mem = dm_pool_create("namepool", 1024))) {
|
||||
log_error("Failed to create mempool.");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!(dm_name = dm_build_dm_name(mem, vg_name, lv_name, NULL))) {
|
||||
log_error("Failed to build dm name for %s/%s.", vg_name, lv_name);
|
||||
rv = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((dm_snprintf(path, path_len, "%s/%s", dm_dir(), dm_name) < 0)) {
|
||||
log_error("Failed to create path %s/%s.", dm_dir(), dm_name);
|
||||
rv = -EINVAL;
|
||||
}
|
||||
|
||||
fail:
|
||||
dm_pool_destroy(mem);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* vgcreate
|
||||
*
|
||||
@@ -366,8 +336,7 @@ int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_ar
|
||||
if (strlen(lock_lv_name) + strlen(lock_args_version) + 2 > MAX_ARGS)
|
||||
return -EARGS;
|
||||
|
||||
if ((rv = build_dm_path(disk.path, SANLK_PATH_LEN, vg_name, lock_lv_name)))
|
||||
return rv;
|
||||
snprintf(disk.path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s", vg_name, lock_lv_name);
|
||||
|
||||
log_debug("S %s init_vg_san path %s", ls_name, disk.path);
|
||||
|
||||
@@ -544,8 +513,7 @@ int lm_init_lv_sanlock(char *ls_name, char *vg_name, char *lv_name,
|
||||
|
||||
strncpy(rd.rs.lockspace_name, ls_name, SANLK_NAME_LEN);
|
||||
rd.rs.num_disks = 1;
|
||||
if ((rv = build_dm_path(rd.rs.disks[0].path, SANLK_PATH_LEN, vg_name, lock_lv_name)))
|
||||
return rv;
|
||||
snprintf(rd.rs.disks[0].path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s", vg_name, lock_lv_name);
|
||||
|
||||
align_size = sanlock_align(&rd.rs.disks[0]);
|
||||
if (align_size <= 0) {
|
||||
@@ -644,8 +612,7 @@ int lm_rename_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_
|
||||
return rv;
|
||||
}
|
||||
|
||||
if ((rv = build_dm_path(disk.path, SANLK_PATH_LEN, vg_name, lock_lv_name)))
|
||||
return rv;
|
||||
snprintf(disk.path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s", vg_name, lock_lv_name);
|
||||
|
||||
log_debug("S %s rename_vg_san path %s", ls_name, disk.path);
|
||||
|
||||
@@ -1102,10 +1069,10 @@ int lm_prepare_lockspace_sanlock(struct lockspace *ls)
|
||||
* and appending "lockctl" to get /path/to/lvmlockctl.
|
||||
*/
|
||||
memset(killpath, 0, sizeof(killpath));
|
||||
snprintf(killpath, SANLK_PATH_LEN, "%slockctl", LVM_PATH);
|
||||
snprintf(killpath, SANLK_PATH_LEN - 1, "%slockctl", LVM_PATH);
|
||||
|
||||
memset(killargs, 0, sizeof(killargs));
|
||||
snprintf(killargs, SANLK_PATH_LEN, "--kill %s", ls->vg_name);
|
||||
snprintf(killargs, SANLK_PATH_LEN - 1, "--kill %s", ls->vg_name);
|
||||
|
||||
rv = check_args_version(ls->vg_args, VG_LOCK_ARGS_MAJOR);
|
||||
if (rv < 0) {
|
||||
@@ -1121,8 +1088,8 @@ int lm_prepare_lockspace_sanlock(struct lockspace *ls)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((ret = build_dm_path(disk_path, SANLK_PATH_LEN, ls->vg_name, lock_lv_name)))
|
||||
goto fail;
|
||||
snprintf(disk_path, SANLK_PATH_LEN-1, "/dev/mapper/%s-%s",
|
||||
ls->vg_name, lock_lv_name);
|
||||
|
||||
/*
|
||||
* When a vg is started, the internal sanlock lv should be
|
||||
|
@@ -1,248 +0,0 @@
|
||||
#include "target.h"
|
||||
|
||||
// For DM_ARRAY_SIZE!
|
||||
#include "libdevmapper.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static char *_tok_cpy(const char *b, const char *e)
|
||||
{
|
||||
char *new = malloc((e - b) + 1);
|
||||
char *ptr = new;
|
||||
|
||||
if (new) {
|
||||
while (b != e)
|
||||
*ptr++ = *b++;
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
static bool _tok_eq(const char *b, const char *e, const char *str)
|
||||
{
|
||||
while (b != e) {
|
||||
if (!*str || *b != *str)
|
||||
return false;
|
||||
|
||||
b++;
|
||||
str++;
|
||||
}
|
||||
|
||||
return !*str;
|
||||
}
|
||||
|
||||
static bool _parse_operating_mode(const char *b, const char *e, void *context)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
enum vdo_operating_mode mode;
|
||||
} _table[] = {
|
||||
{"recovering", VDO_MODE_RECOVERING},
|
||||
{"read-only", VDO_MODE_READ_ONLY},
|
||||
{"normal", VDO_MODE_NORMAL}
|
||||
};
|
||||
|
||||
enum vdo_operating_mode *r = context;
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_table); i++) {
|
||||
if (_tok_eq(b, e, _table[i].str)) {
|
||||
*r = _table[i].mode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_compression_state(const char *b, const char *e, void *context)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
enum vdo_compression_state state;
|
||||
} _table[] = {
|
||||
{"online", VDO_COMPRESSION_ONLINE},
|
||||
{"offline", VDO_COMPRESSION_OFFLINE}
|
||||
};
|
||||
|
||||
enum vdo_compression_state *r = context;
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_table); i++) {
|
||||
if (_tok_eq(b, e, _table[i].str)) {
|
||||
*r = _table[i].state;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_recovering(const char *b, const char *e, void *context)
|
||||
{
|
||||
bool *r = context;
|
||||
|
||||
if (_tok_eq(b, e, "recovering"))
|
||||
*r = true;
|
||||
|
||||
else if (_tok_eq(b, e, "-"))
|
||||
*r = false;
|
||||
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _parse_index_state(const char *b, const char *e, void *context)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
enum vdo_index_state state;
|
||||
} _table[] = {
|
||||
{"error", VDO_INDEX_ERROR},
|
||||
{"closed", VDO_INDEX_CLOSED},
|
||||
{"opening", VDO_INDEX_OPENING},
|
||||
{"closing", VDO_INDEX_CLOSING},
|
||||
{"offline", VDO_INDEX_OFFLINE},
|
||||
{"online", VDO_INDEX_ONLINE},
|
||||
{"unknown", VDO_INDEX_UNKNOWN}
|
||||
};
|
||||
|
||||
enum vdo_index_state *r = context;
|
||||
unsigned i;
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_table); i++) {
|
||||
if (_tok_eq(b, e, _table[i].str)) {
|
||||
*r = _table[i].state;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _parse_uint64(const char *b, const char *e, void *context)
|
||||
{
|
||||
uint64_t *r = context, n;
|
||||
|
||||
n = 0;
|
||||
while (b != e) {
|
||||
if (!isdigit(*b))
|
||||
return false;
|
||||
|
||||
n = (n * 10) + (*b - '0');
|
||||
b++;
|
||||
}
|
||||
|
||||
*r = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *_eat_space(const char *b, const char *e)
|
||||
{
|
||||
while (b != e && isspace(*b))
|
||||
b++;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
static const char *_next_tok(const char *b, const char *e)
|
||||
{
|
||||
const char *te = b;
|
||||
while (te != e && !isspace(*te))
|
||||
te++;
|
||||
|
||||
return te == b ? NULL : te;
|
||||
}
|
||||
|
||||
static void _set_error(struct vdo_status_parse_result *result, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
|
||||
static void _set_error(struct vdo_status_parse_result *result, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(result->error, sizeof(result->error), fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static bool _parse_field(const char **b, const char *e,
|
||||
bool (*p_fn)(const char *, const char *, void *),
|
||||
void *field, const char *field_name,
|
||||
struct vdo_status_parse_result *result)
|
||||
{
|
||||
const char *te;
|
||||
|
||||
te = _next_tok(*b, e);
|
||||
if (!te) {
|
||||
_set_error(result, "couldn't get token for '%s'", field_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!p_fn(*b, te, field)) {
|
||||
_set_error(result, "couldn't parse '%s'", field_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
*b = _eat_space(te, e);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool vdo_status_parse(const char *input, struct vdo_status_parse_result *result)
|
||||
{
|
||||
const char *b = b = input;
|
||||
const char *e = input + strlen(input);
|
||||
const char *te;
|
||||
struct vdo_status *s = malloc(sizeof(*s));
|
||||
|
||||
if (!s) {
|
||||
_set_error(result, "out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
b = _eat_space(b, e);
|
||||
te = _next_tok(b, e);
|
||||
if (!te) {
|
||||
_set_error(result, "couldn't get token for device");
|
||||
free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
s->device = _tok_cpy(b, te);
|
||||
if (!s->device) {
|
||||
_set_error(result, "out of memory");
|
||||
free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
b = _eat_space(te, e);
|
||||
|
||||
#define XX(p, f, fn) if (!_parse_field(&b, e, p, f, fn, result)) goto bad;
|
||||
XX(_parse_operating_mode, &s->operating_mode, "operating mode");
|
||||
XX(_parse_recovering, &s->recovering, "recovering");
|
||||
XX(_parse_index_state, &s->index_state, "index state");
|
||||
XX(_parse_compression_state, &s->compression_state, "compression state");
|
||||
XX(_parse_uint64, &s->used_blocks, "used blocks");
|
||||
XX(_parse_uint64, &s->total_blocks, "total blocks");
|
||||
#undef XX
|
||||
|
||||
if (b != e) {
|
||||
_set_error(result, "too many tokens");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
result->status = s;
|
||||
return true;
|
||||
|
||||
bad:
|
||||
free(s->device);
|
||||
free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_MAPPER_VDO_TARGET_H
|
||||
#define DEVICE_MAPPER_VDO_TARGET_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
enum vdo_operating_mode {
|
||||
VDO_MODE_RECOVERING,
|
||||
VDO_MODE_READ_ONLY,
|
||||
VDO_MODE_NORMAL
|
||||
};
|
||||
|
||||
enum vdo_compression_state {
|
||||
VDO_COMPRESSION_ONLINE,
|
||||
VDO_COMPRESSION_OFFLINE
|
||||
};
|
||||
|
||||
enum vdo_index_state {
|
||||
VDO_INDEX_ERROR,
|
||||
VDO_INDEX_CLOSED,
|
||||
VDO_INDEX_OPENING,
|
||||
VDO_INDEX_CLOSING,
|
||||
VDO_INDEX_OFFLINE,
|
||||
VDO_INDEX_ONLINE,
|
||||
VDO_INDEX_UNKNOWN
|
||||
};
|
||||
|
||||
struct vdo_status {
|
||||
char *device;
|
||||
enum vdo_operating_mode operating_mode;
|
||||
bool recovering;
|
||||
enum vdo_index_state index_state;
|
||||
enum vdo_compression_state compression_state;
|
||||
uint64_t used_blocks;
|
||||
uint64_t total_blocks;
|
||||
};
|
||||
|
||||
void vdo_status_destroy(struct vdo_status *s);
|
||||
|
||||
#define VDO_MAX_ERROR 256
|
||||
|
||||
struct vdo_status_parse_result {
|
||||
char error[VDO_MAX_ERROR];
|
||||
struct vdo_status *status;
|
||||
};
|
||||
|
||||
// Parses the status line from the kernel target.
|
||||
bool vdo_status_parse(const char *input, struct vdo_status_parse_result *result);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
215
doc/aio_design.txt
Normal file
215
doc/aio_design.txt
Normal file
@@ -0,0 +1,215 @@
|
||||
Introducing asynchronous I/O to LVM
|
||||
===================================
|
||||
|
||||
Issuing I/O asynchronously means instructing the kernel to perform specific
|
||||
I/O and return immediately without waiting for it to complete. The data
|
||||
is collected from the kernel later.
|
||||
|
||||
Advantages
|
||||
----------
|
||||
|
||||
A1. While waiting for the I/O to happen, the program could perform other
|
||||
operations.
|
||||
|
||||
A2. When LVM is searching for its Physical Volumes, it issues a small amount of
|
||||
I/O to a large number of disks. If this was issued in parallel the overall
|
||||
runtime might be shorter while there should be little effect on the cpu time.
|
||||
|
||||
A3. If more than one timeout occurs when accessing any devices, these can be
|
||||
taken in parallel, again reducing the runtime. This applies globally,
|
||||
not just while the code is searching for Physical Volumes, so reading,
|
||||
writing and committing the metadata may occasionally benefit too to some
|
||||
extent and there are probably maintenance advantages in using the same
|
||||
method of I/O throughout the main body of the code.
|
||||
|
||||
A4. By introducing a simple callback function mechanism, the conversion can be
|
||||
performed largely incrementally by first refactoring and continuing to
|
||||
use synchronous I/O with the callbacks performed immediately. This allows the
|
||||
callbacks to be introduced without changing the running sequence of the code
|
||||
initially. Future projects could refactor some of the calling sites to
|
||||
simplify the code structure and even eliminate some of the nesting.
|
||||
This allows each part of what might ultimately amount to a large change to be
|
||||
introduced and tested independently.
|
||||
|
||||
|
||||
Disadvantages
|
||||
-------------
|
||||
|
||||
D1. The resulting code may be more complex with more failure modes to
|
||||
handle. Mitigate by thorough auditing and testing, rolling out
|
||||
gradually, and offering a simple switch to revert to the old behaviour.
|
||||
|
||||
D2. The linux asynchronous I/O implementation is less mature than
|
||||
its synchronous I/O implementation and might show up problems that
|
||||
depend on the version of the kernel or library used. Fixes or
|
||||
workarounds for some of these might require kernel changes. For
|
||||
example, there are suggestions that despite being supposedly async,
|
||||
there are still cases where system calls can block. There might be
|
||||
resource dependencies on other processes running on the system that make
|
||||
it unsuitable for use while any devices are suspended. Mitigation
|
||||
as for D1.
|
||||
|
||||
D3. The error handling within callbacks becomes more complicated.
|
||||
However we know that existing call paths can already sometimes discard
|
||||
errors, sometimes deliberately, sometimes not, so this aspect is in need
|
||||
of a complete review anyway and the new approach will make the error
|
||||
handling more transparent. Aim initially for overall behaviour that is
|
||||
no worse than that of the existing code, then work on improving it
|
||||
later.
|
||||
|
||||
D4. The work will take a few weeks to code and test. This leads to a
|
||||
significant opportunity cost when compared against other enhancements
|
||||
that could be achieved in that time. However, the proof-of-concept work
|
||||
performed while writing this design has satisfied me that the work could
|
||||
proceed and be committed incrementally as a background task.
|
||||
|
||||
|
||||
Observations regarding LVM's I/O Architecture
|
||||
---------------------------------------------
|
||||
|
||||
H1. All device, metadata and config file I/O is constrained to pass through a
|
||||
single route in lib/device.
|
||||
|
||||
H2. The first step of the analysis was to instrument this code path with
|
||||
log_debug messages. I/O is split into the following categories:
|
||||
|
||||
"dev signatures",
|
||||
"PV labels",
|
||||
"VG metadata header",
|
||||
"VG metadata content",
|
||||
"extra VG metadata header",
|
||||
"extra VG metadata content",
|
||||
"LVM1 metadata",
|
||||
"pool metadata",
|
||||
"LV content",
|
||||
"logging",
|
||||
|
||||
H3. A bounce buffer is used for most I/O.
|
||||
|
||||
H4. Most callers finish using the supplied data before any further I/O is
|
||||
issued. The few that don't could be converted trivially to do so.
|
||||
|
||||
H5. There is one stream of I/O per metadata area on each device.
|
||||
|
||||
H6. Some reads fall at offsets close to immediately preceding reads, so it's
|
||||
possible to avoid these by caching one "block" per metadata area I/O stream.
|
||||
|
||||
H7. Simple analysis suggests a minimum aligned read size of 8k would deliver
|
||||
immediate gains from this caching. A larger size might perform worse because
|
||||
almost all the time the extra data read would not be used, but this can be
|
||||
re-examined and tuned after the code is in place.
|
||||
|
||||
|
||||
Proposal
|
||||
--------
|
||||
|
||||
P1. Retain the "single I/O path" but offer an asynchronous option.
|
||||
|
||||
P2. Eliminate the bounce buffer in most cases by improving alignment.
|
||||
|
||||
P3. Reduce the number of reads by always reading a minimum of an aligned
|
||||
8k block.
|
||||
|
||||
P4. Eliminate repeated reads by caching the last block read and changing
|
||||
the lib/device interface to return a pointer to read-only data within
|
||||
this block.
|
||||
|
||||
P5. Only perform these interface changes for code on the critical path
|
||||
for now by converting other code sites to use wrappers around the new
|
||||
interface.
|
||||
|
||||
P6. Treat asynchronous I/O as the interface of choice and optimise only
|
||||
for this case.
|
||||
|
||||
P7. Convert the callers on the critical path to pass callback functions
|
||||
to the device layer. These functions will be called later with the
|
||||
read-only data, a context pointer and a success/failure indicator.
|
||||
Where an existing function performs a sequence of I/O, this has the
|
||||
advantage of breaking up the large function into smaller ones and
|
||||
wrapping the parameters used into structures. While this might look
|
||||
rather messy and ad-hoc in the short-term, it's a first step towards
|
||||
breaking up confusingly long functions into component parts and wrapping
|
||||
the existing long parameter lists into more appropriate structures and
|
||||
refactoring these parts of the code.
|
||||
|
||||
P8. Limit the resources used by the asynchronous I/O by using two
|
||||
tunable parameters, one limiting the number of outstanding I/Os issued
|
||||
and another limiting the total amount of memory used.
|
||||
|
||||
P9. Provide a fallback option if asynchronous I/O is unavailable by
|
||||
sharing the code paths but issuing the I/O synchronously and calling the
|
||||
callback immediately.
|
||||
|
||||
P10. Only allocate the buffer for the I/O at the point where the I/O is
|
||||
about to be issued.
|
||||
|
||||
P11. If the thresholds are exceeded, add the request to a simple queue,
|
||||
and process it later after some I/O has completed.
|
||||
|
||||
|
||||
Future work
|
||||
-----------
|
||||
F1. Perform a complete review of the error tracking so that device
|
||||
failures are handled and reported more cleanly, extending the existing
|
||||
basic error counting mechanism.
|
||||
|
||||
F2. Consider whether some of the nested callbacks can be eliminated,
|
||||
which would allow for additional simplifications.
|
||||
|
||||
F3. Adjust the contents of the adhoc context structs into more logical
|
||||
arrangements and use them more widely.
|
||||
|
||||
F4. Perform wider refactoring of these areas of code.
|
||||
|
||||
|
||||
Testing considerations
|
||||
----------------------
|
||||
T1. The changes touch code on the device path, so a thorough re-test of
|
||||
the device layer is required. The new code needs a full audit down
|
||||
through the library layer into the kernel to check that all the error
|
||||
conditions that are currently implemented (such as EAGAIN) are handled
|
||||
sensibly. (LVM's I/O layer needs to remain as solid as we can make it.)
|
||||
|
||||
T2. The current test suite provides a reasonably broad range of coverage
|
||||
of this area but is far from comprehensive.
|
||||
|
||||
|
||||
Acceptance criteria
|
||||
-------------------
|
||||
A1. The current test suite should pass to the same extent as before the
|
||||
changes.
|
||||
|
||||
A2. When all debugging and logging is disabled, strace -c must show
|
||||
improvements e.g. the expected fewer number of reads.
|
||||
|
||||
A3. Running a range of commands under valgrind must not reveal any
|
||||
new leaks due to the changes.
|
||||
|
||||
A4. All new coverity reports from the change must be addressed.
|
||||
|
||||
A5. CPU time should be similar to that before, as the same work
|
||||
is being done overall, just in a different order.
|
||||
|
||||
A6. Tests need to show improved behaviour in targetted areas. For example,
|
||||
if several devices are slow and time out, the delays should occur
|
||||
in parallel and the elapsed time should be less than before.
|
||||
|
||||
|
||||
Release considerations
|
||||
----------------------
|
||||
R1. Async I/O should be widely available and largely reliable on linux
|
||||
nowadays (even though parts of its interface and implementation remain a
|
||||
matter of controversy) so we should try to make its use the default
|
||||
whereever it is supported. If certain types of systems have problems we
|
||||
should try to detect those cases and disable it automatically there.
|
||||
|
||||
R2. Because the implications of an unexpected problem in the new code
|
||||
could be severe for the people affected, the roll out needs to be gentle
|
||||
without a deadline to allow us plenty of time to gain confidence in the
|
||||
new code. Our own testing will only be able to cover a tiny fraction of
|
||||
the different setups our users have, so we need to look out for problems
|
||||
caused by this proactively and encourage people to test it on their own
|
||||
systems and report back. It must go into the tree near the start of a
|
||||
release cycle rather than at the end to provide time for our confidence
|
||||
in it to grow.
|
||||
|
@@ -1,338 +0,0 @@
|
||||
LVM disk reading
|
||||
|
||||
Reading disks happens in two phases. The first is a discovery phase,
|
||||
which determines what's on the disks. The second is a working phase,
|
||||
which does a particular job for the command.
|
||||
|
||||
|
||||
Phase 1: Discovery
|
||||
------------------
|
||||
|
||||
Read all the disks on the system to find out:
|
||||
- What are the LVM devices?
|
||||
- What VG's exist on those devices?
|
||||
|
||||
This phase is called "label scan" (although it reads and scans everything,
|
||||
not just the label.) It stores the information it discovers (what LVM
|
||||
devices exist, and what VGs exist on them) in lvmcache. The devs/VGs info
|
||||
in lvmcache is the starting point for phase two.
|
||||
|
||||
|
||||
Phase 1 in outline:
|
||||
|
||||
For each device:
|
||||
|
||||
a. Read the first <N> KB of the device. (N is configurable.)
|
||||
|
||||
b. Look for the lvm label_header in the first four sectors,
|
||||
if none exists, it's not an lvm device, so quit looking at it.
|
||||
(By default, label_header is in the second sector.)
|
||||
|
||||
c. Look at the pv_header, which follows the label_header.
|
||||
This tells us the location of VG metadata on the device.
|
||||
There can be 0, 1 or 2 copies of VG metadata. The first
|
||||
is always at the start of the device, the second (if used)
|
||||
is at the end.
|
||||
|
||||
d. Look at the first mda_header (location came from pv_header
|
||||
in the previous step). This is by default in sector 8,
|
||||
4096 bytes from the start of the device. This tells us the
|
||||
location of the actual VG metadata text.
|
||||
|
||||
e. Look at the first copy of the text VG metadata (location came
|
||||
from mda_header in the previous step). This is by default
|
||||
in sector 9, 4608 bytes from the start of the device.
|
||||
The VG metadata is only partially analyzed to create a basic
|
||||
summary of the VG.
|
||||
|
||||
f. Store an "info" entry in lvmcache for this device,
|
||||
indicating that it is an lvm device, and store a "vginfo"
|
||||
entry in lvmcache indicating the name of the VG seen
|
||||
in the metadata in step e.
|
||||
|
||||
g. If the pv_header in step c shows a second mda_header
|
||||
location at the end of the device, then read that as
|
||||
in step d, and repeat steps e-f for it.
|
||||
|
||||
At the end of phase 1, lvmcache will have a list of devices
|
||||
that belong to LVM, and a list of VG names that exist on
|
||||
those devices. Each device (info struct) is associated
|
||||
with the VG (vginfo struct) it is used in.
|
||||
|
||||
|
||||
Phase 1 in code:
|
||||
|
||||
The most relevant functions are listed for each step in the outline.
|
||||
|
||||
lvmcache_label_scan()
|
||||
label_scan()
|
||||
|
||||
. dev_cache_scan()
|
||||
choose which devices on the system to look at
|
||||
|
||||
. for each dev in dev_cache: bcache prefetch/read
|
||||
|
||||
. _process_block() to process data from bcache
|
||||
_find_lvm_header() checks if this is an lvm dev by looking at label_header
|
||||
_text_read() via ops->read() looks at mda/pv/vg data to populate lvmcache
|
||||
|
||||
. _read_mda_header_and_metadata()
|
||||
raw_read_mda_header()
|
||||
|
||||
. _read_mda_header_and_metadata()
|
||||
read_metadata_location()
|
||||
text_read_metadata_summary()
|
||||
config_file_read_fd()
|
||||
_read_vgsummary() via ops->read_vgsummary()
|
||||
|
||||
. _text_read(): lvmcache_add()
|
||||
[adds this device to list of lvm devices]
|
||||
_read_mda_header_and_metadata(): lvmcache_update_vgname_and_id()
|
||||
[adds the VG name to list of VGs]
|
||||
|
||||
|
||||
Phase 2: Work
|
||||
-------------
|
||||
|
||||
This phase carries out the operation requested by the command that was
|
||||
run.
|
||||
|
||||
Whereas the first phase is based on iterating through each device on the
|
||||
system, this phase is based on iterating through each VG name. The list
|
||||
of VG names comes from phase 1, which stored the list in lvmcache to be
|
||||
used by phase 2.
|
||||
|
||||
Some commands may need to iterate through all VG names, while others may
|
||||
need to iterate through just one or two.
|
||||
|
||||
This phase includes locking each VG as work is done on it, so that two
|
||||
commands do not interfere with each other.
|
||||
|
||||
|
||||
Phase 2 in outline:
|
||||
|
||||
For each VG name:
|
||||
|
||||
a. Lock the VG.
|
||||
|
||||
b. Repeat the phase 1 scan steps for each device in this VG.
|
||||
The phase 1 information in lvmcache may have changed because no VG lock
|
||||
was held during phase 1. So, repeat the phase 1 steps, but only for the
|
||||
devices in this VG. N.B. for commands that are just reporting data,
|
||||
we skip this step if the data from phase 1 was complete and consistent.
|
||||
|
||||
c. Get the list of on-disk metadata locations for this VG.
|
||||
Phase 1 created this list in lvmcache to be used here. At this
|
||||
point we copy it out of lvmcache. In the simple/common case,
|
||||
this is a list of devices in the VG. But, some devices may
|
||||
have 0 or 2 metadata locations instead of the default 1, so it
|
||||
is not always equal to the list of devices. We want to read
|
||||
every copy of the metadata for this VG.
|
||||
|
||||
d. For each metadata location on each device in the VG
|
||||
(the list from the previous step):
|
||||
|
||||
1) Look at the mda_header. The location of the mda_header was saved
|
||||
in the lvmcache info struct by phase 1 (where it came from the
|
||||
pv_header.) The mda_header tells us where the text VG metadata is
|
||||
located.
|
||||
|
||||
2) Look at the text VG metadata. The location came from mda_header
|
||||
in the previous step. The VG metadata is fully analyzed and used
|
||||
to create an in-memory 'struct volume_group'.
|
||||
|
||||
e. Compare the copies of VG metadata that were found in each location.
|
||||
If some copies are older, choose the newest one to use, and update
|
||||
any older copies.
|
||||
|
||||
f. Update details about the devices/VG in lvmcache.
|
||||
|
||||
g. Pass the 'vg' struct to the command-specific code to work with.
|
||||
|
||||
|
||||
Phase 2 in code:
|
||||
|
||||
The most relevant functions are listed for each step in the outline.
|
||||
|
||||
For each VG name:
|
||||
process_each_vg()
|
||||
|
||||
. vg_read()
|
||||
lock_vol()
|
||||
|
||||
. vg_read()
|
||||
lvmcache_label_rescan_vg() (if needed)
|
||||
[insert phase 1 steps for scanning devs, but only devs in this vg]
|
||||
|
||||
. vg_read()
|
||||
create_instance()
|
||||
_text_create_text_instance()
|
||||
_create_vg_text_instance()
|
||||
lvmcache_fid_add_mdas_vg()
|
||||
[Copies mda locations from info->mdas where it was saved
|
||||
by phase 1, into fid->metadata_areas_in_use. This is
|
||||
the key connection between phase 1 and phase 2.]
|
||||
|
||||
. dm_list_iterate_items(mda, &fid->metadata_areas_in_use)
|
||||
|
||||
. _vg_read_raw() via ops->vg_read()
|
||||
raw_read_mda_header()
|
||||
|
||||
. _vg_read_raw()
|
||||
text_read_metadata()
|
||||
config_file_read_fd()
|
||||
_read_vg() via ops->read_vg()
|
||||
|
||||
. return the 'vg' struct from vg_read() and use it to do
|
||||
command-specific work
|
||||
|
||||
|
||||
|
||||
Filter i/o
|
||||
----------
|
||||
|
||||
Some filters must be applied before reading a device, and other filters
|
||||
must be applied after reading a device. In all cases, the filters must be
|
||||
applied before lvm processes the device, i.e. before it looks for an lvm
|
||||
label.
|
||||
|
||||
1. Some filters need to be applied prior to reading any devices
|
||||
because the purpose of the filter is to avoid submitting any
|
||||
io on the excluded devices. The regex filter is the primary
|
||||
example. Other filters benefit from being applied prior to
|
||||
reading devices because they can tell which devices to
|
||||
exclude without doing io to the device. An example of this
|
||||
is the mpath filter.
|
||||
|
||||
2. Some filters need to be applied after reading a device because
|
||||
they are based on data/signatures seen on the device.
|
||||
The partitioned filter is an example of this; lvm needs to
|
||||
read a device to see if it has a partition table before it can
|
||||
know whether to exclude the device from further processing.
|
||||
|
||||
We apply filters from 1 before reading devices, and we apply filters from
|
||||
2 after populating bcache, but before processing the device (i.e. before
|
||||
checking for an lvm label, which is the first step in processing.)
|
||||
|
||||
The current implementation of this makes filters return -EAGAIN if they
|
||||
want to read the device, but bcache data is not yet available. This will
|
||||
happen when filtering runs prior to populating bcache. In this case the
|
||||
device is flagged. After bcache is populated, the filters are reapplied
|
||||
to the flagged devices. The filters which need to look at device content
|
||||
are now able to get it from bcache. Devices that do not pass filters at
|
||||
this point are excluded just like devices which were excluded earlier.
|
||||
|
||||
(Some filters from 2 can be skipped by consulting udev for the information
|
||||
instead of reading the device. This is not entirely reliable, so it is
|
||||
disabled by default with the config setting external_device_info_source.
|
||||
It may be worthwhile to change the filters to use the udev info as a hint,
|
||||
or only use udev info for filtering in reporting commands where
|
||||
inaccuracies are not a big problem.)
|
||||
|
||||
|
||||
|
||||
I/O Performance
|
||||
---------------
|
||||
|
||||
. 400 loop devices used as PVs
|
||||
. 40 VGs each with 10 PVs
|
||||
. each VG has one active LV
|
||||
. each of the 10 PVs in vg0 has an artificial 100 ms read delay
|
||||
. read/write/io_submit are system call counts using strace
|
||||
. old is lvm 2.2.175
|
||||
. new is lvm 2.2.178 (shortly before)
|
||||
|
||||
|
||||
Command: pvs
|
||||
------------
|
||||
old: 0m17.422s
|
||||
new: 0m0.331s
|
||||
|
||||
old: read 7773 write 497
|
||||
new: read 2807 write 495 io_submit 448
|
||||
|
||||
|
||||
Command: vgs
|
||||
------------
|
||||
old: 0m20.383s
|
||||
new: 0m0.325s
|
||||
|
||||
old: read 10684 write 129
|
||||
new: read 2807 write 129 io_submit 448
|
||||
|
||||
|
||||
Command: vgck vg0
|
||||
-----------------
|
||||
old: 0m16.212s
|
||||
new: 0m1.290s
|
||||
|
||||
old: read 6372 write 4
|
||||
new: read 2807 write 4 io_submit 458
|
||||
|
||||
|
||||
Command: lvcreate -n test -l1 -an vg0
|
||||
-------------------------------------
|
||||
old: 0m29.271s
|
||||
new: 0m1.351s
|
||||
|
||||
old: read 6503 write 39
|
||||
new: read 2808 write 9 io_submit 488
|
||||
|
||||
|
||||
Command: lvremove vg0/test
|
||||
--------------------------
|
||||
old: 0m29.262s
|
||||
new: 0m1.348s
|
||||
|
||||
old: read 6502 write 36
|
||||
new: read 2807 write 6 io_submit 488
|
||||
|
||||
|
||||
io_submit sources
|
||||
-----------------
|
||||
|
||||
vgs:
|
||||
reads:
|
||||
- 400 for each PV
|
||||
- 40 for each LV
|
||||
- 8 for other devs on the system
|
||||
|
||||
vgck vg0:
|
||||
reads:
|
||||
- 400 for each PV
|
||||
- 40 for each LV
|
||||
- 10 for each PV in vg0 (rescan)
|
||||
- 8 for other devs on the system
|
||||
|
||||
lvcreate -n test -l1 -an vg0
|
||||
reads:
|
||||
- 400 for each PV
|
||||
- 40 for each LV
|
||||
- 10 for each PV in vg0 (rescan)
|
||||
- 8 for other devs on the system
|
||||
writes:
|
||||
- 10 for metadata on each PV in vg0
|
||||
- 10 for precommit on each PV in vg0
|
||||
- 10 for commit on each PV in vg0
|
||||
|
||||
|
||||
|
||||
With lvmetad
|
||||
------------
|
||||
|
||||
Command: pvs
|
||||
------------
|
||||
old: 0m5.405s
|
||||
new: 0m1.404s
|
||||
|
||||
Command: vgs
|
||||
------------
|
||||
old: 0m0.222s
|
||||
new: 0m0.223s
|
||||
|
||||
Command: lvcreate -n test -l1 -an vg0
|
||||
-------------------------------------
|
||||
old: 0m10.128s
|
||||
new: 0m1.137s
|
||||
|
||||
|
@@ -1,158 +0,0 @@
|
||||
Over time, I'd like to refactor the LVM code into these high level modules.
|
||||
|
||||
|
||||
+-------------------------------------------+
|
||||
| |
|
||||
| User Interface |
|
||||
| |
|
||||
| |
|
||||
+-------------------+-----------------------+
|
||||
|
|
||||
+--------------------v-----------------------+
|
||||
| |
|
||||
| LVM Core |
|
||||
| |
|
||||
| |
|
||||
+----+----------------+-----------------+----+
|
||||
| | |
|
||||
+-----v-----+ +-----v------+ +------v----+
|
||||
| | | | | |
|
||||
| Device | | Metadata | | System |
|
||||
| Mapper | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
+-----------+ +------------+ +-----------+
|
||||
|
||||
+---------------------------------------------------------+
|
||||
|
||||
|
||||
+------------------------------------+
|
||||
| |
|
||||
| Base |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+------------------------------------+
|
||||
|
||||
Going from the bottom up we have:
|
||||
|
||||
Base
|
||||
----
|
||||
|
||||
This holds all our general purpose code such as data structures, regex engine,
|
||||
memory allocators. In fact pretty much everything in libdevmapper apart from
|
||||
the dm code and config.
|
||||
|
||||
This can be used by any code in the system, which is why I've drawn a line
|
||||
between it and the code above rather than using arrows.
|
||||
|
||||
If anyone can come up with a better name please do. I'm trying to stay away
|
||||
from 'utils'.
|
||||
|
||||
|
||||
Device mapper
|
||||
-------------
|
||||
|
||||
As well as the low level dm-ioctl driving code we need to have all our dm 'best
|
||||
practise' stuff in here. For instance this is the code that decides to use the
|
||||
mirror target to move some data around; that knows to suspend a thin volume
|
||||
before taking a snapshot of it. This module is going to have a lot more code
|
||||
in it than the current libdevmapper.
|
||||
|
||||
It should not know anything about the LVM abstractions or metadata (no PVs, LVs
|
||||
or VGs). It just knows about the dm world.
|
||||
|
||||
Code in here is only allowed to use base.
|
||||
|
||||
|
||||
Metadata model
|
||||
--------------
|
||||
|
||||
Here we have all the format handling, labelling, config parsing etc. We try
|
||||
and put *everything* to do with LVM in here that doesn't actually require dm.
|
||||
|
||||
|
||||
System
|
||||
------
|
||||
|
||||
Code that interfaces with the system (udev etc).
|
||||
|
||||
|
||||
LVM Core
|
||||
--------
|
||||
|
||||
[terrible name]
|
||||
|
||||
This ties together the last 3 units. It should just be glue. We need to be
|
||||
strict about pushing code down from here to keep this as small as possible.
|
||||
|
||||
|
||||
User interface
|
||||
--------------
|
||||
|
||||
Self explanatory.
|
||||
|
||||
|
||||
Headers
|
||||
-------
|
||||
|
||||
Headers will be included using sub directories to make it clearer where they
|
||||
are in the tree.
|
||||
|
||||
eg,
|
||||
#include "base/mm/pool.h"
|
||||
#include "base/data-struct/list.h"
|
||||
#include "dm/thin-provisioning.h"
|
||||
#include "core/pvmove.h"
|
||||
|
||||
|
||||
Getting there
|
||||
=============
|
||||
|
||||
+-------------------------------------------+
|
||||
| |
|
||||
| |
|
||||
| Tools |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+---------+------------------------------+--+
|
||||
| |
|
||||
| +---------------v---------------------------+
|
||||
| | |
|
||||
| | |
|
||||
| | Lib |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| | |
|
||||
| +----------------+--------------------------+
|
||||
| |
|
||||
| |
|
||||
+-----v-------------------------------v-----+
|
||||
| |
|
||||
| |
|
||||
| libdevmapper |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
+-------------------------------------------+
|
||||
|
||||
This is where I see us now.
|
||||
|
||||
'base' should be easy to factor out, it's just the non-dm part of libdevmapper
|
||||
(ie. the bulk of it). But we have the problem that libdevmapper is a public
|
||||
interface to get round.
|
||||
|
||||
'lib' is where the bulk of our code currently is. Dependency-wise the code is
|
||||
a bit like a ball of string. So splitting it up is going to take time. We can
|
||||
probably pull code pretty quickly into the 'metadata model' dir. But factoring
|
||||
out the dm best practises stuff is going to require splitting at least
|
||||
files, and probably functions. Certainly not something that can be done in one
|
||||
go. System should just be a question of cherry picking functions.
|
||||
|
||||
I'm not too familiar with the tools dir. Hopefully it just corresponds with
|
||||
the User Interface module and doesn't contain any business logic.
|
@@ -1,257 +0,0 @@
|
||||
Building unit tests
|
||||
===================
|
||||
|
||||
make unit-unit/unit-test
|
||||
|
||||
|
||||
Running unit tests
|
||||
==================
|
||||
|
||||
The tests leave no artifacts at the moment, so you can just run
|
||||
unit-test/unit-test from wherever you want.
|
||||
|
||||
./unit-test <list|run> [pattern]
|
||||
|
||||
Listing tests
|
||||
-------------
|
||||
|
||||
Every test has a symbolic path associated with it. Just like file paths they
|
||||
are split into components separated by '/'s. The 'list' command will show you
|
||||
a tree of these tests, along with some description text.
|
||||
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list
|
||||
base
|
||||
data-struct
|
||||
bitset
|
||||
and ................................................. and all bits
|
||||
equal ............................................... equality
|
||||
get_next ............................................ get next set bit
|
||||
list
|
||||
splice .............................................. joining lists together
|
||||
string
|
||||
asprint ............................................. tests asprint
|
||||
strncpy ............................................. tests string copying
|
||||
device
|
||||
bcache
|
||||
block-size-multiple-page ............................ block size must be a multiple of page size
|
||||
block-size-positive ................................. block size must be positive
|
||||
blocks-get-evicted .................................. block get evicted with many reads
|
||||
cache-blocks-positive ............................... nr cache blocks must be positive
|
||||
create-destroy ...................................... simple create/destroy
|
||||
flush-waits ......................................... flush waits for all dirty
|
||||
get-reads ........................................... bcache_get() triggers read
|
||||
prefetch-never-waits ................................ too many prefetches does not trigger a wait
|
||||
prefetch-reads ...................................... prefetch issues a read
|
||||
read-multiple-files ................................. read from multiple files
|
||||
reads-cached ........................................ repeated reads are cached
|
||||
writeback-occurs .................................... dirty data gets written back
|
||||
zero-flag-dirties ................................... zeroed data counts as dirty
|
||||
formatting
|
||||
percent
|
||||
0 ................................................... Pretty printing of percentages near 0%
|
||||
100 ................................................. Pretty printing of percentages near 100%
|
||||
regex
|
||||
fingerprints .......................................... not sure
|
||||
matching .............................................. test the matcher with a variety of regexes
|
||||
dm
|
||||
target
|
||||
mirror
|
||||
status .............................................. parsing mirror status
|
||||
metadata
|
||||
config
|
||||
cascade ............................................... cascade
|
||||
clone ................................................. duplicating a config tree
|
||||
parse ................................................. parsing various
|
||||
|
||||
|
||||
An optional 'pattern' argument may be specified to select subsets of tests.
|
||||
This pattern is a posix regex and does a substring match, so you will need to
|
||||
use anchors if you particularly want the match at the beginning or end of the
|
||||
string.
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list data-struct
|
||||
base
|
||||
data-struct
|
||||
bitset
|
||||
and ................................................. and all bits
|
||||
equal ............................................... equality
|
||||
get_next ............................................ get next set bit
|
||||
list
|
||||
splice .............................................. joining lists together
|
||||
string
|
||||
asprint ............................................. tests asprint
|
||||
strncpy ............................................. tests string copying
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test list s$
|
||||
base
|
||||
device
|
||||
bcache
|
||||
flush-waits ......................................... flush waits for all dirty
|
||||
get-reads ........................................... bcache_get() triggers read
|
||||
prefetch-never-waits ................................ too many prefetches does not trigger a wait
|
||||
prefetch-reads ...................................... prefetch issues a read
|
||||
read-multiple-files ................................. read from multiple files
|
||||
writeback-occurs .................................... dirty data gets written back
|
||||
zero-flag-dirties ................................... zeroed data counts as dirty
|
||||
regex
|
||||
fingerprints .......................................... not sure
|
||||
dm
|
||||
target
|
||||
mirror
|
||||
status .............................................. parsing mirror status
|
||||
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
'make run-unit-test' from the top level will run all unit tests. But I tend to
|
||||
run it by hand to I can select just the tests I'm working on.
|
||||
|
||||
Use the 'run' command to run the tests. Currently all logging goes to stderr,
|
||||
so the test runner prints a line at the start of the test and a line
|
||||
indicating success or failure at the end.
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test run bcache/block-size
|
||||
[RUN ] /base/device/bcache/block-size-multiple-page
|
||||
bcache block size must be a multiple of page size
|
||||
bcache block size must be a multiple of page size
|
||||
bcache block size must be a multiple of page size
|
||||
bcache block size must be a multiple of page size
|
||||
[ OK] /base/device/bcache/block-size-multiple-page
|
||||
|
||||
[RUN ] /base/device/bcache/block-size-positive
|
||||
bcache must have a non zero block size
|
||||
[ OK] /base/device/bcache/block-size-positive
|
||||
|
||||
|
||||
2/2 tests passed
|
||||
|
||||
|
||||
ejt@devel-vm1:~/lvm2/unit-test/$ ./unit-test run data-struct
|
||||
[RUN ] /base/data-struct/bitset/and
|
||||
[ OK] /base/data-struct/bitset/and
|
||||
|
||||
[RUN ] /base/data-struct/bitset/equal
|
||||
[ OK] /base/data-struct/bitset/equal
|
||||
|
||||
[RUN ] /base/data-struct/bitset/get_next
|
||||
[ OK] /base/data-struct/bitset/get_next
|
||||
|
||||
[RUN ] /base/data-struct/list/splice
|
||||
[ OK] /base/data-struct/list/splice
|
||||
|
||||
[RUN ] /base/data-struct/string/asprint
|
||||
[ OK] /base/data-struct/string/asprint
|
||||
|
||||
[RUN ] /base/data-struct/string/strncpy
|
||||
[ OK] /base/data-struct/string/strncpy
|
||||
|
||||
|
||||
6/6 tests passed
|
||||
|
||||
|
||||
Writing tests
|
||||
=============
|
||||
|
||||
[See unit-test/framework.h and unit-test/units.h for the details]
|
||||
|
||||
Tests are grouped together into 'suites', all tests in a suite share a
|
||||
'fixture'. A fixture is a void * to any object you want; use it to set up any
|
||||
common environment that you need for the tests to run (eg, creating a dm_pool).
|
||||
|
||||
Test suites have nothing to do with the test paths, you can have tests from
|
||||
different suites with similar paths, the runner sorts things for you.
|
||||
|
||||
Put your tests in a file in unit-test/, with '_t' at the end of the name
|
||||
(convention only, nothing relies on this).
|
||||
|
||||
#include "units.h"
|
||||
|
||||
Then write any fixtures you need:
|
||||
|
||||
eg,
|
||||
static void *_mem_init(void) {
|
||||
struct dm_pool *mem = dm_pool_create("bitset test", 1024);
|
||||
if (!mem) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
static void _mem_exit(void *mem)
|
||||
{
|
||||
dm_pool_destroy(mem);
|
||||
}
|
||||
|
||||
Then write your tests, which should take the void * that was returned by your
|
||||
fixture. Use the T_ASSERT* macros to indicate failure.
|
||||
|
||||
eg,
|
||||
static void test_equal(void *fixture)
|
||||
{
|
||||
struct dm_pool *mem = fixture;
|
||||
dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS);
|
||||
dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS);
|
||||
|
||||
int i, j;
|
||||
for (i = 0, j = 1; i < NR_BITS; i += j, j++) {
|
||||
dm_bit_set(bs1, i);
|
||||
dm_bit_set(bs2, i);
|
||||
}
|
||||
|
||||
T_ASSERT(dm_bitset_equal(bs1, bs2));
|
||||
T_ASSERT(dm_bitset_equal(bs2, bs1));
|
||||
|
||||
for (i = 0; i < NR_BITS; i++) {
|
||||
bit_flip(bs1, i);
|
||||
T_ASSERT(!dm_bitset_equal(bs1, bs2));
|
||||
T_ASSERT(!dm_bitset_equal(bs2, bs1));
|
||||
|
||||
T_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */
|
||||
bit_flip(bs1, i);
|
||||
}
|
||||
}
|
||||
|
||||
At the end of your test file you should write a function that builds one or
|
||||
more test suites and adds them to the list of all suites that is passed in. I
|
||||
tend to write a little macro (T) to save typing the same test path repeatedly.
|
||||
|
||||
eg,
|
||||
#define T(path, desc, fn) register_test(ts, "/base/data-struct/bitset/" path, desc, fn)
|
||||
|
||||
void bitset_tests(struct dm_list *all_tests)
|
||||
{
|
||||
struct test_suite *ts = test_suite_create(_mem_init, _mem_exit);
|
||||
if (!ts) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
T("get_next", "get next set bit", test_get_next);
|
||||
T("equal", "equality", test_equal);
|
||||
T("and", "and all bits", test_and);
|
||||
|
||||
dm_list_add(all_tests, &ts->list);
|
||||
}
|
||||
|
||||
Then you need to declare your registration function and call it in units.h.
|
||||
|
||||
|
||||
// Declare the function that adds tests suites here ...
|
||||
...
|
||||
void bitset_tests(struct dm_list *suites);
|
||||
...
|
||||
|
||||
// ... and call it in here.
|
||||
static inline void register_all_tests(struct dm_list *suites)
|
||||
{
|
||||
...
|
||||
bitset_tests(suites);
|
||||
...
|
||||
}
|
||||
|
||||
Finally add your test file to the Makefile.in and rerun configure.
|
||||
|
59
doc/vdo.md
59
doc/vdo.md
@@ -22,25 +22,17 @@ Usual limitations apply:
|
||||
- Never layer LUKS over another LUKS - it makes no sense.
|
||||
- LUKS is better over the raids, than under.
|
||||
|
||||
Devices which are not best suitable as backing device:
|
||||
|
||||
- thin volumes - at the moment it is not possible to take snapshot of active VDO volume on top of thin volume.
|
||||
|
||||
### Using VDO as a PV:
|
||||
|
||||
1. under tdata
|
||||
1. under tpool
|
||||
- The best fit - it will deduplicate additional redundancies among all
|
||||
snapshots and will reduce the footprint.
|
||||
- Risks: Resize! dmevent will not be able to handle resizing of tpool ATM.
|
||||
2. under corig
|
||||
- Cache fits better under VDO device - it will reduce amount of data, and
|
||||
deduplicate, so there should be more hits.
|
||||
- This is useful to keep the most frequently used data in cache
|
||||
uncompressed or without deduplication if that happens to be a bottleneck.
|
||||
- Cache may fit better under VDO device, depending on compressibility and
|
||||
amount of duplicates, as
|
||||
- compression will reduce amount of data, thus effectively increasing
|
||||
size of cache,
|
||||
- and deduplication may emphasize hotspots.
|
||||
- Performance testing of your particular workload is strongly recommended.
|
||||
uncompressed (if that happens to be a bottleneck.)
|
||||
3. under (multiple) linear LVs - e.g. used for VMs.
|
||||
|
||||
### And where VDO does not fit:
|
||||
@@ -58,47 +50,36 @@ Devices which are not best suitable as backing device:
|
||||
|
||||
- under snapshot CoW device - when there are multiple of those it could deduplicate
|
||||
|
||||
## Development
|
||||
|
||||
### Things to decide
|
||||
|
||||
- under integrity devices
|
||||
- VDO should work well for data blocks,
|
||||
- but hashes are mostly unique and not compressible - were it possible it
|
||||
would make sense to have separate imeta and idata volumes for integrity
|
||||
devices.
|
||||
- under integrity devices - it should work - mostly for data
|
||||
- hash is not compressible and unique - it makes sense to have separate imeta and idata volumes for integrity devices
|
||||
|
||||
### Future Integration of VDO into LVM:
|
||||
|
||||
One issue is using both LUKS and RAID under VDO. We have two options:
|
||||
|
||||
- use mdadm x LUKS x VDO+LV
|
||||
- use LV RAID x LUKS x VDO+LV
|
||||
- use LV RAID x LUKS x VDO+LV - still requiring recursive LVs.
|
||||
|
||||
In both cases dmeventd will not be able to resize the volume at the moment.
|
||||
Another issue is duality of VDO - it is a top level LV but it can be seen as a "pool" for multiple devices.
|
||||
|
||||
Another issue is duality of VDO - it can be used as a top level LV (with a
|
||||
filesystem on top) but it can be used as "pool" for multiple devices too.
|
||||
- This is one usecase which could not be handled by LVM at the moment.
|
||||
- Size of the VDO is its physical size and virtual size - just like tpool.
|
||||
- same problems with virtual vs physical size - it can get full, without exposing it fo a FS
|
||||
|
||||
This will be solved in similar way thin pools allow multiple volumes.
|
||||
Another possible RFE is to split data and metadata:
|
||||
|
||||
Also VDO, has two sizes - its physical size and virtual size - and when
|
||||
overprovisioning, just like tpool, we face same problems - VDO can get full,
|
||||
without exposing it to a FS. dmeventd monitoring will be needed.
|
||||
|
||||
Another possible RFE is to split data and metadata - keep data on HDD and metadata on SSD.
|
||||
- e.g. keep data on HDD and metadata on SSD
|
||||
|
||||
## Issues / Testing
|
||||
|
||||
- fstrim/discard pass down - does it work with VDO?
|
||||
- VDO can run in synchronous vs. asynchronous mode:
|
||||
- synchronous for devices where write is safe after it is confirmed. Some
|
||||
devices are lying.
|
||||
- asynchronous for devices requiring flush.
|
||||
- Multiple devices under VDO - need to find and expose common properties, or
|
||||
not allow grouping them together. (This is same for all volumes with more
|
||||
physical devices below.)
|
||||
- pvmove changing characteristics of underlying device.
|
||||
- autoactivation during boot?
|
||||
- Q: can we use VDO for RootFS? Dracut!
|
||||
- VDO can run in synchronous vs. asynchronous mode
|
||||
- synchronous for devices where write is safe after it is confirmed. Some devices are lying.
|
||||
- asynchronous for devices requiring flush
|
||||
- multiple devices under VDO - need to find common options
|
||||
- pvmove - changing characteristics of underlying device
|
||||
- autoactivation during boot
|
||||
- Q: can we use VDO for RootFS?
|
||||
|
||||
|
@@ -14,7 +14,6 @@
|
||||
@top_srcdir@/lib/config/defaults.h
|
||||
@top_srcdir@/lib/datastruct/btree.h
|
||||
@top_srcdir@/lib/datastruct/str_list.h
|
||||
@top_srcdir@/lib/device/bcache.h
|
||||
@top_srcdir@/lib/device/dev-cache.h
|
||||
@top_srcdir@/lib/device/dev-ext-udev-constants.h
|
||||
@top_srcdir@/lib/device/dev-type.h
|
||||
|
@@ -1,4 +1,7 @@
|
||||
/* include/configure.h.in. Generated from configure.ac by autoheader. */
|
||||
/* include/configure.h.in. Generated from configure.in by autoheader. */
|
||||
|
||||
/* Define to 1 if aio is available. */
|
||||
#undef AIO_SUPPORT
|
||||
|
||||
/* Define to 1 to use libblkid detection of signatures when wiping. */
|
||||
#undef BLKID_WIPING_SUPPORT
|
||||
@@ -249,9 +252,6 @@
|
||||
/* Define to 1 if you have the <langinfo.h> header file. */
|
||||
#undef HAVE_LANGINFO_H
|
||||
|
||||
/* Define to 1 if you have the <libaio.h> header file. */
|
||||
#undef HAVE_LIBAIO_H
|
||||
|
||||
/* Define to 1 if you have the <libcman.h> header file. */
|
||||
#undef HAVE_LIBCMAN_H
|
||||
|
||||
@@ -350,6 +350,9 @@
|
||||
/* Define to 1 if the system has the type `ptrdiff_t'. */
|
||||
#undef HAVE_PTRDIFF_T
|
||||
|
||||
/* Define to 1 if the compiler has the `__builtin_clz` builtin. */
|
||||
#undef HAVE___BUILTIN_CLZ
|
||||
|
||||
/* Define to 1 if you have the <readline/history.h> header file. */
|
||||
#undef HAVE_READLINE_HISTORY_H
|
||||
|
||||
@@ -478,16 +481,9 @@
|
||||
/* Define to 1 if you have the `strtoull' function. */
|
||||
#undef HAVE_STRTOULL
|
||||
|
||||
/* Define to 1 if `st_blocks' is a member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if `st_rdev' is a member of `struct stat'. */
|
||||
#undef HAVE_STRUCT_STAT_ST_RDEV
|
||||
|
||||
/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use
|
||||
`HAVE_STRUCT_STAT_ST_BLOCKS' instead. */
|
||||
#undef HAVE_ST_BLOCKS
|
||||
|
||||
/* Define to 1 if you have the <syslog.h> header file. */
|
||||
#undef HAVE_SYSLOG_H
|
||||
|
||||
@@ -559,9 +555,6 @@
|
||||
/* Define to 1 if you have the <sys/utsname.h> header file. */
|
||||
#undef HAVE_SYS_UTSNAME_H
|
||||
|
||||
/* Define to 1 if you have the <sys/vfs.h> header file. */
|
||||
#undef HAVE_SYS_VFS_H
|
||||
|
||||
/* Define to 1 if you have the <sys/wait.h> header file. */
|
||||
#undef HAVE_SYS_WAIT_H
|
||||
|
||||
@@ -601,9 +594,6 @@
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Define to 1 if the system has the `__builtin_clz' built-in function */
|
||||
#undef HAVE___BUILTIN_CLZ
|
||||
|
||||
/* Internalization package */
|
||||
#undef INTL_PACKAGE
|
||||
|
||||
|
@@ -24,6 +24,26 @@ ifeq ("@POOL@", "shared")
|
||||
SUBDIRS += format_pool
|
||||
endif
|
||||
|
||||
ifeq ("@SNAPSHOTS@", "shared")
|
||||
SUBDIRS += snapshot
|
||||
endif
|
||||
|
||||
ifeq ("@MIRRORS@", "shared")
|
||||
SUBDIRS += mirror
|
||||
endif
|
||||
|
||||
ifeq ("@RAID@", "shared")
|
||||
SUBDIRS += raid
|
||||
endif
|
||||
|
||||
ifeq ("@THIN@", "shared")
|
||||
SUBDIRS += thin
|
||||
endif
|
||||
|
||||
ifeq ("@CACHE@", "shared")
|
||||
SUBDIRS += cache_segtype
|
||||
endif
|
||||
|
||||
ifeq ("@CLUSTER@", "shared")
|
||||
SUBDIRS += locking
|
||||
endif
|
||||
@@ -31,13 +51,10 @@ endif
|
||||
SOURCES =\
|
||||
activate/activate.c \
|
||||
cache/lvmcache.c \
|
||||
cache_segtype/cache.c \
|
||||
commands/toolcontext.c \
|
||||
config/config.c \
|
||||
datastruct/btree.c \
|
||||
datastruct/str_list.c \
|
||||
device/bcache.c \
|
||||
device/bcache-utils.c \
|
||||
device/dev-cache.c \
|
||||
device/dev-ext.c \
|
||||
device/dev-io.c \
|
||||
@@ -46,7 +63,6 @@ SOURCES =\
|
||||
device/dev-type.c \
|
||||
device/dev-luks.c \
|
||||
device/dev-dasd.c \
|
||||
device/dev-lvm1-pool.c \
|
||||
display/display.c \
|
||||
error/errseg.c \
|
||||
unknown/unknown.c \
|
||||
@@ -61,7 +77,6 @@ SOURCES =\
|
||||
filters/filter-type.c \
|
||||
filters/filter-usable.c \
|
||||
filters/filter-internal.c \
|
||||
filters/filter-signature.c \
|
||||
format_text/archive.c \
|
||||
format_text/archiver.c \
|
||||
format_text/export.c \
|
||||
@@ -92,7 +107,6 @@ SOURCES =\
|
||||
metadata/snapshot_manip.c \
|
||||
metadata/thin_manip.c \
|
||||
metadata/vg.c \
|
||||
mirror/mirrored.c \
|
||||
misc/crc.c \
|
||||
misc/lvm-exec.c \
|
||||
misc/lvm-file.c \
|
||||
@@ -106,12 +120,9 @@ SOURCES =\
|
||||
mm/memlock.c \
|
||||
notify/lvmnotify.c \
|
||||
properties/prop_common.c \
|
||||
raid/raid.c \
|
||||
report/properties.c \
|
||||
report/report.c \
|
||||
snapshot/snapshot.c \
|
||||
striped/striped.c \
|
||||
thin/thin.c \
|
||||
uuid/uuid.c \
|
||||
zero/zero.c
|
||||
|
||||
@@ -138,6 +149,26 @@ ifeq ("@CLUSTER@", "internal")
|
||||
SOURCES += locking/cluster_locking.c
|
||||
endif
|
||||
|
||||
ifeq ("@SNAPSHOTS@", "internal")
|
||||
SOURCES += snapshot/snapshot.c
|
||||
endif
|
||||
|
||||
ifeq ("@MIRRORS@", "internal")
|
||||
SOURCES += mirror/mirrored.c
|
||||
endif
|
||||
|
||||
ifeq ("@RAID@", "internal")
|
||||
SOURCES += raid/raid.c
|
||||
endif
|
||||
|
||||
ifeq ("@THIN@", "internal")
|
||||
SOURCES += thin/thin.c
|
||||
endif
|
||||
|
||||
ifeq ("@CACHE@", "internal")
|
||||
SOURCES += cache_segtype/cache.c
|
||||
endif
|
||||
|
||||
ifeq ("@DEVMAPPER@", "yes")
|
||||
SOURCES +=\
|
||||
activate/dev_manager.c \
|
||||
@@ -172,7 +203,12 @@ ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS =\
|
||||
format1 \
|
||||
format_pool \
|
||||
snapshot \
|
||||
mirror \
|
||||
notify \
|
||||
raid \
|
||||
thin \
|
||||
cache_segtype \
|
||||
locking
|
||||
endif
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -28,8 +28,6 @@
|
||||
#include "config.h"
|
||||
#include "segtype.h"
|
||||
#include "sharedlib.h"
|
||||
#include "lvmcache.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
@@ -608,7 +606,7 @@ int module_present(struct cmd_context *cmd, const char *target_name)
|
||||
#endif
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
int i = dm_snprintf(path, sizeof(path), "%smodule/dm_%s",
|
||||
int i = dm_snprintf(path, (sizeof(path) - 1), "%smodule/dm_%s",
|
||||
dm_sysfs_dir(), target_name);
|
||||
|
||||
if (i > 0) {
|
||||
@@ -1008,10 +1006,8 @@ int lv_raid_data_offset(const struct logical_volume *lv, uint64_t *data_offset)
|
||||
if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name, 1)))
|
||||
return_0;
|
||||
|
||||
if (!(r = dev_manager_raid_status(dm, lv, &status))) {
|
||||
dev_manager_destroy(dm);
|
||||
return_0;
|
||||
}
|
||||
if (!(r = dev_manager_raid_status(dm, lv, &status)))
|
||||
stack;
|
||||
|
||||
*data_offset = status->data_offset;
|
||||
|
||||
@@ -1547,11 +1543,8 @@ static int _lv_is_active(const struct logical_volume *lv,
|
||||
if (skip_cluster_query)
|
||||
goto out;
|
||||
|
||||
if ((r = cluster_lock_held(lv->lvid.s, "", &e)) >= 0) {
|
||||
if (l && e)
|
||||
r = 0; /* exclusive locally */
|
||||
if ((r = cluster_lock_held(lv->lvid.s, "", &e)) >= 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If lock query is not supported (due to interfacing with old
|
||||
@@ -1669,10 +1662,7 @@ static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd
|
||||
if (!(dmevh = dm_event_handler_create()))
|
||||
return_NULL;
|
||||
|
||||
if (!cmd->default_settings.dmeventd_executable)
|
||||
cmd->default_settings.dmeventd_executable = find_config_tree_str(cmd, dmeventd_executable_CFG, NULL);
|
||||
|
||||
if (dm_event_handler_set_dmeventd_path(dmevh, cmd->default_settings.dmeventd_executable))
|
||||
if (dm_event_handler_set_dmeventd_path(dmevh, find_config_tree_str(cmd, dmeventd_executable_CFG, NULL)))
|
||||
goto_bad;
|
||||
|
||||
if (dso && dm_event_handler_set_dso(dmevh, dso))
|
||||
@@ -1688,18 +1678,21 @@ static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd
|
||||
|
||||
bad:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, int id)
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath)
|
||||
{
|
||||
const char *libpath = find_config_tree_str(cmd, id, NULL);
|
||||
char path[PATH_MAX];
|
||||
char *path;
|
||||
|
||||
get_shared_library_path(cmd, libpath, path, sizeof(path));
|
||||
if (!(path = dm_pool_alloc(cmd->mem, PATH_MAX))) {
|
||||
log_error("Failed to allocate dmeventd library path.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dm_strdup(path);
|
||||
get_shared_library_path(cmd, libpath, path, PATH_MAX);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *_build_target_uuid(struct cmd_context *cmd, const struct logical_volume *lv)
|
||||
@@ -1716,18 +1709,13 @@ static char *_build_target_uuid(struct cmd_context *cmd, const struct logical_vo
|
||||
return build_dm_uuid(cmd->mem, lv, layer);
|
||||
}
|
||||
|
||||
static int _device_registered_with_dmeventd(struct cmd_context *cmd,
|
||||
const struct logical_volume *lv,
|
||||
const char **dso,
|
||||
int *pending, int *monitored)
|
||||
static int _device_registered_with_dmeventd(struct cmd_context *cmd, const struct logical_volume *lv, int *pending, const char **dso)
|
||||
{
|
||||
char *uuid;
|
||||
enum dm_event_mask evmask;
|
||||
enum dm_event_mask evmask = 0;
|
||||
struct dm_event_handler *dmevh;
|
||||
int r;
|
||||
|
||||
*pending = 0;
|
||||
*monitored = 0;
|
||||
|
||||
if (!(uuid = _build_target_uuid(cmd, lv)))
|
||||
return_0;
|
||||
@@ -1735,20 +1723,9 @@ static int _device_registered_with_dmeventd(struct cmd_context *cmd,
|
||||
if (!(dmevh = _create_dm_event_handler(cmd, uuid, NULL, 0, DM_EVENT_ALL_ERRORS)))
|
||||
return_0;
|
||||
|
||||
if ((r = dm_event_get_registered_device(dmevh, 0))) {
|
||||
if (r == -ENOENT) {
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/* FIXME: why do we care which 'dso' is monitoring? */
|
||||
if (dso && (*dso = dm_event_handler_get_dso(dmevh)) &&
|
||||
!(*dso = dm_pool_strdup(cmd->mem, *dso))) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
if (dm_event_get_registered_device(dmevh, 0)) {
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
evmask = dm_event_handler_get_event_mask(dmevh);
|
||||
@@ -1757,25 +1734,21 @@ static int _device_registered_with_dmeventd(struct cmd_context *cmd,
|
||||
evmask &= ~DM_EVENT_REGISTRATION_PENDING;
|
||||
}
|
||||
|
||||
*monitored = evmask;
|
||||
r = 1;
|
||||
out:
|
||||
if (dso && (*dso = dm_event_handler_get_dso(dmevh)) && !(*dso = dm_pool_strdup(cmd->mem, *dso)))
|
||||
log_error("Failed to duplicate dso name.");
|
||||
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return r;
|
||||
return evmask;
|
||||
}
|
||||
|
||||
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
const struct logical_volume *lv,
|
||||
int *pending, int *monitored)
|
||||
const struct logical_volume *lv, int *pending)
|
||||
{
|
||||
char *uuid;
|
||||
enum dm_event_mask evmask;
|
||||
enum dm_event_mask evmask = 0;
|
||||
struct dm_event_handler *dmevh;
|
||||
int r;
|
||||
|
||||
*pending = 0;
|
||||
*monitored = 0;
|
||||
|
||||
if (!dso)
|
||||
return_0;
|
||||
@@ -1786,13 +1759,9 @@ int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
if (!(dmevh = _create_dm_event_handler(cmd, uuid, dso, 0, DM_EVENT_ALL_ERRORS)))
|
||||
return_0;
|
||||
|
||||
if ((r = dm_event_get_registered_device(dmevh, 0))) {
|
||||
if (r == -ENOENT) {
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
r = 0;
|
||||
goto_out;
|
||||
if (dm_event_get_registered_device(dmevh, 0)) {
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
evmask = dm_event_handler_get_event_mask(dmevh);
|
||||
@@ -1801,12 +1770,9 @@ int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
evmask &= ~DM_EVENT_REGISTRATION_PENDING;
|
||||
}
|
||||
|
||||
*monitored = evmask;
|
||||
r = 1;
|
||||
out:
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return r;
|
||||
return evmask;
|
||||
}
|
||||
|
||||
int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv,
|
||||
@@ -1834,7 +1800,7 @@ int target_register_events(struct cmd_context *cmd, const char *dso, const struc
|
||||
if (!r)
|
||||
return_0;
|
||||
|
||||
log_verbose("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
|
||||
log_very_verbose("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1849,7 +1815,7 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
const struct lv_activate_opts *laopts, int monitor)
|
||||
{
|
||||
#ifdef DMEVENTD
|
||||
int i, pending = 0, monitored = 0;
|
||||
int i, pending = 0, monitored;
|
||||
int r = 1;
|
||||
struct dm_list *snh, *snht;
|
||||
struct lv_segment *seg;
|
||||
@@ -1857,7 +1823,6 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
int (*monitor_fn) (struct lv_segment *s, int e);
|
||||
uint32_t s;
|
||||
static const struct lv_activate_opts zlaopts = { 0 };
|
||||
struct lv_activate_opts mirr_laopts = { .origin_only = 1 };
|
||||
struct lvinfo info;
|
||||
const char *dso = NULL;
|
||||
int new_unmonitor;
|
||||
@@ -1959,7 +1924,9 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
continue;
|
||||
if (!monitor_dev_for_events(cmd, seg_lv(seg, s), NULL,
|
||||
monitor)) {
|
||||
stack;
|
||||
log_error("Failed to %smonitor %s",
|
||||
monitor ? "" : "un",
|
||||
display_lvname(seg_lv(seg, s)));
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
@@ -1994,21 +1961,11 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
!seg->segtype->ops->target_monitored) /* doesn't support registration */
|
||||
continue;
|
||||
|
||||
if (!monitor) {
|
||||
if (!monitor)
|
||||
/* When unmonitoring, obtain existing dso being used. */
|
||||
if (!_device_registered_with_dmeventd(cmd, seg_is_snapshot(seg) ? seg->cow : seg->lv,
|
||||
&dso, &pending, &monitored)) {
|
||||
log_warn("WARNING: Failed to %smonitor %s.",
|
||||
monitor ? "" : "un",
|
||||
display_lvname(seg_is_snapshot(seg) ? seg->cow : seg->lv));
|
||||
return 0;
|
||||
}
|
||||
} else if (!seg->segtype->ops->target_monitored(seg, &pending, &monitored)) {
|
||||
log_warn("WARNING: Failed to %smonitor %s.",
|
||||
monitor ? "" : "un",
|
||||
display_lvname(seg->lv));
|
||||
return 0;
|
||||
}
|
||||
monitored = _device_registered_with_dmeventd(cmd, seg_is_snapshot(seg) ? seg->cow : seg->lv, &pending, &dso);
|
||||
else
|
||||
monitored = seg->segtype->ops->target_monitored(seg, &pending);
|
||||
|
||||
/* FIXME: We should really try again if pending */
|
||||
monitored = (pending) ? 0 : monitored;
|
||||
@@ -2020,9 +1977,7 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
if (monitored)
|
||||
log_verbose("%s already monitored.", display_lvname(lv));
|
||||
else if (seg->segtype->ops->target_monitor_events) {
|
||||
log_very_verbose("Monitoring %s with %s.%s", display_lvname(lv),
|
||||
seg->segtype->dso,
|
||||
test_mode() ? " [Test mode: skipping this]" : "");
|
||||
log_verbose("Monitoring %s%s", display_lvname(lv), test_mode() ? " [Test mode: skipping this]" : "");
|
||||
monitor_fn = seg->segtype->ops->target_monitor_events;
|
||||
}
|
||||
} else {
|
||||
@@ -2044,42 +1999,26 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
|
||||
if (new_unmonitor) {
|
||||
if (!target_register_events(cmd, dso, seg_is_snapshot(seg) ? seg->cow : lv, 0, 0, 10)) {
|
||||
log_warn("WARNING: %s: segment unmonitoring failed.",
|
||||
display_lvname(lv));
|
||||
log_error("%s: segment unmonitoring failed.",
|
||||
display_lvname(lv));
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else if (monitor_fn) {
|
||||
/* FIXME specify events */
|
||||
if (!monitor_fn(seg, 0)) {
|
||||
log_warn("WARNING: %s: %s segment monitoring function failed.",
|
||||
display_lvname(lv), lvseg_name(seg));
|
||||
log_error("%s: %s segment monitoring function failed.",
|
||||
display_lvname(lv), lvseg_name(seg));
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
continue;
|
||||
|
||||
if (!vg_write_lock_held() && lv_is_mirror(lv)) {
|
||||
mirr_laopts.exclusive = lv_is_active_exclusive_locally(lv) ? 1 : 0;
|
||||
/*
|
||||
* Commands vgchange and lvchange do use read-only lock when changing
|
||||
* monitoring (--monitor y|n). All other use cases hold 'write-lock'
|
||||
* so they skip this dm mirror table refreshing step.
|
||||
*/
|
||||
if (!_lv_activate_lv(lv, &mirr_laopts)) {
|
||||
stack;
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check [un]monitor results */
|
||||
/* Try a couple times if pending, but not forever... */
|
||||
for (i = 0;; i++) {
|
||||
pending = 0;
|
||||
if (!seg->segtype->ops->target_monitored(seg, &pending, &monitored)) {
|
||||
stack;
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
monitored = seg->segtype->ops->target_monitored(seg, &pending);
|
||||
if (!pending || i >= 40)
|
||||
break;
|
||||
log_very_verbose("%s %smonitoring still pending: waiting...",
|
||||
@@ -2092,8 +2031,8 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
}
|
||||
|
||||
if (!r && !error_message_produced())
|
||||
log_warn("WARNING: %sonitoring %s failed.", monitor ? "M" : "Not m",
|
||||
display_lvname(lv));
|
||||
log_error("%sonitoring %s failed.", monitor ? "M" : "Not m",
|
||||
display_lvname(lv));
|
||||
return r;
|
||||
#else
|
||||
return 1;
|
||||
@@ -2124,12 +2063,6 @@ static int _preload_detached_lv(struct logical_volume *lv, void *data)
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!lv_is_visible(lv) && (lv_pre = find_lv(detached->lv_pre->vg, lv->name)) &&
|
||||
lv_is_visible(lv_pre)) {
|
||||
if (!_lv_preload(lv_pre, detached->laopts, detached->flush_required))
|
||||
return_0;
|
||||
}
|
||||
|
||||
/* FIXME: condition here should be far more limiting to really
|
||||
* detect detached LVs */
|
||||
if ((lv_pre = find_lv(detached->lv_pre->vg, lv->name))) {
|
||||
@@ -2159,67 +2092,11 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
struct dm_pool *mem = NULL;
|
||||
struct dm_list suspend_lvs;
|
||||
struct lv_list *lvl;
|
||||
const union lvid *lvid = (const union lvid *) lvid_s;
|
||||
const char *vgid = (const char *)lvid->id[0].uuid;
|
||||
struct volume_group *vg;
|
||||
struct volume_group *vg_pre;
|
||||
int found;
|
||||
|
||||
if (!activation())
|
||||
return 1;
|
||||
|
||||
if (!cmd->is_clvmd)
|
||||
goto skip_read;
|
||||
|
||||
if (lv && lv_pre)
|
||||
goto skip_read;
|
||||
|
||||
if (!(vg = lvmcache_get_saved_vg(vgid, 0))) {
|
||||
log_debug("lv_suspend did not find saved_vg %.8s so reading", vgid);
|
||||
if (!(vg = vg_read_by_vgid(cmd, vgid, 0))) {
|
||||
log_error("lv_suspend could not read vgid %.8s", vgid);
|
||||
goto out;
|
||||
}
|
||||
log_debug("lv_suspend using read vg %s %d %p", vg->name, vg->seqno, vg);
|
||||
} else {
|
||||
log_debug("lv_suspend using saved_vg %s %d %p", vg->name, vg->seqno, vg);
|
||||
}
|
||||
|
||||
if (!(vg_pre = lvmcache_get_saved_vg(vgid, 1))) {
|
||||
log_debug("lv_suspend did not find pre saved_vg %.8s so reading", vgid);
|
||||
if (!(vg_pre = vg_read_by_vgid(cmd, vgid, 1))) {
|
||||
log_error("lv_suspend could not read pre vgid %.8s", vgid);
|
||||
goto out;
|
||||
}
|
||||
log_debug("lv_suspend using pre read vg %s %d %p", vg_pre->name, vg_pre->seqno, vg_pre);
|
||||
} else {
|
||||
log_debug("lv_suspend using pre saved_vg %s %d %p", vg_pre->name, vg_pre->seqno, vg_pre);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that vg and vg_pre returned by vg_read_by_vgid will
|
||||
* not be the same as saved_vg_old/saved_vg_new that would
|
||||
* be returned by lvmcache_get_saved_vg() because the saved_vg's
|
||||
* are copies of the vg struct that is created by _vg_read.
|
||||
* (Should we grab and use the saved_vg to use here instead of
|
||||
* the vg returned by vg_read_by_vgid?)
|
||||
*/
|
||||
|
||||
if ((vg->status & EXPORTED_VG) || (vg_pre->status & EXPORTED_VG)) {
|
||||
log_error("Volume group \"%s\" is exported", vg->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lv = lv_to_free = find_lv_in_vg_by_lvid(vg, lvid);
|
||||
lv_pre = lv_pre_to_free = find_lv_in_vg_by_lvid(vg_pre, lvid);
|
||||
|
||||
if (!lv || !lv_pre) {
|
||||
log_error("lv_suspend could not find lv %p lv_pre %p vg %p vg_pre %p vgid %s",
|
||||
lv, lv_pre, vg, vg_pre, vgid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
skip_read:
|
||||
/* lv comes from committed metadata */
|
||||
if (!lv && !(lv_to_free = lv = lv_from_lvid(cmd, lvid_s, 0)))
|
||||
goto_out;
|
||||
@@ -2244,19 +2121,6 @@ skip_read:
|
||||
if (!lv_info(cmd, lv, laopts->origin_only, &info, 0, 0))
|
||||
goto_out;
|
||||
|
||||
/*
|
||||
* Save old and new (current and precommitted) versions of the
|
||||
* VG metadata for lv_resume() to use, since lv_resume can't
|
||||
* read metadata given that devices are suspended. lv_resume()
|
||||
* will resume LVs using the old/current metadata if the vg_commit
|
||||
* did happen (or failed), and it will resume LVs using the
|
||||
* new/precommitted metadata if the vg_commit succeeded.
|
||||
*/
|
||||
if (cmd->is_clvmd) {
|
||||
lvmcache_save_vg(lv->vg, 0);
|
||||
lvmcache_save_vg(lv_pre->vg, 1);
|
||||
}
|
||||
|
||||
if (!info.exists || info.suspended) {
|
||||
if (!error_if_not_suspended) {
|
||||
r = 1;
|
||||
@@ -2463,55 +2327,16 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
|
||||
struct lv_activate_opts *laopts, int error_if_not_active,
|
||||
const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *lv_to_free = NULL;
|
||||
struct dm_list *snh;
|
||||
struct volume_group *vg = NULL;
|
||||
struct logical_volume *lv_found = NULL;
|
||||
const union lvid *lvid;
|
||||
const char *vgid;
|
||||
struct lvinfo info;
|
||||
int r = 0;
|
||||
|
||||
if (!activation())
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* When called in clvmd, lvid_s is set and lv is not. We need to
|
||||
* get the VG metadata without reading disks because devs are
|
||||
* suspended. lv_suspend() saved old and new VG metadata for us
|
||||
* to use here. If vg_commit() happened, lvmcache_get_saved_vg_latest
|
||||
* will return the new metadata for us to use in resuming LVs.
|
||||
* If vg_commit() did not happen, lvmcache_get_saved_vg_latest
|
||||
* returns the old metadata which we use to resume LVs.
|
||||
*/
|
||||
if (!lv && lvid_s) {
|
||||
lvid = (const union lvid *) lvid_s;
|
||||
vgid = (const char *)lvid->id[0].uuid;
|
||||
|
||||
if ((vg = lvmcache_get_saved_vg_latest(vgid))) {
|
||||
log_debug_activation("Resuming LVID %s found saved vg seqno %d %s", lvid_s, vg->seqno, vg->name);
|
||||
if ((lv_found = find_lv_in_vg_by_lvid(vg, lvid))) {
|
||||
log_debug_activation("Resuming LVID %s found saved LV %s", lvid_s, display_lvname(lv_found));
|
||||
lv = lv_found;
|
||||
} else
|
||||
log_debug_activation("Resuming LVID %s did not find saved LV", lvid_s);
|
||||
} else
|
||||
log_debug_activation("Resuming LVID %s did not find saved VG", lvid_s);
|
||||
|
||||
/*
|
||||
* resume must have been called without a preceding suspend,
|
||||
* so we need to read the vg.
|
||||
*/
|
||||
|
||||
if (!lv) {
|
||||
log_debug_activation("Resuming LVID %s reading VG", lvid_s);
|
||||
if (!(lv_found = lv_from_lvid(cmd, lvid_s, 0))) {
|
||||
log_debug_activation("Resuming LVID %s failed to read VG", lvid_s);
|
||||
goto out;
|
||||
}
|
||||
|
||||
lv = lv_found;
|
||||
}
|
||||
}
|
||||
if (!lv && !(lv_to_free = lv = lv_from_lvid(cmd, lvid_s, 0)))
|
||||
goto_out;
|
||||
|
||||
if (!lv_is_origin(lv) && !lv_is_thin_volume(lv) && !lv_is_thin_pool(lv))
|
||||
laopts->origin_only = 0;
|
||||
@@ -2572,6 +2397,9 @@ needs_resume:
|
||||
|
||||
r = 1;
|
||||
out:
|
||||
if (lv_to_free)
|
||||
release_vg(lv_to_free->vg);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -2800,12 +2628,7 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Component LV activation is enforced to be 'read-only' */
|
||||
/* TODO: should not apply for LVs in maintenance mode */
|
||||
if (!lv_is_visible(lv) && lv_is_component(lv)) {
|
||||
laopts->read_only = 1;
|
||||
laopts->component_lv = lv;
|
||||
} else if (filter)
|
||||
if (filter)
|
||||
laopts->read_only = _passes_readonly_filter(cmd, lv);
|
||||
|
||||
log_debug_activation("Activating %s%s%s%s%s.", display_lvname(lv),
|
||||
@@ -2821,7 +2644,7 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
* Nothing to do?
|
||||
*/
|
||||
if (info.exists && !info.suspended && info.live_table &&
|
||||
(info.read_only == read_only_lv(lv, laopts, NULL))) {
|
||||
(info.read_only == read_only_lv(lv, laopts))) {
|
||||
r = 1;
|
||||
log_debug_activation("LV %s is already active.", display_lvname(lv));
|
||||
goto out;
|
||||
@@ -2992,112 +2815,3 @@ void activation_exit(void)
|
||||
dev_manager_exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
static int _component_cb(struct logical_volume *lv, void *data)
|
||||
{
|
||||
struct logical_volume **component_lv = (struct logical_volume **) data;
|
||||
|
||||
if (lv_is_locked(lv) || lv_is_pvmove(lv) ||/* ignoring */
|
||||
/* thin-pool is special and it's using layered device */
|
||||
(lv_is_thin_pool(lv) && pool_is_active(lv)))
|
||||
return -1;
|
||||
|
||||
if (lv_is_active(lv)) {
|
||||
if (!lv_is_component(lv) || lv_is_visible(lv))
|
||||
return -1; /* skip whole subtree */
|
||||
|
||||
log_debug_activation("Found active component LV %s.", display_lvname(lv));
|
||||
*component_lv = lv;
|
||||
return 0; /* break any further processing */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds out for any LV if any of its component LVs are active.
|
||||
* Function first checks if an existing LV is visible and active eventually
|
||||
* it's lock holding LV is already active. In such case sub LV cannot be
|
||||
* actived alone and no further checking is needed.
|
||||
*
|
||||
* Returns active component LV if there is such.
|
||||
*/
|
||||
const struct logical_volume *lv_component_is_active(const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *component_lv = NULL;
|
||||
const struct logical_volume *holder_lv = lv_lock_holder(lv);
|
||||
|
||||
if ((holder_lv != lv) && lv_is_active(holder_lv))
|
||||
return NULL; /* Lock holding LV is active, do not check components */
|
||||
|
||||
if (_component_cb((struct logical_volume *) lv, &holder_lv) == 1)
|
||||
(void) for_each_sub_lv((struct logical_volume *) lv, _component_cb,
|
||||
(void*) &component_lv);
|
||||
|
||||
return component_lv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds out if any LV above is active, as stacked device tree can be composed of
|
||||
* chained set of LVs.
|
||||
*
|
||||
* Returns active holder LV if there is such.
|
||||
*/
|
||||
const struct logical_volume *lv_holder_is_active(const struct logical_volume *lv)
|
||||
{
|
||||
const struct logical_volume *holder;
|
||||
const struct seg_list *sl;
|
||||
|
||||
if (lv_is_locked(lv) || lv_is_pvmove(lv))
|
||||
return NULL; /* Skip pvmove/locked LV tracking */
|
||||
|
||||
dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
|
||||
/* Recursive call for upper-stack holder */
|
||||
if ((holder = lv_holder_is_active(sl->seg->lv)))
|
||||
return holder;
|
||||
|
||||
if (lv_is_active(sl->seg->lv)) {
|
||||
log_debug_activation("Found active holder LV %s.", display_lvname(sl->seg->lv));
|
||||
return sl->seg->lv;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _deactivate_sub_lv_cb(struct logical_volume *lv, void *data)
|
||||
{
|
||||
struct logical_volume **slv = data;
|
||||
|
||||
if (lv_is_thin_pool(lv) || lv_is_external_origin(lv))
|
||||
return -1;
|
||||
|
||||
if (!deactivate_lv(lv->vg->cmd, lv)) {
|
||||
*slv = lv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deactivates LV toghether with explicit deactivation call made also for all its component LVs.
|
||||
*/
|
||||
int deactivate_lv_with_sub_lv(const struct logical_volume *lv)
|
||||
{
|
||||
struct logical_volume *flv;
|
||||
|
||||
if (!deactivate_lv(lv->vg->cmd, lv)) {
|
||||
log_error("Cannot deactivate logical volume %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!for_each_sub_lv((struct logical_volume *)lv, _deactivate_sub_lv_cb, &flv)) {
|
||||
log_error("Cannot deactivate subvolume %s of logical volume %s.",
|
||||
display_lvname(flv), display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -83,7 +83,6 @@ struct lv_activate_opts {
|
||||
* flags are persistent in udev db for any spurious event
|
||||
* that follows. */
|
||||
unsigned resuming; /* Set when resuming after a suspend. */
|
||||
const struct logical_volume *component_lv;
|
||||
};
|
||||
|
||||
void set_activation(int activation, int silent);
|
||||
@@ -199,11 +198,6 @@ int lv_is_active_exclusive(const struct logical_volume *lv);
|
||||
int lv_is_active_exclusive_locally(const struct logical_volume *lv);
|
||||
int lv_is_active_exclusive_remotely(const struct logical_volume *lv);
|
||||
|
||||
/* Check is any component LV is active */
|
||||
const struct logical_volume *lv_component_is_active(const struct logical_volume *lv);
|
||||
const struct logical_volume *lv_holder_is_active(const struct logical_volume *lv);
|
||||
int deactivate_lv_with_sub_lv(const struct logical_volume *lv);
|
||||
|
||||
int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
const char *layer, const char *target_type);
|
||||
|
||||
@@ -212,9 +206,9 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
|
||||
#ifdef DMEVENTD
|
||||
# include "libdevmapper-event.h"
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, int id);
|
||||
char *get_monitor_dso_path(struct cmd_context *cmd, const char *libpath);
|
||||
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
const struct logical_volume *lv, int *pending, int *monitored);
|
||||
const struct logical_volume *lv, int *pending);
|
||||
int target_register_events(struct cmd_context *cmd, const char *dso, const struct logical_volume *lv,
|
||||
int evmask __attribute__((unused)), int set, int timeout);
|
||||
#endif
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -32,8 +32,6 @@
|
||||
|
||||
#define MAX_TARGET_PARAMSIZE 50000
|
||||
#define LVM_UDEV_NOSCAN_FLAG DM_SUBSYSTEM_UDEV_FLAG0
|
||||
#define CRYPT_TEMP "CRYPT-TEMP"
|
||||
#define STRATIS "stratis-"
|
||||
|
||||
typedef enum {
|
||||
PRELOAD,
|
||||
@@ -74,17 +72,10 @@ struct dev_manager {
|
||||
struct lv_layer {
|
||||
const struct logical_volume *lv;
|
||||
const char *old_name;
|
||||
int visible_component;
|
||||
};
|
||||
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts, const char *layer)
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts)
|
||||
{
|
||||
if (layer && lv_is_cow(lv))
|
||||
return 0; /* Keep snapshot's COW volume writable */
|
||||
|
||||
if (lv_is_raid_image(lv) || lv_is_raid_metadata(lv))
|
||||
return 0; /* Keep RAID SubLvs writable */
|
||||
|
||||
return (laopts->read_only || !(lv->status & LVM_WRITE));
|
||||
}
|
||||
|
||||
@@ -450,21 +441,15 @@ static int _ignore_suspended_snapshot_component(struct device *dev)
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
|
||||
if (!target_type)
|
||||
continue;
|
||||
|
||||
if (!strcmp(target_type, TARGET_NAME_SNAPSHOT)) {
|
||||
if (!target_type || !strcmp(target_type, TARGET_NAME_SNAPSHOT)) {
|
||||
if (!params || sscanf(params, "%d:%d %d:%d", &major1, &minor1, &major2, &minor2) != 4) {
|
||||
log_warn("WARNING: Incorrect snapshot table found for %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
log_error("Incorrect snapshot table found.");
|
||||
goto out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1) || _device_is_suspended(major2, minor2);
|
||||
} else if (!strcmp(target_type, TARGET_NAME_SNAPSHOT_ORIGIN)) {
|
||||
if (!params || sscanf(params, "%d:%d", &major1, &minor1) != 2) {
|
||||
log_warn("WARNING: Incorrect snapshot-origin table found for %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
log_error("Incorrect snapshot-origin table found.");
|
||||
goto out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1);
|
||||
@@ -499,7 +484,7 @@ static int _ignore_unusable_thins(struct device *dev)
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!params || sscanf(params, "%d:%d", &major, &minor) != 2) {
|
||||
log_warn("WARNING: Cannot get thin-pool major:minor for thin device %d:%d.",
|
||||
log_error("Failed to get thin-pool major:minor for thin device %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
goto out;
|
||||
}
|
||||
@@ -529,47 +514,6 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _ignore_invalid_snapshot(const char *params)
|
||||
{
|
||||
struct dm_status_snapshot *s;
|
||||
struct dm_pool *mem;
|
||||
int r = 0;
|
||||
|
||||
if (!(mem = dm_pool_create("invalid snapshots", 128)))
|
||||
return_0;
|
||||
|
||||
if (!dm_get_status_snapshot(mem, params, &s))
|
||||
stack;
|
||||
else
|
||||
r = s->invalid;
|
||||
|
||||
dm_pool_destroy(mem);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _ignore_frozen_raid(struct device *dev, const char *params)
|
||||
{
|
||||
struct dm_status_raid *s;
|
||||
struct dm_pool *mem;
|
||||
int r = 0;
|
||||
|
||||
if (!(mem = dm_pool_create("frozen raid", 128)))
|
||||
return_0;
|
||||
|
||||
if (!dm_get_status_raid(mem, params, &s))
|
||||
stack;
|
||||
else if (s->sync_action && !strcmp(s->sync_action, "frozen")) {
|
||||
log_warn("WARNING: %s frozen raid device (%d:%d) needs inspection.",
|
||||
dev_name(dev), (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
r = 1;
|
||||
}
|
||||
|
||||
dm_pool_destroy(mem);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* device_is_usable
|
||||
* @dev
|
||||
@@ -639,26 +583,12 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
}
|
||||
}
|
||||
|
||||
if (check.check_reserved && uuid &&
|
||||
(!strncmp(uuid, CRYPT_TEMP, sizeof(CRYPT_TEMP) - 1) ||
|
||||
!strncmp(uuid, STRATIS, sizeof(STRATIS) - 1))) {
|
||||
/* Skip private crypto devices */
|
||||
log_debug_activation("%s: Reserved uuid %s on %s device %s not usable.",
|
||||
dev_name(dev), uuid,
|
||||
uuid[0] == 'C' ? "crypto" : "stratis",
|
||||
name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FIXME Also check for mpath no paths */
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&target_type, ¶ms);
|
||||
|
||||
if (!target_type)
|
||||
continue;
|
||||
|
||||
if (check.check_blocked && !strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
if (check.check_blocked && target_type && !strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
if (ignore_lvm_mirrors()) {
|
||||
log_debug_activation("%s: Scanning mirror devices is disabled.", dev_name(dev));
|
||||
goto out;
|
||||
@@ -692,33 +622,21 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
* correctly, not just snapshots but any cobimnation possible
|
||||
* in a stack - use proper dm tree to check this instead.
|
||||
*/
|
||||
if (check.check_suspended &&
|
||||
if (check.check_suspended && target_type &&
|
||||
(!strcmp(target_type, TARGET_NAME_SNAPSHOT) || !strcmp(target_type, TARGET_NAME_SNAPSHOT_ORIGIN)) &&
|
||||
_ignore_suspended_snapshot_component(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strcmp(target_type, TARGET_NAME_SNAPSHOT) &&
|
||||
_ignore_invalid_snapshot(params)) {
|
||||
log_debug_activation("%s: Invalid %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strncmp(target_type, TARGET_NAME_RAID, 4) && _ignore_frozen_raid(dev, params)) {
|
||||
log_debug_activation("%s: Frozen %s device %s not usable.",
|
||||
dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: extend check struct ? */
|
||||
if (!strcmp(target_type, TARGET_NAME_THIN) &&
|
||||
if (target_type && !strcmp(target_type, TARGET_NAME_THIN) &&
|
||||
!_ignore_unusable_thins(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(target_type, TARGET_NAME_ERROR))
|
||||
if (target_type && strcmp(target_type, TARGET_NAME_ERROR))
|
||||
only_error_target = 0;
|
||||
} while (next);
|
||||
|
||||
@@ -1672,8 +1590,7 @@ int dev_manager_mknodes(const struct logical_volume *lv)
|
||||
return_0;
|
||||
|
||||
if (dminfo.exists) {
|
||||
/* read-only component LV is also made visible */
|
||||
if (_lv_has_mknode(lv) || (dminfo.read_only && lv_is_component(lv)))
|
||||
if (_lv_has_mknode(lv))
|
||||
r = _dev_manager_lv_mknodes(lv);
|
||||
} else
|
||||
r = _dev_manager_lv_rmnodes(lv);
|
||||
@@ -1735,8 +1652,7 @@ static int _check_udev_fallback(struct cmd_context *cmd)
|
||||
#endif /* UDEV_SYNC_SUPPORT */
|
||||
|
||||
static uint16_t _get_udev_flags(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
const char *layer, int noscan, int temporary,
|
||||
int visible_component)
|
||||
const char *layer, int noscan, int temporary)
|
||||
{
|
||||
uint16_t udev_flags = 0;
|
||||
|
||||
@@ -1752,7 +1668,7 @@ static uint16_t _get_udev_flags(struct dev_manager *dm, const struct logical_vol
|
||||
* If not, create just the /dev/mapper content.
|
||||
*/
|
||||
/* FIXME: add target's method for this */
|
||||
if (lv_is_new_thin_pool(lv) || visible_component)
|
||||
if (lv_is_new_thin_pool(lv))
|
||||
/* New thin-pool is regular LV with -tpool UUID suffix. */
|
||||
udev_flags |= DM_UDEV_DISABLE_DISK_RULES_FLAG |
|
||||
DM_UDEV_DISABLE_OTHER_RULES_FLAG;
|
||||
@@ -1950,8 +1866,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
}
|
||||
|
||||
if (info.exists && !dm_tree_add_dev_with_udev_flags(dtree, info.major, info.minor,
|
||||
_get_udev_flags(dm, lv, layer,
|
||||
0, 0, 0))) {
|
||||
_get_udev_flags(dm, lv, layer, 0, 0))) {
|
||||
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree.",
|
||||
info.major, info.minor);
|
||||
return 0;
|
||||
@@ -1983,56 +1898,9 @@ struct pool_cb_data {
|
||||
int skip_zero; /* to skip zeroed device header (check first 64B) */
|
||||
int exec; /* which binary to call */
|
||||
int opts;
|
||||
struct {
|
||||
unsigned maj;
|
||||
unsigned min;
|
||||
unsigned patch;
|
||||
} version;
|
||||
const char *global;
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple version of check function calling 'tool -V'
|
||||
*
|
||||
* Returns 1 if the tool's version is equal or better to given.
|
||||
* Otherwise it returns 0.
|
||||
*/
|
||||
static int _check_tool_version(struct cmd_context *cmd, const char *tool,
|
||||
unsigned maj, unsigned min, unsigned patch)
|
||||
{
|
||||
const char *argv[] = { tool, "-V", NULL };
|
||||
struct pipe_data pdata;
|
||||
FILE *f;
|
||||
char buf[128] = { 0 };
|
||||
char *nl;
|
||||
unsigned v_maj, v_min, v_patch;
|
||||
int ret = 0;
|
||||
|
||||
if (!(f = pipe_open(cmd, argv, 0, &pdata))) {
|
||||
log_warn("WARNING: Cannot read output from %s.", argv[0]);
|
||||
} else {
|
||||
if (fgets(buf, sizeof(buf) - 1, f) &&
|
||||
(sscanf(buf, "%u.%u.%u", &v_maj, &v_min, &v_patch) == 3)) {
|
||||
if ((v_maj > maj) ||
|
||||
((v_maj == maj) &&
|
||||
((v_min > min) ||
|
||||
(v_min == min && v_patch >= patch))))
|
||||
ret = 1;
|
||||
|
||||
if ((nl = strchr(buf, '\n')))
|
||||
nl[0] = 0; /* cut newline away */
|
||||
|
||||
log_verbose("Found version of %s %s is %s then requested %u.%u.%u.",
|
||||
argv[0], buf, ret ? "better" : "older", maj, min, patch);
|
||||
} else
|
||||
log_warn("WARNING: Cannot parse output '%s' from %s.", buf, argv[0]);
|
||||
|
||||
(void) pipe_close(&pdata);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _pool_callback(struct dm_tree_node *node,
|
||||
dm_node_callback_t type, void *cb_data)
|
||||
{
|
||||
@@ -2108,19 +1976,6 @@ static int _pool_callback(struct dm_tree_node *node,
|
||||
|
||||
if (!(ret = exec_cmd(pool_lv->vg->cmd, (const char * const *)argv,
|
||||
&status, 0))) {
|
||||
if (status == ENOENT) {
|
||||
log_warn("WARNING: Check is skipped, please install recommended missing binary %s!",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((data->version.maj || data->version.min || data->version.patch) &&
|
||||
!_check_tool_version(pool_lv->vg->cmd, argv[0],
|
||||
data->version.maj, data->version.min, data->version.patch)) {
|
||||
log_warn("WARNING: Check is skipped, please upgrade installed version of %s!",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
switch (type) {
|
||||
case DM_NODE_CALLBACK_PRELOADED:
|
||||
log_err_once("Check of pool %s failed (status:%d). "
|
||||
@@ -2178,10 +2033,6 @@ static int _pool_register_callback(struct dev_manager *dm,
|
||||
data->exec = global_cache_check_executable_CFG;
|
||||
data->opts = global_cache_check_options_CFG;
|
||||
data->global = "cache";
|
||||
if (first_seg(first_seg(lv)->pool_lv)->cache_metadata_format > 1) {
|
||||
data->version.maj = 0;
|
||||
data->version.min = 7;
|
||||
}
|
||||
} else {
|
||||
log_error(INTERNAL_ERROR "Registering unsupported pool callback.");
|
||||
return 0;
|
||||
@@ -2955,7 +2806,6 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
}
|
||||
|
||||
lvlayer->lv = lv;
|
||||
lvlayer->visible_component = (laopts->component_lv == lv) ? 1 : 0;
|
||||
|
||||
/*
|
||||
* Add LV to dtree.
|
||||
@@ -2969,11 +2819,10 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (!(dnode = dm_tree_add_new_dev_with_udev_flags(dtree, name, dlid,
|
||||
layer ? UINT32_C(0) : (uint32_t) lv->major,
|
||||
layer ? UINT32_C(0) : (uint32_t) lv->minor,
|
||||
read_only_lv(lv, laopts, layer),
|
||||
read_only_lv(lv, laopts),
|
||||
((lv->vg->status & PRECOMMITTED) | laopts->revert) ? 1 : 0,
|
||||
lvlayer,
|
||||
_get_udev_flags(dm, lv, layer, laopts->noscan, laopts->temporary,
|
||||
lvlayer->visible_component))))
|
||||
_get_udev_flags(dm, lv, layer, laopts->noscan, laopts->temporary))))
|
||||
return_0;
|
||||
|
||||
/* Store existing name so we can do rename later */
|
||||
@@ -3117,7 +2966,7 @@ static int _create_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root
|
||||
r = 0;
|
||||
continue;
|
||||
}
|
||||
if (_lv_has_mknode(lvlayer->lv) || lvlayer->visible_component) {
|
||||
if (_lv_has_mknode(lvlayer->lv)) {
|
||||
if (!_dev_manager_lv_mknodes(lvlayer->lv))
|
||||
r = 0;
|
||||
continue;
|
||||
|
@@ -27,7 +27,7 @@ struct dm_info;
|
||||
struct device;
|
||||
struct lv_seg_status;
|
||||
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts, const char *layer);
|
||||
int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts *laopts);
|
||||
|
||||
/*
|
||||
* Constructor and destructor.
|
||||
|
@@ -441,7 +441,7 @@ static int _fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
|
||||
const char *lv_name, const char *dev, const char *old_lv_name,
|
||||
int check_udev)
|
||||
{
|
||||
if (prioritized_section()) {
|
||||
if (critical_section()) {
|
||||
if (!_stack_fs_op(type, dev_dir, vg_name, lv_name, dev,
|
||||
old_lv_name, check_udev))
|
||||
return_0;
|
||||
@@ -487,7 +487,7 @@ int fs_rename_lv(const struct logical_volume *lv, const char *dev,
|
||||
|
||||
void fs_unlock(void)
|
||||
{
|
||||
if (!prioritized_section()) {
|
||||
if (!critical_section()) {
|
||||
log_debug_activation("Syncing device names");
|
||||
/* Wait for all processed udev devices */
|
||||
if (!dm_udev_wait(_fs_cookie))
|
||||
|
1402
lib/cache/lvmcache.c
vendored
1402
lib/cache/lvmcache.c
vendored
File diff suppressed because it is too large
Load Diff
48
lib/cache/lvmcache.h
vendored
48
lib/cache/lvmcache.h
vendored
@@ -59,17 +59,21 @@ struct lvmcache_vgsummary {
|
||||
const char *lock_type;
|
||||
uint32_t mda_checksum;
|
||||
size_t mda_size;
|
||||
int zero_offset;
|
||||
int seqno;
|
||||
};
|
||||
|
||||
int lvmcache_init(struct cmd_context *cmd);
|
||||
int lvmcache_init(void);
|
||||
void lvmcache_allow_reads_with_lvmetad(void);
|
||||
|
||||
void lvmcache_destroy(struct cmd_context *cmd, int retain_orphans, int reset);
|
||||
|
||||
/*
|
||||
* lvmcache_label_scan() will scan labels the first time it's
|
||||
* called, but not on subsequent calls, unless
|
||||
* lvmcache_force_next_label_scan() is called first
|
||||
* to force the next lvmcache_label_scan() to scan again.
|
||||
*/
|
||||
void lvmcache_force_next_label_scan(void);
|
||||
int lvmcache_label_scan(struct cmd_context *cmd);
|
||||
int lvmcache_label_rescan_vg(struct cmd_context *cmd, const char *vgname, const char *vgid);
|
||||
|
||||
/* Add/delete a device */
|
||||
struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
|
||||
@@ -78,11 +82,10 @@ struct lvmcache_info *lvmcache_add(struct labeller *labeller, const char *pvid,
|
||||
uint32_t vgstatus);
|
||||
int lvmcache_add_orphan_vginfo(const char *vgname, struct format_type *fmt);
|
||||
void lvmcache_del(struct lvmcache_info *info);
|
||||
void lvmcache_del_dev(struct device *dev);
|
||||
|
||||
/* Update things */
|
||||
int lvmcache_update_vgname_and_id(struct lvmcache_info *info,
|
||||
struct lvmcache_vgsummary *vgsummary);
|
||||
const struct lvmcache_vgsummary *vgsummary);
|
||||
int lvmcache_update_vg(struct volume_group *vg, unsigned precommitted);
|
||||
|
||||
void lvmcache_lock_vgname(const char *vgname, int read_only);
|
||||
@@ -102,8 +105,10 @@ struct lvmcache_vginfo *lvmcache_vginfo_from_vgid(const char *vgid);
|
||||
struct lvmcache_info *lvmcache_info_from_pvid(const char *pvid, struct device *dev, int valid_only);
|
||||
const char *lvmcache_vgname_from_vgid(struct dm_pool *mem, const char *vgid);
|
||||
const char *lvmcache_vgid_from_vgname(struct cmd_context *cmd, const char *vgname);
|
||||
struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct id *pvid, uint64_t *label_sector);
|
||||
const char *lvmcache_pvid_from_devname(struct cmd_context *cmd, const char *devname);
|
||||
struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
|
||||
unsigned *scan_done_once, uint64_t *label_sector);
|
||||
const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
|
||||
const char *devname);
|
||||
char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid);
|
||||
const char *lvmcache_vgname_from_info(struct lvmcache_info *info);
|
||||
const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info);
|
||||
@@ -129,16 +134,20 @@ int lvmcache_get_vgnameids(struct cmd_context *cmd, int include_internal,
|
||||
struct dm_list *lvmcache_get_pvids(struct cmd_context *cmd, const char *vgname,
|
||||
const char *vgid);
|
||||
|
||||
/* Returns cached volume group metadata. */
|
||||
struct volume_group *lvmcache_get_vg(struct cmd_context *cmd, const char *vgname,
|
||||
const char *vgid, unsigned precommitted);
|
||||
void lvmcache_drop_metadata(const char *vgname, int drop_precommitted);
|
||||
void lvmcache_commit_metadata(const char *vgname);
|
||||
|
||||
int lvmcache_pvid_is_locked(const char *pvid);
|
||||
int lvmcache_fid_add_mdas(struct lvmcache_info *info, struct format_instance *fid,
|
||||
const char *id, int id_len);
|
||||
int lvmcache_fid_add_mdas_pv(struct lvmcache_info *info, struct format_instance *fid);
|
||||
int lvmcache_fid_add_mdas_vg(struct lvmcache_vginfo *vginfo, struct format_instance *fid);
|
||||
int lvmcache_populate_pv_fields(struct lvmcache_info *info,
|
||||
struct volume_group *vg,
|
||||
struct physical_volume *pv);
|
||||
struct physical_volume *pv,
|
||||
int scan_label_only);
|
||||
int lvmcache_check_format(struct lvmcache_info *info, const struct format_type *fmt);
|
||||
void lvmcache_del_mdas(struct lvmcache_info *info);
|
||||
void lvmcache_del_das(struct lvmcache_info *info);
|
||||
@@ -155,8 +164,6 @@ uint32_t lvmcache_ext_flags(struct lvmcache_info *info);
|
||||
|
||||
const struct format_type *lvmcache_fmt(struct lvmcache_info *info);
|
||||
struct label *lvmcache_get_label(struct lvmcache_info *info);
|
||||
struct label *lvmcache_get_dev_label(struct device *dev);
|
||||
int lvmcache_has_dev_info(struct device *dev);
|
||||
|
||||
void lvmcache_update_pv(struct lvmcache_info *info, struct physical_volume *pv,
|
||||
const struct format_type *fmt);
|
||||
@@ -180,6 +187,7 @@ int lvmcache_foreach_pv(struct lvmcache_vginfo *vginfo,
|
||||
uint64_t lvmcache_device_size(struct lvmcache_info *info);
|
||||
void lvmcache_set_device_size(struct lvmcache_info *info, uint64_t size);
|
||||
struct device *lvmcache_device(struct lvmcache_info *info);
|
||||
void lvmcache_make_valid(struct lvmcache_info *info);
|
||||
int lvmcache_is_orphan(struct lvmcache_info *info);
|
||||
int lvmcache_uncertain_ownership(struct lvmcache_info *info);
|
||||
unsigned lvmcache_mda_count(struct lvmcache_info *info);
|
||||
@@ -207,20 +215,4 @@ void lvmcache_remove_unchosen_duplicate(struct device *dev);
|
||||
|
||||
int lvmcache_pvid_in_unchosen_duplicates(const char *pvid);
|
||||
|
||||
int lvmcache_get_vg_devs(struct cmd_context *cmd,
|
||||
struct lvmcache_vginfo *vginfo,
|
||||
struct dm_list *devs);
|
||||
void lvmcache_set_independent_location(const char *vgname);
|
||||
|
||||
int lvmcache_scan_mismatch(struct cmd_context *cmd, const char *vgname, const char *vgid);
|
||||
|
||||
/*
|
||||
* These are clvmd-specific functions and are not related to lvmcache.
|
||||
* FIXME: rename these with a clvm_ prefix in place of lvmcache_
|
||||
*/
|
||||
void lvmcache_save_vg(struct volume_group *vg, int precommitted);
|
||||
struct volume_group *lvmcache_get_saved_vg(const char *vgid, int precommitted);
|
||||
struct volume_group *lvmcache_get_saved_vg_latest(const char *vgid);
|
||||
void lvmcache_drop_saved_vgid(const char *vgid);
|
||||
|
||||
#endif
|
||||
|
499
lib/cache/lvmetad.c
vendored
499
lib/cache/lvmetad.c
vendored
@@ -39,7 +39,7 @@ static int64_t _lvmetad_update_timeout;
|
||||
|
||||
static int _found_lvm1_metadata = 0;
|
||||
|
||||
static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg, const char *vgid, struct format_type *fmt);
|
||||
static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg);
|
||||
|
||||
static uint64_t _monotonic_seconds(void)
|
||||
{
|
||||
@@ -1093,17 +1093,14 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna
|
||||
* invalidated the cached vg.
|
||||
*/
|
||||
if (rescan) {
|
||||
if (!(vg2 = _lvmetad_pvscan_vg(cmd, vg, vgid, fmt))) {
|
||||
if (!(vg2 = _lvmetad_pvscan_vg(cmd, vg))) {
|
||||
log_debug_lvmetad("VG %s from lvmetad not found during rescan.", vgname);
|
||||
fid = NULL;
|
||||
release_vg(vg);
|
||||
vg = NULL;
|
||||
goto out;
|
||||
}
|
||||
fid->ref_count++;
|
||||
release_vg(vg);
|
||||
fid->ref_count--;
|
||||
fmt->ops->destroy_instance(fid);
|
||||
vg = vg2;
|
||||
fid = vg2->fid;
|
||||
}
|
||||
@@ -1111,14 +1108,14 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd, const char *vgna
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (!_pv_update_struct_pv(pvl->pv, fid)) {
|
||||
vg = NULL;
|
||||
goto_out; /* FIXME: use an error path that disables lvmetad */
|
||||
goto_out; /* FIXME error path */
|
||||
}
|
||||
}
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs_outdated) {
|
||||
if (!_pv_update_struct_pv(pvl->pv, fid)) {
|
||||
vg = NULL;
|
||||
goto_out; /* FIXME: use an error path that disables lvmetad */
|
||||
goto_out; /* FIXME error path */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1680,7 +1677,7 @@ int lvmetad_pv_found(struct cmd_context *cmd, const struct id *pvid, struct devi
|
||||
|
||||
if (vg && result) {
|
||||
seqno_after = daemon_reply_int(reply, "seqno_after", -1);
|
||||
if ((seqno_after != (int) vg->seqno) ||
|
||||
if ((seqno_after != vg->seqno) ||
|
||||
(seqno_after != daemon_reply_int(reply, "seqno_before", -1)))
|
||||
log_warn("WARNING: Inconsistent metadata found for VG %s", vg->name);
|
||||
}
|
||||
@@ -1764,7 +1761,6 @@ int lvmetad_pv_gone_by_dev(struct device *dev)
|
||||
*/
|
||||
|
||||
struct _lvmetad_pvscan_baton {
|
||||
struct cmd_context *cmd;
|
||||
struct volume_group *vg;
|
||||
struct format_instance *fid;
|
||||
};
|
||||
@@ -1775,7 +1771,7 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton)
|
||||
struct volume_group *vg;
|
||||
|
||||
if (mda_is_ignored(mda) ||
|
||||
!(vg = mda->ops->vg_read(b->fid, "", mda, NULL, NULL)))
|
||||
!(vg = mda->ops->vg_read(b->fid, "", mda, NULL, NULL, 1, 0)))
|
||||
return 1;
|
||||
|
||||
/* FIXME Also ensure contents match etc. */
|
||||
@@ -1787,33 +1783,6 @@ static int _lvmetad_pvscan_single(struct metadata_area *mda, void *baton)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: handle errors and do proper comparison of metadata from each area
|
||||
* like vg_read and fall back to real vg_read from disk if there's any problem.
|
||||
*/
|
||||
|
||||
static int _lvmetad_pvscan_vg_single(struct metadata_area *mda, void *baton)
|
||||
{
|
||||
struct _lvmetad_pvscan_baton *b = baton;
|
||||
struct volume_group *vg = NULL;
|
||||
|
||||
if (mda_is_ignored(mda))
|
||||
return 1;
|
||||
|
||||
if (!(vg = mda->ops->vg_read(b->fid, "", mda, NULL, NULL)))
|
||||
return 1;
|
||||
|
||||
if (!b->vg)
|
||||
b->vg = vg;
|
||||
else if (vg->seqno > b->vg->seqno) {
|
||||
release_vg(b->vg);
|
||||
b->vg = vg;
|
||||
} else
|
||||
release_vg(vg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* The lock manager may detect that the vg cached in lvmetad is out of date,
|
||||
* due to something like an lvcreate from another host.
|
||||
@@ -1823,41 +1792,41 @@ static int _lvmetad_pvscan_vg_single(struct metadata_area *mda, void *baton)
|
||||
* the VG, and that PV may have been reused for another VG.
|
||||
*/
|
||||
|
||||
static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
const char *vgid, struct format_type *fmt)
|
||||
static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
char pvid_s[ID_LEN + 1] __attribute__((aligned(8)));
|
||||
char uuid[64] __attribute__((aligned(8)));
|
||||
struct label *label;
|
||||
struct volume_group *vg_ret = NULL;
|
||||
struct dm_config_tree *vgmeta_ret = NULL;
|
||||
struct dm_config_tree *vgmeta;
|
||||
struct pv_list *pvl, *pvl_new;
|
||||
struct device_list *devl, *devlsafe;
|
||||
struct device_list *devl, *devl_new, *devlsafe;
|
||||
struct dm_list pvs_scan;
|
||||
struct dm_list pvs_drop;
|
||||
struct lvmcache_vginfo *vginfo = NULL;
|
||||
struct dm_list pvs_new;
|
||||
struct lvmcache_info *info = NULL;
|
||||
struct format_instance *fid;
|
||||
struct format_instance_ctx fic = { .type = 0 };
|
||||
struct _lvmetad_pvscan_baton baton;
|
||||
struct volume_group *save_vg;
|
||||
struct dm_config_tree *save_meta;
|
||||
struct device *save_dev = NULL;
|
||||
uint32_t save_seqno = 0;
|
||||
int found_new_pvs = 0;
|
||||
int retried_reads = 0;
|
||||
int missing_devs = 0;
|
||||
int check_new_pvs = 0;
|
||||
int found;
|
||||
|
||||
save_vg = NULL;
|
||||
save_meta = NULL;
|
||||
save_dev = NULL;
|
||||
save_seqno = 0;
|
||||
|
||||
dm_list_init(&pvs_scan);
|
||||
dm_list_init(&pvs_drop);
|
||||
dm_list_init(&pvs_new);
|
||||
|
||||
log_debug_lvmetad("Rescan VG %s to update lvmetad (seqno %u).", vg->name, vg->seqno);
|
||||
log_debug_lvmetad("Rescanning VG %s (seqno %u).", vg->name, vg->seqno);
|
||||
|
||||
/*
|
||||
* Make sure this command knows about all PVs from lvmetad.
|
||||
* Another host may have added a PV to the VG, and some
|
||||
* commands do not always populate their lvmcache with
|
||||
* all devs from lvmetad, so they would fail to find
|
||||
* the new PV when scanning the VG. So make sure this
|
||||
* command knows about all PVs from lvmetad.
|
||||
*/
|
||||
lvmcache_seed_infos_from_lvmetad(cmd);
|
||||
|
||||
@@ -1872,111 +1841,54 @@ static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct v
|
||||
dm_list_add(&pvs_scan, &devl->list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rescan labels/metadata only from devs that we previously
|
||||
* saw in the VG. If we find below that there are new PVs
|
||||
* in the VG, we'll have to rescan all devices to find which
|
||||
* device(s) are now being used.
|
||||
*/
|
||||
log_debug_lvmetad("Rescan VG %s scanning data from devs in previous metadata.", vg->name);
|
||||
|
||||
label_scan_devs(cmd, cmd->full_filter, &pvs_scan);
|
||||
scan_more:
|
||||
|
||||
/*
|
||||
* Check if any pvs_scan entries are no longer PVs.
|
||||
* In that case, label_read/_find_label_header will have
|
||||
* found no label_header, and would have dropped the
|
||||
* info struct for the device from lvmcache. So, if
|
||||
* we look up the info struct here and don't find it,
|
||||
* we can infer it's no longer a PV.
|
||||
*
|
||||
* FIXME: we should record specific results from the
|
||||
* label_read and then check specifically for whatever
|
||||
* result means "no label was found", rather than going
|
||||
* about this indirectly via the lvmcache side effects.
|
||||
*/
|
||||
dm_list_iterate_items_safe(devl, devlsafe, &pvs_scan) {
|
||||
if (!(info = lvmcache_info_from_pvid(devl->dev->pvid, devl->dev, 0))) {
|
||||
/* Another host removed this PV from the VG. */
|
||||
log_debug_lvmetad("Rescan VG %s from %s dropping dev (no label).",
|
||||
vg->name, dev_name(devl->dev));
|
||||
dm_list_move(&pvs_drop, &devl->list);
|
||||
}
|
||||
}
|
||||
|
||||
fic.type = FMT_INSTANCE_MDAS | FMT_INSTANCE_AUX_MDAS;
|
||||
fic.context.vg_ref.vg_name = vg->name;
|
||||
fic.context.vg_ref.vg_id = vgid;
|
||||
|
||||
retry_reads:
|
||||
|
||||
if (!(fid = fmt->ops->create_instance(fmt, &fic))) {
|
||||
/* FIXME: are there only internal reasons for failures here? */
|
||||
log_error("Reading VG %s failed to create format instance.", vg->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: not sure if this is necessary */
|
||||
fid->ref_count++;
|
||||
|
||||
baton.fid = fid;
|
||||
baton.cmd = cmd;
|
||||
|
||||
/*
|
||||
* FIXME: this vg_read path does not have the ability to repair
|
||||
* any problems with the VG, e.g. VG on one dev has an older
|
||||
* seqno. When vg_read() is reworked, we need to fall back
|
||||
* to using that from here (and vg_read's from lvmetad) when
|
||||
* there is a problem. Perhaps by disabling lvmetad when a
|
||||
* VG problem is detected, causing commands to fully fall
|
||||
* back to disk, which will repair the VG. Then lvmetad can
|
||||
* be repopulated and re-enabled (possibly automatically.)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Do a low level vg_read on each dev, verify the vg returned
|
||||
* from metadata on each device is for the VG being read
|
||||
* (the PV may have been removed from the VG being read and
|
||||
* added to a different one), and return this vg to the caller
|
||||
* as the current vg to use.
|
||||
*
|
||||
* The label scan above will have saved in lvmcache which
|
||||
* vg each device is used in, so we could figure that part
|
||||
* out without doing the vg_read.
|
||||
* Run the equivalent of lvmetad_pvscan_single on each dev in the VG.
|
||||
*/
|
||||
dm_list_iterate_items_safe(devl, devlsafe, &pvs_scan) {
|
||||
if (!devl->dev)
|
||||
continue;
|
||||
|
||||
log_debug_lvmetad("Rescan VG %s getting metadata from %s.",
|
||||
vg->name, dev_name(devl->dev));
|
||||
log_debug_lvmetad("Rescan VG %s scanning %s.", vg->name, dev_name(devl->dev));
|
||||
|
||||
if (!label_read(devl->dev, &label, 0)) {
|
||||
/* Another host removed this PV from the VG. */
|
||||
log_debug_lvmetad("Rescan VG %s found %s was removed.", vg->name, dev_name(devl->dev));
|
||||
|
||||
if ((info = lvmcache_info_from_pvid(devl->dev->pvid, NULL, 0)))
|
||||
lvmcache_del(info);
|
||||
|
||||
/*
|
||||
* The info struct for this dev knows what and where
|
||||
* the mdas are for this dev (the label scan saved
|
||||
* the mda locations for this dev on the lvmcache info struct).
|
||||
*/
|
||||
if (!(info = lvmcache_info_from_pvid(devl->dev->pvid, devl->dev, 0))) {
|
||||
log_debug_lvmetad("Rescan VG %s from %s dropping dev (no info).",
|
||||
vg->name, dev_name(devl->dev));
|
||||
dm_list_move(&pvs_drop, &devl->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
info = (struct lvmcache_info *) label->info;
|
||||
|
||||
baton.vg = NULL;
|
||||
baton.fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
|
||||
if (!baton.fid)
|
||||
return_NULL;
|
||||
|
||||
if (baton.fid->fmt->features & FMT_OBSOLETE) {
|
||||
log_debug_lvmetad("Ignoring obsolete format on PV %s in VG %s.", dev_name(devl->dev), vg->name);
|
||||
lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
|
||||
dm_list_move(&pvs_drop, &devl->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read VG metadata from this dev's mdas.
|
||||
*/
|
||||
lvmcache_foreach_mda(info, _lvmetad_pvscan_vg_single, &baton);
|
||||
lvmcache_foreach_mda(info, _lvmetad_pvscan_single, &baton);
|
||||
|
||||
/*
|
||||
* The PV may have been removed from the VG by another host
|
||||
* since we last read the VG.
|
||||
*/
|
||||
if (!baton.vg) {
|
||||
log_debug_lvmetad("Rescan VG %s from %s dropping dev (no metadata).",
|
||||
vg->name, dev_name(devl->dev));
|
||||
log_debug_lvmetad("Rescan VG %s did not find %s.", vg->name, dev_name(devl->dev));
|
||||
lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
|
||||
dm_list_move(&pvs_drop, &devl->list);
|
||||
continue;
|
||||
}
|
||||
@@ -1986,15 +1898,10 @@ static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct v
|
||||
* different VG since we last read the VG.
|
||||
*/
|
||||
if (strcmp(baton.vg->name, vg->name)) {
|
||||
log_debug_lvmetad("Rescan VG %s from %s dropping dev (other VG %s).",
|
||||
vg->name, dev_name(devl->dev), baton.vg->name);
|
||||
release_vg(baton.vg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(vgmeta = export_vg_to_config_tree(baton.vg))) {
|
||||
log_error("VG export to config tree failed");
|
||||
log_debug_lvmetad("Rescan VG %s found different VG %s on PV %s.",
|
||||
vg->name, baton.vg->name, dev_name(devl->dev));
|
||||
release_vg(baton.vg);
|
||||
dm_list_move(&pvs_drop, &devl->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2004,35 +1911,20 @@ static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct v
|
||||
* read from each other dev.
|
||||
*/
|
||||
|
||||
if (save_vg && (save_seqno != baton.vg->seqno)) {
|
||||
/* FIXME: fall back to vg_read to correct this. */
|
||||
log_warn("WARNING: inconsistent metadata for VG %s on devices %s seqno %u and %s seqno %u.",
|
||||
vg->name, dev_name(save_dev), save_seqno,
|
||||
dev_name(devl->dev), baton.vg->seqno);
|
||||
log_warn("WARNING: temporarily disable lvmetad to repair metadata.");
|
||||
if (!save_seqno)
|
||||
save_seqno = baton.vg->seqno;
|
||||
|
||||
/* Use the most recent */
|
||||
if (save_seqno < baton.vg->seqno) {
|
||||
release_vg(save_vg);
|
||||
dm_config_destroy(save_meta);
|
||||
save_vg = baton.vg;
|
||||
save_meta = vgmeta;
|
||||
save_seqno = baton.vg->seqno;
|
||||
save_dev = devl->dev;
|
||||
} else {
|
||||
release_vg(baton.vg);
|
||||
dm_config_destroy(vgmeta);
|
||||
}
|
||||
continue;
|
||||
if (!(vgmeta = export_vg_to_config_tree(baton.vg))) {
|
||||
log_error("VG export to config tree failed");
|
||||
release_vg(baton.vg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!save_vg) {
|
||||
save_vg = baton.vg;
|
||||
save_meta = vgmeta;
|
||||
save_seqno = baton.vg->seqno;
|
||||
if (!vgmeta_ret) {
|
||||
vgmeta_ret = vgmeta;
|
||||
save_dev = devl->dev;
|
||||
} else {
|
||||
struct dm_config_node *meta1 = save_meta->root;
|
||||
struct dm_config_node *meta1 = vgmeta_ret->root;
|
||||
struct dm_config_node *meta2 = vgmeta->root;
|
||||
struct dm_config_node *sib1 = meta1->sib;
|
||||
struct dm_config_node *sib2 = meta2->sib;
|
||||
@@ -2057,128 +1949,73 @@ static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct v
|
||||
meta2->sib = NULL;
|
||||
|
||||
if (compare_config(meta1, meta2)) {
|
||||
/* FIXME: fall back to vg_read to correct this. */
|
||||
log_warn("WARNING: inconsistent metadata for VG %s on devices %s seqno %u and %s seqno %u.",
|
||||
vg->name, dev_name(save_dev), save_seqno,
|
||||
dev_name(devl->dev), baton.vg->seqno);
|
||||
log_warn("WARNING: temporarily disable lvmetad to repair metadata.");
|
||||
log_error("VG %s metadata comparison failed for device %s vs %s",
|
||||
vg->name, dev_name(devl->dev), save_dev ? dev_name(save_dev) : "none");
|
||||
_log_debug_inequality(vg->name, save_meta->root, vgmeta->root);
|
||||
_log_debug_inequality(vg->name, vgmeta_ret->root, vgmeta->root);
|
||||
|
||||
meta1->sib = sib1;
|
||||
meta2->sib = sib2;
|
||||
|
||||
/* no right choice, just use the previous copy */
|
||||
release_vg(baton.vg);
|
||||
dm_config_destroy(vgmeta);
|
||||
dm_config_destroy(vgmeta_ret);
|
||||
release_vg(baton.vg);
|
||||
return NULL;
|
||||
}
|
||||
meta1->sib = sib1;
|
||||
meta2->sib = sib2;
|
||||
release_vg(baton.vg);
|
||||
dm_config_destroy(vgmeta);
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: see above */
|
||||
fid->ref_count--;
|
||||
|
||||
/*
|
||||
* Look for any new PVs in the VG metadata that were not in our
|
||||
* previous version of the VG.
|
||||
*
|
||||
* (Don't look for new PVs after a rescan and retry.)
|
||||
*/
|
||||
found_new_pvs = 0;
|
||||
|
||||
if (save_vg && !retried_reads) {
|
||||
dm_list_iterate_items(pvl_new, &save_vg->pvs) {
|
||||
found = 0;
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (pvl_new->pv->dev != pvl->pv->dev)
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* PV in new VG metadata not found in old VG metadata.
|
||||
* There's a good chance we don't know about this new
|
||||
* PV or what device it's on; a label scan is needed
|
||||
* of all devices so we know which device the VG is
|
||||
* now using.
|
||||
*/
|
||||
if (!found) {
|
||||
found_new_pvs++;
|
||||
strncpy(pvid_s, (char *) &pvl_new->pv->id, sizeof(pvid_s) - 1);
|
||||
if (!id_write_format((const struct id *)&pvid_s, uuid, sizeof(uuid)))
|
||||
stack;
|
||||
log_debug_lvmetad("Rescan VG %s found new PV %s.", vg->name, uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!save_vg && retried_reads) {
|
||||
log_error("VG %s not found after rescanning devices.", vg->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a full rescan of devices, then look up which devices the
|
||||
* scan found for this VG name, and select those devices to
|
||||
* read metadata from in the loop above (rather than the list
|
||||
* of devices we created from our last copy of the vg metadata.)
|
||||
*
|
||||
* Case 1: VG we knew is no longer on any of the devices we knew it
|
||||
* to be on (save_vg is NULL, which means the metadata wasn't found
|
||||
* when reading mdas on each of the initial pvs_scan devices).
|
||||
* Rescan all devs and then retry reading metadata from the devs that
|
||||
* the scan finds associated with this VG.
|
||||
*
|
||||
* Case 2: VG has new PVs but we don't know what devices they are
|
||||
* so rescan all devs and then retry reading metadata from the devs
|
||||
* that the scan finds associated with this VG.
|
||||
*
|
||||
* (N.B. after a retry, we don't check for found_new_pvs.)
|
||||
*/
|
||||
if (!save_vg || found_new_pvs) {
|
||||
if (!save_vg)
|
||||
log_debug_lvmetad("Rescan VG %s did not find VG on previous devs.", vg->name);
|
||||
if (found_new_pvs)
|
||||
log_debug_lvmetad("Rescan VG %s scanning all devs to find new PVs.", vg->name);
|
||||
|
||||
label_scan(cmd);
|
||||
|
||||
if (!(vginfo = lvmcache_vginfo_from_vgname(vg->name, NULL))) {
|
||||
log_error("VG %s vg info not found after rescanning devices.", vg->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set pvs_scan to devs that the label scan found
|
||||
* in the VG and retry the metadata reading loop.
|
||||
* Look for any new PVs in the VG metadata that were not in our
|
||||
* previous version of the VG. Add them to pvs_new to be
|
||||
* scanned in this loop just like the old PVs.
|
||||
*/
|
||||
dm_list_init(&pvs_scan);
|
||||
|
||||
if (!lvmcache_get_vg_devs(cmd, vginfo, &pvs_scan)) {
|
||||
log_error("VG %s info devs not found after rescanning devices.", vg->name);
|
||||
goto out;
|
||||
if (!check_new_pvs) {
|
||||
check_new_pvs = 1;
|
||||
dm_list_iterate_items(pvl_new, &baton.vg->pvs) {
|
||||
found = 0;
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
if (pvl_new->pv->dev != pvl->pv->dev)
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
continue;
|
||||
if (!pvl_new->pv->dev) {
|
||||
strncpy(pvid_s, (char *) &pvl_new->pv->id, sizeof(pvid_s) - 1);
|
||||
if (!id_write_format((const struct id *)&pvid_s, uuid, sizeof(uuid)))
|
||||
stack;
|
||||
log_error("Device not found for PV %s in VG %s", uuid, vg->name);
|
||||
missing_devs++;
|
||||
continue;
|
||||
}
|
||||
if (!(devl_new = dm_pool_zalloc(cmd->mem, sizeof(*devl_new))))
|
||||
return_NULL;
|
||||
devl_new->dev = pvl_new->pv->dev;
|
||||
dm_list_add(&pvs_new, &devl_new->list);
|
||||
log_debug_lvmetad("Rescan VG %s found %s was added.", vg->name, dev_name(devl_new->dev));
|
||||
}
|
||||
}
|
||||
|
||||
log_debug_lvmetad("Rescan VG %s has %d PVs after label scan.",
|
||||
vg->name, dm_list_size(&pvs_scan));
|
||||
release_vg(baton.vg);
|
||||
}
|
||||
|
||||
if (save_vg)
|
||||
release_vg(save_vg);
|
||||
if (save_meta)
|
||||
dm_config_destroy(save_meta);
|
||||
save_vg = NULL;
|
||||
save_meta = NULL;
|
||||
save_dev = NULL;
|
||||
save_seqno = 0;
|
||||
found_new_pvs = 0;
|
||||
retried_reads = 1;
|
||||
goto retry_reads;
|
||||
/*
|
||||
* Do the same scanning above for any new PVs.
|
||||
*/
|
||||
if (!dm_list_empty(&pvs_new)) {
|
||||
dm_list_init(&pvs_scan);
|
||||
dm_list_splice(&pvs_scan, &pvs_new);
|
||||
dm_list_init(&pvs_new);
|
||||
log_debug_lvmetad("Rescan VG %s found new PVs to scan.", vg->name);
|
||||
goto scan_more;
|
||||
}
|
||||
|
||||
if (missing_devs) {
|
||||
if (vgmeta_ret)
|
||||
dm_config_destroy(vgmeta_ret);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2187,50 +2024,52 @@ static struct volume_group *_lvmetad_pvscan_vg(struct cmd_context *cmd, struct v
|
||||
dm_list_iterate_items(devl, &pvs_drop) {
|
||||
if (!devl->dev)
|
||||
continue;
|
||||
log_debug_lvmetad("Rescan VG %s removing %s from lvmetad.", vg->name, dev_name(devl->dev));
|
||||
if (!lvmetad_pv_gone_by_dev(devl->dev)) {
|
||||
/* FIXME: use an error path that disables lvmetad */
|
||||
log_error("Failed to remove %s from lvmetad.", dev_name(devl->dev));
|
||||
}
|
||||
log_debug_lvmetad("Rescan VG %s dropping %s.", vg->name, dev_name(devl->dev));
|
||||
if (!lvmetad_pv_gone_by_dev(devl->dev))
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update lvmetad with the newly read version of the VG.
|
||||
* When the seqno is unchanged the cached VG can be left.
|
||||
* Update the VG in lvmetad.
|
||||
*/
|
||||
if (save_vg && (save_seqno != vg->seqno)) {
|
||||
dm_list_iterate_items(devl, &pvs_scan) {
|
||||
if (!devl->dev)
|
||||
continue;
|
||||
log_debug_lvmetad("Rescan VG %s removing %s from lvmetad to replace.",
|
||||
vg->name, dev_name(devl->dev));
|
||||
if (!lvmetad_pv_gone_by_dev(devl->dev)) {
|
||||
/* FIXME: use an error path that disables lvmetad */
|
||||
log_error("Failed to remove %s from lvmetad.", dev_name(devl->dev));
|
||||
}
|
||||
if (vgmeta_ret) {
|
||||
fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
|
||||
if (!(vg_ret = import_vg_from_config_tree(vgmeta_ret, fid))) {
|
||||
log_error("VG import from config tree failed");
|
||||
lvmcache_fmt(info)->ops->destroy_instance(fid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_debug_lvmetad("Rescan VG %s updating lvmetad from seqno %u to seqno %u.",
|
||||
vg->name, vg->seqno, save_seqno);
|
||||
|
||||
/*
|
||||
* If this vg_update fails the cached metadata in
|
||||
* lvmetad will remain invalid.
|
||||
* Update lvmetad with the newly read version of the VG.
|
||||
* When the seqno is unchanged the cached VG can be left.
|
||||
*/
|
||||
save_vg->lvmetad_update_pending = 1;
|
||||
if (!lvmetad_vg_update_finish(save_vg)) {
|
||||
/* FIXME: use an error path that disables lvmetad */
|
||||
log_error("Failed to update lvmetad with new VG meta");
|
||||
if (save_seqno != vg->seqno) {
|
||||
dm_list_iterate_items(devl, &pvs_scan) {
|
||||
if (!devl->dev)
|
||||
continue;
|
||||
log_debug_lvmetad("Rescan VG %s dropping to replace %s.", vg->name, dev_name(devl->dev));
|
||||
if (!lvmetad_pv_gone_by_dev(devl->dev))
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
log_debug_lvmetad("Rescan VG %s updating lvmetad from seqno %u to seqno %u.",
|
||||
vg->name, vg->seqno, save_seqno);
|
||||
|
||||
/*
|
||||
* If this vg_update fails the cached metadata in
|
||||
* lvmetad will remain invalid.
|
||||
*/
|
||||
vg_ret->lvmetad_update_pending = 1;
|
||||
if (!lvmetad_vg_update_finish(vg_ret))
|
||||
log_error("Failed to update lvmetad with new VG meta");
|
||||
}
|
||||
dm_config_destroy(vgmeta_ret);
|
||||
}
|
||||
out:
|
||||
if (!save_vg && fid)
|
||||
fmt->ops->destroy_instance(fid);
|
||||
if (save_meta)
|
||||
dm_config_destroy(save_meta);
|
||||
if (save_vg)
|
||||
log_debug_lvmetad("Rescan VG %s done (new seqno %u).", save_vg->name, save_vg->seqno);
|
||||
return save_vg;
|
||||
if (vg_ret)
|
||||
log_debug_lvmetad("Rescan VG %s done (seqno %u).", vg_ret->name, vg_ret->seqno);
|
||||
return vg_ret;
|
||||
}
|
||||
|
||||
int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
@@ -2240,12 +2079,9 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
struct label *label;
|
||||
struct lvmcache_info *info;
|
||||
struct _lvmetad_pvscan_baton baton;
|
||||
const struct format_type *fmt;
|
||||
/* Create a dummy instance. */
|
||||
struct format_instance_ctx fic = { .type = 0 };
|
||||
|
||||
log_debug_lvmetad("Scan metadata from dev %s", dev_name(dev));
|
||||
|
||||
if (!lvmetad_used()) {
|
||||
log_error("Cannot proceed since lvmetad is not active.");
|
||||
return 0;
|
||||
@@ -2256,31 +2092,23 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
|
||||
log_print_unless_silent("No PV info found on %s for PVID %s.", dev_name(dev), dev->pvid);
|
||||
if (!label_read(dev, &label, 0)) {
|
||||
log_print_unless_silent("No PV label found on %s.", dev_name(dev));
|
||||
if (!lvmetad_pv_gone_by_dev(dev))
|
||||
goto_bad;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(label = lvmcache_get_label(info))) {
|
||||
log_print_unless_silent("No PV label found for %s.", dev_name(dev));
|
||||
if (!lvmetad_pv_gone_by_dev(dev))
|
||||
goto_bad;
|
||||
return 1;
|
||||
}
|
||||
info = (struct lvmcache_info *) label->info;
|
||||
|
||||
fmt = lvmcache_fmt(info);
|
||||
|
||||
baton.cmd = cmd;
|
||||
baton.vg = NULL;
|
||||
baton.fid = fmt->ops->create_instance(fmt, &fic);
|
||||
baton.fid = lvmcache_fmt(info)->ops->create_instance(lvmcache_fmt(info), &fic);
|
||||
|
||||
if (!baton.fid)
|
||||
goto_bad;
|
||||
|
||||
if (fmt->features & FMT_OBSOLETE) {
|
||||
fmt->ops->destroy_instance(baton.fid);
|
||||
if (baton.fid->fmt->features & FMT_OBSOLETE) {
|
||||
lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
|
||||
log_warn("WARNING: Disabling lvmetad cache which does not support obsolete (lvm1) metadata.");
|
||||
lvmetad_set_disabled(cmd, LVMETAD_DISABLE_REASON_LVM1);
|
||||
_found_lvm1_metadata = 1;
|
||||
@@ -2294,9 +2122,9 @@ int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
lvmcache_foreach_mda(info, _lvmetad_pvscan_single, &baton);
|
||||
|
||||
if (!baton.vg)
|
||||
fmt->ops->destroy_instance(baton.fid);
|
||||
lvmcache_fmt(info)->ops->destroy_instance(baton.fid);
|
||||
|
||||
if (!lvmetad_pv_found(cmd, (const struct id *) &dev->pvid, dev, fmt,
|
||||
if (!lvmetad_pv_found(cmd, (const struct id *) &dev->pvid, dev, lvmcache_fmt(info),
|
||||
label->sector, baton.vg, found_vgnames, changed_vgnames)) {
|
||||
release_vg(baton.vg);
|
||||
goto_bad;
|
||||
@@ -2362,13 +2190,6 @@ int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait)
|
||||
replacing_other_update = 1;
|
||||
}
|
||||
|
||||
label_scan(cmd);
|
||||
|
||||
if (lvmcache_found_duplicate_pvs()) {
|
||||
log_warn("WARNING: Scan found duplicate PVs.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_verbose("Scanning all devices to update lvmetad.");
|
||||
|
||||
if (!(iter = dev_iter_create(cmd->lvmetad_filter, 1))) {
|
||||
@@ -2551,18 +2372,11 @@ static int _lvmetad_get_pv_cache_list(struct cmd_context *cmd, struct dm_list *p
|
||||
*/
|
||||
static void _update_pv_in_udev(struct cmd_context *cmd, dev_t devt)
|
||||
{
|
||||
|
||||
/*
|
||||
* FIXME: this is diabled as part of removing dev_opens
|
||||
* to integrate bcache. If this is really needed, we
|
||||
* can do a separate open/close here.
|
||||
*/
|
||||
log_debug_devs("SKIP device %d:%d open to update udev",
|
||||
(int)MAJOR(devt), (int)MINOR(devt));
|
||||
|
||||
#if 0
|
||||
struct device *dev;
|
||||
|
||||
log_debug_devs("device %d:%d open to update udev",
|
||||
(int)MAJOR(devt), (int)MINOR(devt));
|
||||
|
||||
if (!(dev = dev_cache_get_by_devt(devt, cmd->lvmetad_filter))) {
|
||||
log_error("_update_pv_in_udev no dev found");
|
||||
return;
|
||||
@@ -2575,7 +2389,6 @@ static void _update_pv_in_udev(struct cmd_context *cmd, dev_t devt)
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2747,8 +2560,6 @@ void lvmetad_validate_global_cache(struct cmd_context *cmd, int force)
|
||||
*/
|
||||
_lvmetad_get_pv_cache_list(cmd, &pvc_before);
|
||||
|
||||
log_debug_lvmetad("Rescan all devices to validate global cache.");
|
||||
|
||||
/*
|
||||
* Update the local lvmetad cache so it correctly reflects any
|
||||
* changes made on remote hosts. (It's possible that this command
|
||||
@@ -2817,7 +2628,7 @@ void lvmetad_validate_global_cache(struct cmd_context *cmd, int force)
|
||||
_update_changed_pvs_in_udev(cmd, &pvc_before, &pvc_after);
|
||||
}
|
||||
|
||||
log_debug_lvmetad("Rescanned all devices");
|
||||
log_debug_lvmetad("Validating global lvmetad cache finished");
|
||||
}
|
||||
|
||||
int lvmetad_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const char *vgid)
|
||||
|
76
lib/cache/lvmetad.h
vendored
76
lib/cache/lvmetad.h
vendored
@@ -17,8 +17,6 @@
|
||||
|
||||
#include "config-util.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct volume_group;
|
||||
struct cmd_context;
|
||||
struct dm_config_tree;
|
||||
@@ -165,48 +163,38 @@ void lvmetad_clear_disabled(struct cmd_context *cmd);
|
||||
|
||||
# else /* LVMETAD_SUPPORT */
|
||||
|
||||
static inline int lvmetad_connect(struct cmd_context *cmd) {return 0;}
|
||||
static inline void lvmetad_disconnect(void) {}
|
||||
static inline void lvmetad_make_unused(struct cmd_context *cmd) {}
|
||||
static inline int lvmetad_used(void) {return 0;}
|
||||
static inline void lvmetad_set_socket(const char *thing) {}
|
||||
static inline int lvmetad_socket_present(void) {return 0;}
|
||||
static inline int lvmetad_pidfile_present(void) {return 0;}
|
||||
static inline void lvmetad_set_token(const struct dm_config_value *filter) {}
|
||||
static inline void lvmetad_release_token(void) {}
|
||||
static inline int lvmetad_vg_update_pending(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_vg_update_finish(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_vg_remove_pending(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_vg_remove_finish(struct volume_group *vg) {return 1;}
|
||||
static inline int lvmetad_pv_found(struct cmd_context *cmd, const struct id *pvid, struct device *dev,
|
||||
const struct format_type *fmt, uint64_t label_sector,
|
||||
struct volume_group *vg,
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames) {return 1;}
|
||||
static inline int lvmetad_pv_gone(dev_t devno, const char *pv_name) {return 1;}
|
||||
static inline int lvmetad_pv_gone_by_dev(struct device *dev) {return 1;}
|
||||
static inline int lvmetad_pv_list_to_lvmcache(struct cmd_context *cmd) {return 1;}
|
||||
static inline int lvmetad_pv_lookup(struct cmd_context *cmd, struct id pvid, int *found) {return 0;}
|
||||
static inline int lvmetad_pv_lookup_by_dev(struct cmd_context *cmd, struct device *dev, int *found) {return 0;}
|
||||
static inline int lvmetad_vg_list_to_lvmcache(struct cmd_context *cmd) {return 1;}
|
||||
static inline int lvmetad_get_vgnameids(struct cmd_context *cmd, struct dm_list *vgnameids) {return 0;}
|
||||
static inline struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd,
|
||||
const char *vgname, const char *vgid) {return NULL;}
|
||||
static inline int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames) {return 0;}
|
||||
|
||||
static inline int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait) {return 0;}
|
||||
|
||||
static inline int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg) {return 0;}
|
||||
static inline void lvmetad_validate_global_cache(struct cmd_context *cmd, int force) {}
|
||||
static inline int lvmetad_token_matches(struct cmd_context *cmd) {return 1;}
|
||||
|
||||
static inline int lvmetad_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const char *vgid) {return 0;}
|
||||
|
||||
static inline int lvmetad_is_disabled(struct cmd_context *cmd, const char **reason) {return 0;}
|
||||
static inline void lvmetad_set_disabled(struct cmd_context *cmd, const char *reason) {}
|
||||
static inline void lvmetad_clear_disabled(struct cmd_context *cmd) {}
|
||||
# define lvmetad_disconnect() do { } while (0)
|
||||
# define lvmetad_connect(cmd) (0)
|
||||
# define lvmetad_make_unused(cmd) do { } while (0)
|
||||
# define lvmetad_used() (0)
|
||||
# define lvmetad_set_socket(a) do { } while (0)
|
||||
# define lvmetad_socket_present() (0)
|
||||
# define lvmetad_pidfile_present() (0)
|
||||
# define lvmetad_set_token(a) do { } while (0)
|
||||
# define lvmetad_release_token() do { } while (0)
|
||||
# define lvmetad_vg_update(vg) (1)
|
||||
# define lvmetad_vg_update_pending(vg) (1)
|
||||
# define lvmetad_vg_update_finish(vg) (1)
|
||||
# define lvmetad_vg_remove_pending(vg) (1)
|
||||
# define lvmetad_vg_remove_finish(vg) (1)
|
||||
# define lvmetad_pv_found(cmd, pvid, dev, fmt, label_sector, vg, found_vgnames, changed_vgnames) (1)
|
||||
# define lvmetad_pv_gone(devno, pv_name) (1)
|
||||
# define lvmetad_pv_gone_by_dev(dev) (1)
|
||||
# define lvmetad_pv_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_pv_lookup(cmd, pvid, found) (0)
|
||||
# define lvmetad_pv_lookup_by_dev(cmd, dev, found) (0)
|
||||
# define lvmetad_vg_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_get_vgnameids(cmd, vgnameids) do { } while (0)
|
||||
# define lvmetad_vg_lookup(cmd, vgname, vgid) (NULL)
|
||||
# define lvmetad_pvscan_single(cmd, dev, found_vgnames, changed_vgnames) (0)
|
||||
# define lvmetad_pvscan_all_devs(cmd, do_wait) (0)
|
||||
# define lvmetad_vg_clear_outdated_pvs(vg) do { } while (0)
|
||||
# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
|
||||
# define lvmetad_vg_is_foreign(cmd, vgname, vgid) (0)
|
||||
# define lvmetad_token_matches(cmd) (1)
|
||||
# define lvmetad_is_disabled(cmd, reason) (0)
|
||||
# define lvmetad_set_disabled(cmd, reason) do { } while (0)
|
||||
# define lvmetad_clear_disabled(cmd) do { } while (0)
|
||||
|
||||
# endif /* LVMETAD_SUPPORT */
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
# Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This file is part of LVM2.
|
||||
#
|
||||
@@ -14,10 +14,11 @@ srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES=\
|
||||
vdo/status.c
|
||||
SOURCES = cache.c
|
||||
|
||||
LIB_SHARED = liblvm2cache.$(LIB_SUFFIX)
|
||||
LIB_VERSION = $(LIB_VERSION_LVM)
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
LIB_NAME = libdevicemapper
|
||||
LIB_STATIC = $(LIB_NAME).a
|
||||
install: install_lvm2_plugin
|
@@ -636,6 +636,16 @@ static int _process_config(struct cmd_context *cmd)
|
||||
*/
|
||||
cmd->default_settings.udev_fallback = udev_disabled ? 1 : -1;
|
||||
|
||||
#ifdef AIO_SUPPORT
|
||||
cmd->use_aio = find_config_tree_bool(cmd, devices_use_aio_CFG, NULL);
|
||||
#else
|
||||
cmd->use_aio = 0;
|
||||
#endif
|
||||
if (cmd->use_aio && !dev_async_setup(cmd))
|
||||
cmd->use_aio = 0;
|
||||
|
||||
log_debug_io("%ssing asynchronous I/O.", cmd->use_aio ? "U" : "Not u");
|
||||
|
||||
init_retry_deactivation(find_config_tree_bool(cmd, activation_retry_deactivation_CFG, NULL));
|
||||
|
||||
init_activation_checks(find_config_tree_bool(cmd, activation_checks_CFG, NULL));
|
||||
@@ -688,6 +698,9 @@ static int _process_config(struct cmd_context *cmd)
|
||||
if (find_config_tree_bool(cmd, report_two_word_unknown_device_CFG, NULL))
|
||||
init_unknown_device_name("unknown device");
|
||||
|
||||
init_detect_internal_vg_cache_corruption
|
||||
(find_config_tree_bool(cmd, global_detect_internal_vg_cache_corruption_CFG, NULL));
|
||||
|
||||
if (!_init_system_id(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -1061,7 +1074,7 @@ static int _init_dev_cache(struct cmd_context *cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MAX_FILTERS 10
|
||||
#define MAX_FILTERS 9
|
||||
|
||||
static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
{
|
||||
@@ -1142,19 +1155,10 @@ static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
#if 0
|
||||
/* signature filter. Required. */
|
||||
if (!(filters[nr_filt] = signature_filter_create(cmd->dev_types))) {
|
||||
log_error("Failed to create signature device filter");
|
||||
goto bad;
|
||||
}
|
||||
nr_filt++;
|
||||
#endif
|
||||
|
||||
/* md component filter. Optional, non-critical. */
|
||||
if (find_config_tree_bool(cmd, devices_md_component_detection_CFG, NULL)) {
|
||||
init_md_filtering(1);
|
||||
if ((filters[nr_filt] = md_filter_create(cmd, cmd->dev_types)))
|
||||
if ((filters[nr_filt] = md_filter_create(cmd->dev_types)))
|
||||
nr_filt++;
|
||||
}
|
||||
|
||||
@@ -1294,7 +1298,7 @@ int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache)
|
||||
lvm_stat_ctim(&ts, &st);
|
||||
cts = config_file_timestamp(cmd->cft);
|
||||
if (timespeccmp(&ts, &cts, >) &&
|
||||
!persistent_filter_load(cmd->filter, NULL))
|
||||
!persistent_filter_load(cmd->mem, cmd->filter, NULL))
|
||||
log_verbose("Failed to load existing device cache from %s",
|
||||
dev_cache);
|
||||
}
|
||||
@@ -1657,6 +1661,7 @@ static void _init_rand(struct cmd_context *cmd)
|
||||
|
||||
static void _init_globals(struct cmd_context *cmd)
|
||||
{
|
||||
init_full_scan_done(0);
|
||||
init_mirror_in_sync(0);
|
||||
}
|
||||
|
||||
@@ -1787,8 +1792,6 @@ void destroy_config_context(struct cmd_context *cmd)
|
||||
/*
|
||||
* A "config context" is a very light weight toolcontext that
|
||||
* is only used for reading config settings from lvm.conf.
|
||||
*
|
||||
* FIXME: this needs to go back to parametrized create_toolcontext()
|
||||
*/
|
||||
struct cmd_context *create_config_context(void)
|
||||
{
|
||||
@@ -1805,9 +1808,6 @@ struct cmd_context *create_config_context(void)
|
||||
if (!(cmd->libmem = dm_pool_create("library", 4 * 1024)))
|
||||
goto_out;
|
||||
|
||||
if (!(cmd->mem = dm_pool_create("command", 4 * 1024)))
|
||||
goto out;
|
||||
|
||||
dm_list_init(&cmd->config_files);
|
||||
dm_list_init(&cmd->tags);
|
||||
|
||||
@@ -1838,7 +1838,7 @@ out:
|
||||
}
|
||||
|
||||
/* Entry point */
|
||||
struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
const char *system_dir,
|
||||
unsigned set_buffering,
|
||||
unsigned threaded,
|
||||
@@ -1865,8 +1865,7 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
log_error("Failed to allocate command context");
|
||||
return NULL;
|
||||
}
|
||||
cmd->is_long_lived = is_clvmd;
|
||||
cmd->is_clvmd = is_clvmd;
|
||||
cmd->is_long_lived = is_long_lived;
|
||||
cmd->threaded = threaded ? 1 : 0;
|
||||
cmd->handles_missing_pvs = 0;
|
||||
cmd->handles_unknown_segments = 0;
|
||||
@@ -1995,10 +1994,6 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
if (!_init_formats(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!lvmcache_init(cmd))
|
||||
goto_out;
|
||||
|
||||
/* FIXME: move into lvmcache_init */
|
||||
if (!init_lvmcache_orphans(cmd))
|
||||
goto_out;
|
||||
|
||||
@@ -2020,6 +2015,7 @@ struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
if (set_filters && !init_filters(cmd, 1))
|
||||
goto_out;
|
||||
|
||||
cmd->default_settings.cache_vgmetadata = 1;
|
||||
cmd->current_settings = cmd->default_settings;
|
||||
|
||||
cmd->initialized.config = 1;
|
||||
@@ -2133,7 +2129,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
activation_release();
|
||||
lvmcache_destroy(cmd, 0, 0);
|
||||
label_scan_destroy(cmd);
|
||||
label_exit();
|
||||
_destroy_segtypes(&cmd->segtypes);
|
||||
_destroy_formats(cmd, &cmd->formats);
|
||||
@@ -2160,6 +2155,8 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
|
||||
cmd->lib_dir = NULL;
|
||||
|
||||
label_init();
|
||||
|
||||
if (!_init_lvm_conf(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -2220,9 +2217,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
if (!_init_formats(cmd))
|
||||
return_0;
|
||||
|
||||
if (!lvmcache_init(cmd))
|
||||
return_0;
|
||||
|
||||
if (!init_lvmcache_orphans(cmd))
|
||||
return_0;
|
||||
|
||||
@@ -2250,13 +2244,12 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
int flags;
|
||||
|
||||
if (cmd->dump_filter && cmd->filter && cmd->filter->dump &&
|
||||
!cmd->filter->dump(cmd->filter, 1))
|
||||
!cmd->filter->dump(cmd->filter, cmd->mem, 1))
|
||||
stack;
|
||||
|
||||
archive_exit(cmd);
|
||||
backup_exit(cmd);
|
||||
lvmcache_destroy(cmd, 0, 0);
|
||||
label_scan_destroy(cmd);
|
||||
label_exit();
|
||||
_destroy_segtypes(&cmd->segtypes);
|
||||
_destroy_formats(cmd, &cmd->formats);
|
||||
|
@@ -42,7 +42,6 @@ struct config_info {
|
||||
int cache_vgmetadata;
|
||||
const char *msg_prefix;
|
||||
const char *fmt_name;
|
||||
const char *dmeventd_executable;
|
||||
uint64_t unit_factor;
|
||||
int cmd_name; /* Show command name? */
|
||||
mode_t umask;
|
||||
@@ -165,13 +164,7 @@ struct cmd_context {
|
||||
unsigned vg_notify:1;
|
||||
unsigned lv_notify:1;
|
||||
unsigned pv_notify:1;
|
||||
unsigned activate_component:1; /* command activates component LV */
|
||||
unsigned process_component_lvs:1; /* command processes also component LVs */
|
||||
unsigned mirror_warn_printed:1; /* command already printed warning about non-monitored mirrors */
|
||||
unsigned pvscan_cache_single:1;
|
||||
unsigned can_use_one_scan:1;
|
||||
unsigned is_clvmd:1;
|
||||
unsigned use_full_md_check:1;
|
||||
unsigned use_aio:1;
|
||||
|
||||
/*
|
||||
* Filtering.
|
||||
@@ -237,7 +230,7 @@ struct cmd_context {
|
||||
* system_dir may be NULL to use the default value.
|
||||
* The environment variable LVM_SYSTEM_DIR always takes precedence.
|
||||
*/
|
||||
struct cmd_context *create_toolcontext(unsigned is_clvmd,
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
const char *system_dir,
|
||||
unsigned set_buffering,
|
||||
unsigned threaded,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "toolcontext.h"
|
||||
#include "lvm-file.h"
|
||||
#include "memlock.h"
|
||||
#include "label.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
@@ -280,7 +279,7 @@ struct dm_config_tree *config_file_open_and_read(const char *config_file,
|
||||
}
|
||||
|
||||
log_very_verbose("Loading config file: %s", config_file);
|
||||
if (!config_file_read(cft)) {
|
||||
if (!config_file_read(cmd->mem, cft)) {
|
||||
log_error("Failed to load config file %s", config_file);
|
||||
goto bad;
|
||||
}
|
||||
@@ -490,32 +489,102 @@ int override_config_tree_from_profile(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct process_config_file_params {
|
||||
struct dm_config_tree *cft;
|
||||
struct device *dev;
|
||||
off_t offset;
|
||||
size_t size;
|
||||
off_t offset2;
|
||||
size_t size2;
|
||||
checksum_fn_t checksum_fn;
|
||||
uint32_t checksum;
|
||||
int checksum_only;
|
||||
int no_dup_node_check;
|
||||
lvm_callback_fn_t config_file_read_fd_callback;
|
||||
void *config_file_read_fd_context;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void _process_config_file_buffer(int failed, unsigned ioflags, void *context, const void *data)
|
||||
{
|
||||
struct process_config_file_params *pcfp = context;
|
||||
const char *fb = data, *fe;
|
||||
|
||||
if (failed) {
|
||||
pcfp->ret = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if (pcfp->checksum_fn && pcfp->checksum !=
|
||||
(pcfp->checksum_fn(pcfp->checksum_fn(INITIAL_CRC, (const uint8_t *)fb, pcfp->size),
|
||||
(const uint8_t *)(fb + pcfp->size), pcfp->size2))) {
|
||||
log_error("%s: Checksum error at offset %" PRIu64, dev_name(pcfp->dev), (uint64_t) pcfp->offset);
|
||||
pcfp->ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!pcfp->checksum_only) {
|
||||
fe = fb + pcfp->size + pcfp->size2;
|
||||
if (pcfp->no_dup_node_check) {
|
||||
if (!dm_config_parse_without_dup_node_check(pcfp->cft, fb, fe))
|
||||
pcfp->ret = 0;
|
||||
} else if (!dm_config_parse(pcfp->cft, fb, fe))
|
||||
pcfp->ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (pcfp->config_file_read_fd_callback)
|
||||
pcfp->config_file_read_fd_callback(!pcfp->ret, ioflags, pcfp->config_file_read_fd_context, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* When checksum_only is set, the checksum of buffer is only matched
|
||||
* and function avoids parsing of mda into config tree which
|
||||
* remains unmodified and should not be used.
|
||||
*/
|
||||
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
int config_file_read_fd(struct dm_pool *mem, struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, size_t size, off_t offset2, size_t size2,
|
||||
checksum_fn_t checksum_fn, uint32_t checksum,
|
||||
int checksum_only, int no_dup_node_check)
|
||||
int checksum_only, int no_dup_node_check, unsigned ioflags,
|
||||
lvm_callback_fn_t config_file_read_fd_callback, void *config_file_read_fd_context)
|
||||
{
|
||||
char *fb, *fe;
|
||||
char *fb;
|
||||
int r = 0;
|
||||
int use_mmap = 1;
|
||||
off_t mmap_offset = 0;
|
||||
char *buf = NULL;
|
||||
int use_mmap = 1;
|
||||
const char *buf = NULL;
|
||||
unsigned circular = size2 ? 1 : 0; /* Wrapped around end of disk metadata buffer? */
|
||||
struct config_source *cs = dm_config_get_custom(cft);
|
||||
struct process_config_file_params *pcfp;
|
||||
|
||||
if (!_is_file_based_config_source(cs->type)) {
|
||||
log_error(INTERNAL_ERROR "config_file_read_fd: expected file, special file "
|
||||
"or profile config source, found %s config source.",
|
||||
_config_source_names[cs->type]);
|
||||
return 0;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(pcfp = dm_pool_zalloc(mem, sizeof(*pcfp)))) {
|
||||
log_debug("config_file_read_fd: process_config_file_params struct allocation failed");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pcfp->cft = cft;
|
||||
pcfp->dev = dev;
|
||||
pcfp->offset = offset;
|
||||
pcfp->size = size;
|
||||
pcfp->offset2 = offset2;
|
||||
pcfp->size2 = size2;
|
||||
pcfp->checksum_fn = checksum_fn;
|
||||
pcfp->checksum = checksum;
|
||||
pcfp->checksum_only = checksum_only;
|
||||
pcfp->no_dup_node_check = no_dup_node_check;
|
||||
pcfp->config_file_read_fd_callback = config_file_read_fd_callback;
|
||||
pcfp->config_file_read_fd_context = config_file_read_fd_context;
|
||||
pcfp->ret = 1;
|
||||
|
||||
/* Only use mmap with regular files */
|
||||
if (!(dev->flags & DEV_REGULAR) || size2)
|
||||
if (!(dev->flags & DEV_REGULAR) || circular)
|
||||
use_mmap = 0;
|
||||
|
||||
if (use_mmap) {
|
||||
@@ -525,67 +594,40 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_r
|
||||
MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
|
||||
if (fb == (caddr_t) (-1)) {
|
||||
log_sys_error("mmap", dev_name(dev));
|
||||
goto out;
|
||||
goto bad;
|
||||
}
|
||||
fb = fb + mmap_offset;
|
||||
} else {
|
||||
if (!(buf = dm_malloc(size + size2))) {
|
||||
log_error("Failed to allocate circular buffer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_read_bytes(dev, offset, size, buf))
|
||||
goto out;
|
||||
|
||||
if (size2) {
|
||||
if (!dev_read_bytes(dev, offset2, size2, buf + size))
|
||||
goto out;
|
||||
}
|
||||
|
||||
fb = buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* The checksum passed in is the checksum from the mda_header
|
||||
* preceding this metadata. They should always match.
|
||||
* FIXME: handle case where mda_header checksum is bad,
|
||||
* but the checksum calculated here is correct.
|
||||
*/
|
||||
if (checksum_fn && checksum !=
|
||||
(checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)fb, size),
|
||||
(const uint8_t *)(fb + size), size2))) {
|
||||
log_error("%s: Checksum error at offset %" PRIu64, dev_name(dev), (uint64_t) offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!checksum_only) {
|
||||
fe = fb + size + size2;
|
||||
if (no_dup_node_check) {
|
||||
if (!dm_config_parse_without_dup_node_check(cft, fb, fe))
|
||||
goto_out;
|
||||
} else {
|
||||
if (!dm_config_parse(cft, fb, fe))
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (!use_mmap)
|
||||
dm_free(buf);
|
||||
else {
|
||||
_process_config_file_buffer(0, ioflags, pcfp, fb + mmap_offset);
|
||||
r = pcfp->ret;
|
||||
/* unmap the file */
|
||||
if (munmap(fb - mmap_offset, size + mmap_offset)) {
|
||||
if (munmap(fb, size + mmap_offset)) {
|
||||
log_sys_error("munmap", dev_name(dev));
|
||||
r = 0;
|
||||
}
|
||||
} else {
|
||||
if (circular) {
|
||||
if (!(buf = dev_read_circular(dev, (uint64_t) offset, size, (uint64_t) offset2, size2, reason)))
|
||||
goto_out;
|
||||
_process_config_file_buffer(0, ioflags, pcfp, buf);
|
||||
dm_free((void *)buf);
|
||||
} else {
|
||||
dev_read_callback(dev, (uint64_t) offset, size, reason, ioflags, _process_config_file_buffer, pcfp);
|
||||
if (config_file_read_fd_callback)
|
||||
return 1;
|
||||
}
|
||||
r = pcfp->ret;
|
||||
}
|
||||
|
||||
out:
|
||||
return r;
|
||||
|
||||
bad:
|
||||
if (config_file_read_fd_callback)
|
||||
config_file_read_fd_callback(1, ioflags, config_file_read_fd_context, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_file_read(struct dm_config_tree *cft)
|
||||
int config_file_read(struct dm_pool *mem, struct dm_config_tree *cft)
|
||||
{
|
||||
const char *filename = NULL;
|
||||
struct config_source *cs = dm_config_get_custom(cft);
|
||||
@@ -613,8 +655,8 @@ int config_file_read(struct dm_config_tree *cft)
|
||||
}
|
||||
}
|
||||
|
||||
r = config_file_read_fd(cft, cf->dev, DEV_IO_MDA_CONTENT, 0, (size_t) info.st_size, 0, 0,
|
||||
(checksum_fn_t) NULL, 0, 0, 0);
|
||||
r = config_file_read_fd(mem, cft, cf->dev, DEV_IO_MDA_CONTENT, 0, (size_t) info.st_size, 0, 0,
|
||||
(checksum_fn_t) NULL, 0, 0, 0, 0, NULL, NULL);
|
||||
|
||||
if (!cf->keep_open) {
|
||||
if (!dev_close(cf->dev))
|
||||
|
@@ -239,11 +239,13 @@ config_source_t config_get_source_type(struct dm_config_tree *cft);
|
||||
typedef uint32_t (*checksum_fn_t) (uint32_t initial, const uint8_t *buf, uint32_t size);
|
||||
|
||||
struct dm_config_tree *config_open(config_source_t source, const char *filename, int keep_open);
|
||||
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
int config_file_read_fd(struct dm_pool *mem, struct dm_config_tree *cft, struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, size_t size, off_t offset2, size_t size2,
|
||||
checksum_fn_t checksum_fn, uint32_t checksum,
|
||||
int skip_parse, int no_dup_node_check);
|
||||
int config_file_read(struct dm_config_tree *cft);
|
||||
int skip_parse, int no_dup_node_check, unsigned ioflags,
|
||||
lvm_callback_fn_t config_file_read_fd_callback, void *config_file_read_fd_context);
|
||||
|
||||
int config_file_read(struct dm_pool *mem, struct dm_config_tree *cft);
|
||||
struct dm_config_tree *config_file_open_and_read(const char *config_file, config_source_t source,
|
||||
struct cmd_context *cmd);
|
||||
int config_write(struct dm_config_tree *cft, struct config_def_tree_spec *tree_spec,
|
||||
|
@@ -226,6 +226,16 @@ cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING,
|
||||
cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, "#S/dev", vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Directories containing device nodes to use with LVM.\n")
|
||||
|
||||
cfg(devices_use_aio_CFG, "use_aio", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_USE_AIO, vsn(2, 2, 178), NULL, 0, NULL,
|
||||
"Use linux asynchronous I/O for parallel device access where possible.\n")
|
||||
|
||||
cfg(devices_aio_max_CFG, "aio_max", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_AIO_MAX, vsn(2, 2, 178), NULL, 0, NULL,
|
||||
"Maximum number of asynchronous I/Os to issue concurrently.\n")
|
||||
|
||||
cfg(devices_aio_memory_CFG, "aio_memory", devices_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_AIO_MEMORY, vsn(2, 2, 178), NULL, 0, NULL,
|
||||
"Approximate maximum total amount of memory (in MB) used\n"
|
||||
"for asynchronous I/O buffers.\n")
|
||||
|
||||
cfg_array(devices_loopfiles_CFG, "loopfiles", devices_CFG_SECTION, CFG_DEFAULT_UNDEFINED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 2, 0), NULL, 0, NULL, NULL)
|
||||
|
||||
cfg(devices_obtain_device_list_from_udev_CFG, "obtain_device_list_from_udev", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV, vsn(2, 2, 85), NULL, 0, NULL,
|
||||
@@ -280,8 +290,8 @@ cfg_array(devices_filter_CFG, "filter", devices_CFG_SECTION, CFG_DEFAULT_COMMENT
|
||||
"device path names. Each regex is delimited by a vertical bar '|'\n"
|
||||
"(or any character) and is preceded by 'a' to accept the path, or\n"
|
||||
"by 'r' to reject the path. The first regex in the list to match the\n"
|
||||
"path is used, producing the 'a' or 'r' result for the device.\n"
|
||||
"When multiple path names exist for a block device, if any path name\n"
|
||||
"path is used, producing the 'a' or 'r' result for that path.\n"
|
||||
"If any of multiple existing path names for a block device\n"
|
||||
"matches an 'a' pattern before an 'r' pattern, then the device is\n"
|
||||
"accepted. If all the path names match an 'r' pattern first, then the\n"
|
||||
"device is rejected. Unmatching path names do not affect the accept\n"
|
||||
@@ -782,7 +792,7 @@ cfg(global_format_CFG, "format", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_
|
||||
" lvm1\n"
|
||||
" lvm2\n"
|
||||
"#\n")
|
||||
|
||||
|
||||
cfg_array(global_format_libraries_CFG, "format_libraries", global_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Shared libraries that process different metadata formats.\n"
|
||||
"If support for LVM1 metadata was compiled as a shared library use\n"
|
||||
@@ -868,8 +878,11 @@ cfg(global_abort_on_internal_errors_CFG, "abort_on_internal_errors", global_CFG_
|
||||
"Treat any internal errors as fatal errors, aborting the process that\n"
|
||||
"encountered the internal error. Please only enable for debugging.\n")
|
||||
|
||||
cfg(global_detect_internal_vg_cache_corruption_CFG, "detect_internal_vg_cache_corruption", global_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(2, 2, 96), NULL, vsn(2, 2, 174), NULL,
|
||||
"No longer used.\n")
|
||||
cfg(global_detect_internal_vg_cache_corruption_CFG, "detect_internal_vg_cache_corruption", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_DETECT_INTERNAL_VG_CACHE_CORRUPTION, vsn(2, 2, 96), NULL, 0, NULL,
|
||||
"Internal verification of VG structures.\n"
|
||||
"Check if CRC matches when a parsed VG is used multiple times. This\n"
|
||||
"is useful to catch unexpected changes to cached VG structures.\n"
|
||||
"Please only enable for debugging.\n")
|
||||
|
||||
cfg(global_metadata_read_only_CFG, "metadata_read_only", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_METADATA_READ_ONLY, vsn(2, 2, 75), NULL, 0, NULL,
|
||||
"No operations that change on-disk metadata are permitted.\n"
|
||||
|
@@ -32,6 +32,9 @@
|
||||
#define DEFAULT_SYSTEM_ID_SOURCE "none"
|
||||
#define DEFAULT_OBTAIN_DEVICE_LIST_FROM_UDEV 1
|
||||
#define DEFAULT_EXTERNAL_DEVICE_INFO_SOURCE "none"
|
||||
#define DEFAULT_USE_AIO 1
|
||||
#define DEFAULT_AIO_MAX 128
|
||||
#define DEFAULT_AIO_MEMORY 10
|
||||
#define DEFAULT_SYSFS_SCAN 1
|
||||
#define DEFAULT_MD_COMPONENT_DETECTION 1
|
||||
#define DEFAULT_FW_RAID_COMPONENT_DETECTION 0
|
||||
@@ -179,6 +182,7 @@
|
||||
#define DEFAULT_LOGLEVEL 0
|
||||
#define DEFAULT_INDENT 1
|
||||
#define DEFAULT_ABORT_ON_INTERNAL_ERRORS 0
|
||||
#define DEFAULT_DETECT_INTERNAL_VG_CACHE_CORRUPTION 0
|
||||
#define DEFAULT_UNITS "r"
|
||||
#define DEFAULT_SUFFIX 1
|
||||
#define DEFAULT_HOSTTAGS 0
|
||||
|
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "bcache.h"
|
||||
|
||||
// FIXME: need to define this in a common place (that doesn't pull in deps)
|
||||
#ifndef SECTOR_SHIFT
|
||||
#define SECTOR_SHIFT 9
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void byte_range_to_block_range(struct bcache *cache, uint64_t start, size_t len,
|
||||
block_address *bb, block_address *be)
|
||||
{
|
||||
block_address block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
*bb = start / block_size;
|
||||
*be = (start + len + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
static uint64_t _min(uint64_t lhs, uint64_t rhs)
|
||||
{
|
||||
if (rhs < lhs)
|
||||
return rhs;
|
||||
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
block_address bb, be;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
while (bb < be) {
|
||||
bcache_prefetch(cache, fd, bb);
|
||||
bb++;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data)
|
||||
{
|
||||
struct block *b;
|
||||
block_address bb, be;
|
||||
uint64_t block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
uint64_t block_offset = start % block_size;
|
||||
|
||||
bcache_prefetch_bytes(cache, fd, start, len);
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(cache, fd, bb, 0, &b))
|
||||
return false;
|
||||
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
memcpy(data, ((unsigned char *) b->data) + block_offset, blen);
|
||||
bcache_put(b);
|
||||
|
||||
block_offset = 0;
|
||||
len -= blen;
|
||||
data = ((unsigned char *) data) + blen;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// Writing bytes and zeroing bytes are very similar, so we factor out
|
||||
// this common code.
|
||||
|
||||
struct updater;
|
||||
|
||||
typedef bool (*partial_update_fn)(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len);
|
||||
typedef bool (*whole_update_fn)(struct updater *u, int fd, block_address bb, block_address be);
|
||||
|
||||
struct updater {
|
||||
struct bcache *cache;
|
||||
partial_update_fn partial_fn;
|
||||
whole_update_fn whole_fn;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static bool _update_bytes(struct updater *u, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
struct bcache *cache = u->cache;
|
||||
block_address bb, be;
|
||||
uint64_t block_size = bcache_block_sectors(cache) << SECTOR_SHIFT;
|
||||
uint64_t block_offset = start % block_size;
|
||||
uint64_t nr_whole;
|
||||
|
||||
byte_range_to_block_range(cache, start, len, &bb, &be);
|
||||
|
||||
// If the last block is partial, we will require a read, so let's
|
||||
// prefetch it.
|
||||
if ((start + len) % block_size)
|
||||
bcache_prefetch(cache, fd, (start + len) / block_size);
|
||||
|
||||
// First block may be partial
|
||||
if (block_offset) {
|
||||
size_t blen = _min(block_size - block_offset, len);
|
||||
if (!u->partial_fn(u, fd, bb, block_offset, blen))
|
||||
return false;
|
||||
|
||||
len -= blen;
|
||||
if (!len)
|
||||
return true;
|
||||
|
||||
bb++;
|
||||
}
|
||||
|
||||
// Now we write out a set of whole blocks
|
||||
nr_whole = len / block_size;
|
||||
if (!u->whole_fn(u, fd, bb, bb + nr_whole))
|
||||
return false;
|
||||
|
||||
bb += nr_whole;
|
||||
len -= nr_whole * block_size;
|
||||
|
||||
if (!len)
|
||||
return true;
|
||||
|
||||
// Finally we write a partial end block
|
||||
return u->partial_fn(u, fd, bb, 0, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _write_partial(struct updater *u, int fd, block_address bb,
|
||||
uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b))
|
||||
return false;
|
||||
|
||||
memcpy(((unsigned char *) b->data) + offset, u->data, len);
|
||||
u->data = ((unsigned char *) u->data) + len;
|
||||
|
||||
bcache_put(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _write_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
uint64_t block_size = bcache_block_sectors(u->cache) << SECTOR_SHIFT;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
// We don't need to read the block since we are overwriting
|
||||
// it completely.
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b))
|
||||
return false;
|
||||
memcpy(b->data, u->data, block_size);
|
||||
u->data = ((unsigned char *) u->data) + block_size;
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _write_partial;
|
||||
u.whole_fn = _write_whole;
|
||||
u.data = data;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _zero_partial(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b))
|
||||
return false;
|
||||
|
||||
memset(((unsigned char *) b->data) + offset, 0, len);
|
||||
bcache_put(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _zero_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b))
|
||||
return false;
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _zero_partial;
|
||||
u.whole_fn = _zero_whole;
|
||||
u.data = NULL;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static bool _set_partial(struct updater *u, int fd, block_address bb, uint64_t offset, size_t len)
|
||||
{
|
||||
struct block *b;
|
||||
uint8_t val = *((uint8_t *) u->data);
|
||||
|
||||
if (!bcache_get(u->cache, fd, bb, GF_DIRTY, &b))
|
||||
return false;
|
||||
|
||||
memset(((unsigned char *) b->data) + offset, val, len);
|
||||
bcache_put(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _set_whole(struct updater *u, int fd, block_address bb, block_address be)
|
||||
{
|
||||
struct block *b;
|
||||
uint8_t val = *((uint8_t *) u->data);
|
||||
uint64_t len = bcache_block_sectors(u->cache) * 512;
|
||||
|
||||
for (; bb != be; bb++) {
|
||||
if (!bcache_get(u->cache, fd, bb, GF_ZERO, &b))
|
||||
return false;
|
||||
memset((unsigned char *) b->data, val, len);
|
||||
bcache_put(b);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bcache_set_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, uint8_t val)
|
||||
{
|
||||
struct updater u;
|
||||
|
||||
u.cache = cache;
|
||||
u.partial_fn = _set_partial;
|
||||
u.whole_fn = _set_whole;
|
||||
u.data = &val;
|
||||
|
||||
return _update_bytes(&u, fd, start, len);
|
||||
}
|
||||
|
1144
lib/device/bcache.c
1144
lib/device/bcache.c
File diff suppressed because it is too large
Load Diff
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef BCACHE_H
|
||||
#define BCACHE_H
|
||||
|
||||
#include "libdevmapper.h"
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
// FIXME: move somewhere more sensible
|
||||
#define container_of(v, t, head) \
|
||||
((t *)((const char *)(v) - (const char *)&((t *) 0)->head))
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
enum dir {
|
||||
DIR_READ,
|
||||
DIR_WRITE
|
||||
};
|
||||
|
||||
typedef uint64_t block_address;
|
||||
typedef uint64_t sector_t;
|
||||
|
||||
typedef void io_complete_fn(void *context, int io_error);
|
||||
|
||||
struct io_engine {
|
||||
void (*destroy)(struct io_engine *e);
|
||||
bool (*issue)(struct io_engine *e, enum dir d, int fd,
|
||||
sector_t sb, sector_t se, void *data, void *context);
|
||||
bool (*wait)(struct io_engine *e, io_complete_fn fn);
|
||||
unsigned (*max_io)(struct io_engine *e);
|
||||
};
|
||||
|
||||
struct io_engine *create_async_io_engine(void);
|
||||
struct io_engine *create_sync_io_engine(void);
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
|
||||
struct bcache;
|
||||
struct block {
|
||||
/* clients may only access these three fields */
|
||||
int fd;
|
||||
uint64_t index;
|
||||
void *data;
|
||||
|
||||
struct bcache *cache;
|
||||
struct dm_list list;
|
||||
struct dm_list hash;
|
||||
|
||||
unsigned flags;
|
||||
unsigned ref_count;
|
||||
int error;
|
||||
enum dir io_dir;
|
||||
};
|
||||
|
||||
/*
|
||||
* Ownership of engine passes. Engine will be destroyed even if this fails.
|
||||
*/
|
||||
struct bcache *bcache_create(sector_t block_size, unsigned nr_cache_blocks,
|
||||
struct io_engine *engine);
|
||||
void bcache_destroy(struct bcache *cache);
|
||||
|
||||
enum bcache_get_flags {
|
||||
/*
|
||||
* The block will be zeroed before get_block returns it. This
|
||||
* potentially avoids a read if the block is not already in the cache.
|
||||
* GF_DIRTY is implicit.
|
||||
*/
|
||||
GF_ZERO = (1 << 0),
|
||||
|
||||
/*
|
||||
* Indicates the caller is intending to change the data in the block, a
|
||||
* writeback will occur after the block is released.
|
||||
*/
|
||||
GF_DIRTY = (1 << 1)
|
||||
};
|
||||
|
||||
sector_t bcache_block_sectors(struct bcache *cache);
|
||||
unsigned bcache_nr_cache_blocks(struct bcache *cache);
|
||||
unsigned bcache_max_prefetches(struct bcache *cache);
|
||||
|
||||
/*
|
||||
* Use the prefetch method to take advantage of asynchronous IO. For example,
|
||||
* if you wanted to read a block from many devices concurrently you'd do
|
||||
* something like this:
|
||||
*
|
||||
* dm_list_iterate_items (dev, &devices)
|
||||
* bcache_prefetch(cache, dev->fd, block);
|
||||
*
|
||||
* dm_list_iterate_items (dev, &devices) {
|
||||
* if (!bcache_get(cache, dev->fd, block, &b))
|
||||
* fail();
|
||||
*
|
||||
* process_block(b);
|
||||
* }
|
||||
*
|
||||
* It's slightly sub optimal, since you may not run the gets in the order that
|
||||
* they complete. But we're talking a very small difference, and it's worth it
|
||||
* to keep callbacks out of this interface.
|
||||
*/
|
||||
void bcache_prefetch(struct bcache *cache, int fd, block_address index);
|
||||
|
||||
/*
|
||||
* Returns true on success.
|
||||
*/
|
||||
bool bcache_get(struct bcache *cache, int fd, block_address index,
|
||||
unsigned flags, struct block **result);
|
||||
void bcache_put(struct block *b);
|
||||
|
||||
/*
|
||||
* flush() does not attempt to writeback locked blocks. flush will fail
|
||||
* (return false), if any unlocked dirty data cannot be written back.
|
||||
*/
|
||||
bool bcache_flush(struct bcache *cache);
|
||||
|
||||
/*
|
||||
* Removes a block from the cache.
|
||||
*
|
||||
* If the block is dirty it will be written back first. If the writeback fails
|
||||
* false will be returned.
|
||||
*
|
||||
* If the block is currently held false will be returned.
|
||||
*/
|
||||
bool bcache_invalidate(struct bcache *cache, int fd, block_address index);
|
||||
|
||||
/*
|
||||
* Invalidates all blocks on the given descriptor. Call this before closing
|
||||
* the descriptor to make sure everything is written back.
|
||||
*/
|
||||
bool bcache_invalidate_fd(struct bcache *cache, int fd);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// The next four functions are utilities written in terms of the above api.
|
||||
|
||||
// Prefetches the blocks neccessary to satisfy a byte range.
|
||||
void bcache_prefetch_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
|
||||
|
||||
// Reads, writes and zeroes bytes. Returns false if errors occur.
|
||||
bool bcache_read_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
|
||||
bool bcache_write_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, void *data);
|
||||
bool bcache_zero_bytes(struct bcache *cache, int fd, uint64_t start, size_t len);
|
||||
bool bcache_set_bytes(struct bcache *cache, int fd, uint64_t start, size_t len, uint8_t val);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
@@ -73,6 +73,7 @@ static void _dev_init(struct device *dev, int max_error_count)
|
||||
dev->ext.src = DEV_EXT_NONE;
|
||||
|
||||
dm_list_init(&dev->aliases);
|
||||
dm_list_init(&dev->open_list);
|
||||
}
|
||||
|
||||
void dev_destroy_file(struct device *dev)
|
||||
@@ -274,8 +275,10 @@ static int _compare_paths(const char *path0, const char *path1)
|
||||
if (slash1 < slash0)
|
||||
return 1;
|
||||
|
||||
(void) dm_strncpy(p0, path0, sizeof(p0));
|
||||
(void) dm_strncpy(p1, path1, sizeof(p1));
|
||||
strncpy(p0, path0, sizeof(p0) - 1);
|
||||
p0[sizeof(p0) - 1] = '\0';
|
||||
strncpy(p1, path1, sizeof(p1) - 1);
|
||||
p1[sizeof(p1) - 1] = '\0';
|
||||
s0 = p0 + 1;
|
||||
s1 = p1 + 1;
|
||||
|
||||
@@ -1076,11 +1079,12 @@ static int _insert(const char *path, const struct stat *info,
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dev_cache_scan(void)
|
||||
static void _full_scan(int dev_scan)
|
||||
{
|
||||
struct dir_list *dl;
|
||||
|
||||
_cache.has_scanned = 1;
|
||||
if (_cache.has_scanned && !dev_scan)
|
||||
return;
|
||||
|
||||
_insert_dirs(&_cache.dirs);
|
||||
|
||||
@@ -1088,6 +1092,9 @@ void dev_cache_scan(void)
|
||||
|
||||
dm_list_iterate_items(dl, &_cache.files)
|
||||
_insert_file(dl->dir);
|
||||
|
||||
_cache.has_scanned = 1;
|
||||
init_full_scan_done(1);
|
||||
}
|
||||
|
||||
int dev_cache_has_scanned(void)
|
||||
@@ -1095,6 +1102,14 @@ int dev_cache_has_scanned(void)
|
||||
return _cache.has_scanned;
|
||||
}
|
||||
|
||||
void dev_cache_scan(int do_scan)
|
||||
{
|
||||
if (!do_scan)
|
||||
_cache.has_scanned = 1;
|
||||
else
|
||||
_full_scan(1);
|
||||
}
|
||||
|
||||
static int _init_preferred_names(struct cmd_context *cmd)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
@@ -1158,6 +1173,7 @@ out:
|
||||
int dev_cache_init(struct cmd_context *cmd)
|
||||
{
|
||||
_cache.names = NULL;
|
||||
_cache.has_scanned = 0;
|
||||
|
||||
if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
|
||||
return_0;
|
||||
@@ -1231,12 +1247,24 @@ int dev_cache_check_for_open_devices(void)
|
||||
|
||||
int dev_cache_exit(void)
|
||||
{
|
||||
struct btree_iter *b;
|
||||
int num_open = 0;
|
||||
|
||||
dev_async_exit();
|
||||
|
||||
if (_cache.names)
|
||||
if ((num_open = _check_for_open_devices(1)) > 0)
|
||||
log_error(INTERNAL_ERROR "%d device(s) were left open and have been closed.", num_open);
|
||||
|
||||
if (_cache.devices) {
|
||||
/* FIXME Replace with structured devbuf cache */
|
||||
b = btree_first(_cache.devices);
|
||||
while (b) {
|
||||
devbufs_release(btree_get_data(b));
|
||||
b = btree_next(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (_cache.mem)
|
||||
dm_pool_destroy(_cache.mem);
|
||||
|
||||
@@ -1377,7 +1405,6 @@ struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
struct stat buf;
|
||||
struct device *d = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
int info_available = 0;
|
||||
int ret = 1;
|
||||
|
||||
if (d && (d->flags & DEV_REGULAR))
|
||||
return d;
|
||||
@@ -1400,30 +1427,15 @@ struct device *dev_cache_get(const char *name, struct dev_filter *f)
|
||||
_insert(name, info_available ? &buf : NULL, 0, obtain_device_list_from_udev());
|
||||
d = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
if (!d) {
|
||||
dev_cache_scan();
|
||||
_full_scan(0);
|
||||
d = (struct device *) dm_hash_lookup(_cache.names, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
if (d && (d->flags & DEV_REGULAR))
|
||||
return d;
|
||||
|
||||
if (f && !(d->flags & DEV_REGULAR)) {
|
||||
ret = f->passes_filter(f, d);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by name defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (f && !(d->flags & DEV_REGULAR) && !ret)
|
||||
if (!d || (f && !(d->flags & DEV_REGULAR) && !(f->passes_filter(f, d))))
|
||||
return NULL;
|
||||
|
||||
log_debug_devs("%s: Using device (%d:%d)", dev_name(d), (int) MAJOR(d->dev), (int) MINOR(d->dev));
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -1450,7 +1462,6 @@ struct device *dev_cache_get_by_devt(dev_t dev, struct dev_filter *f)
|
||||
const char *sysfs_dir;
|
||||
struct stat info;
|
||||
struct device *d = _dev_cache_seek_devt(dev);
|
||||
int ret;
|
||||
|
||||
if (d && (d->flags & DEV_REGULAR))
|
||||
return d;
|
||||
@@ -1466,40 +1477,31 @@ struct device *dev_cache_get_by_devt(dev_t dev, struct dev_filter *f)
|
||||
}
|
||||
|
||||
if (lstat(path, &info)) {
|
||||
log_debug("No sysfs entry for %d:%d errno %d at %s.",
|
||||
(int)MAJOR(dev), (int)MINOR(dev), errno, path);
|
||||
log_debug("No sysfs entry for %d:%d.",
|
||||
(int)MAJOR(dev), (int)MINOR(dev));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dev_cache_scan();
|
||||
_full_scan(0);
|
||||
d = _dev_cache_seek_devt(dev);
|
||||
}
|
||||
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
if (d->flags & DEV_REGULAR)
|
||||
return d;
|
||||
|
||||
if (!f)
|
||||
return d;
|
||||
|
||||
ret = f->passes_filter(f, d);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by number defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return d;
|
||||
|
||||
return NULL;
|
||||
return (d && (!f || (d->flags & DEV_REGULAR) ||
|
||||
f->passes_filter(f, d))) ? d : NULL;
|
||||
}
|
||||
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int unused)
|
||||
void dev_cache_full_scan(struct dev_filter *f)
|
||||
{
|
||||
if (f && f->wipe) {
|
||||
f->wipe(f); /* might call _full_scan(1) */
|
||||
if (!full_scan_done())
|
||||
_full_scan(1);
|
||||
} else
|
||||
_full_scan(1);
|
||||
}
|
||||
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan)
|
||||
{
|
||||
struct dev_iter *di = dm_malloc(sizeof(*di));
|
||||
|
||||
@@ -1508,6 +1510,13 @@ struct dev_iter *dev_iter_create(struct dev_filter *f, int unused)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dev_scan && !trust_cache()) {
|
||||
/* Flag gets reset between each command */
|
||||
if (!full_scan_done())
|
||||
dev_cache_full_scan(f);
|
||||
} else
|
||||
_full_scan(0);
|
||||
|
||||
di->current = btree_first(_cache.devices);
|
||||
di->filter = f;
|
||||
if (di->filter)
|
||||
@@ -1532,27 +1541,13 @@ static struct device *_iter_next(struct dev_iter *iter)
|
||||
|
||||
struct device *dev_iter_get(struct dev_iter *iter)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
int ret;
|
||||
|
||||
while (iter->current) {
|
||||
struct device *d = _iter_next(iter);
|
||||
ret = 1;
|
||||
|
||||
f = iter->filter;
|
||||
|
||||
if (f && !(d->flags & DEV_REGULAR)) {
|
||||
ret = f->passes_filter(f, d);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
log_debug_devs("get device by iter defer filter %s", dev_name(d));
|
||||
d->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!f || (d->flags & DEV_REGULAR) || ret)
|
||||
if (!iter->filter || (d->flags & DEV_REGULAR) ||
|
||||
iter->filter->passes_filter(iter->filter, d)) {
|
||||
log_debug_devs("%s: Using device (%d:%d)", dev_name(d), (int) MAJOR(d->dev), (int) MINOR(d->dev));
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@@ -23,10 +23,10 @@
|
||||
* predicate for devices.
|
||||
*/
|
||||
struct dev_filter {
|
||||
int (*passes_filter) (struct dev_filter * f, struct device * dev);
|
||||
void (*destroy) (struct dev_filter * f);
|
||||
void (*wipe) (struct dev_filter * f);
|
||||
int (*dump) (struct dev_filter * f, int merge_existing);
|
||||
int (*passes_filter) (struct dev_filter *f, struct device *dev);
|
||||
void (*destroy) (struct dev_filter *f);
|
||||
void (*wipe) (struct dev_filter *f);
|
||||
int (*dump) (struct dev_filter *f, struct dm_pool *mem, int merge_existing);
|
||||
void *private;
|
||||
unsigned use_count;
|
||||
};
|
||||
@@ -46,8 +46,10 @@ int dev_cache_exit(void);
|
||||
*/
|
||||
int dev_cache_check_for_open_devices(void);
|
||||
|
||||
void dev_cache_scan(void);
|
||||
/* Trigger(1) or avoid(0) a scan */
|
||||
void dev_cache_scan(int do_scan);
|
||||
int dev_cache_has_scanned(void);
|
||||
void dev_cache_full_scan(struct dev_filter *f);
|
||||
|
||||
int dev_cache_add_dir(const char *path);
|
||||
int dev_cache_add_loopfile(const char *path);
|
||||
@@ -64,7 +66,7 @@ void dev_set_preferred_name(struct dm_str_list *sl, struct device *dev);
|
||||
* Object for iterating through the cache.
|
||||
*/
|
||||
struct dev_iter;
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int unused);
|
||||
struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan);
|
||||
void dev_iter_destroy(struct dev_iter *iter);
|
||||
struct device *dev_iter_get(struct dev_iter *iter);
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "lib.h"
|
||||
#include "device.h"
|
||||
#include "metadata.h"
|
||||
#include "lvmcache.h"
|
||||
#include "memlock.h"
|
||||
#include "locking.h"
|
||||
|
||||
@@ -52,6 +53,13 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Always read at least 8k from disk.
|
||||
* This seems to be a good compromise for the existing LVM2 metadata layout.
|
||||
*/
|
||||
#define MIN_READ_SIZE (8 * 1024)
|
||||
|
||||
static DM_LIST_INIT(_open_devices);
|
||||
static unsigned _dev_size_seqno = 1;
|
||||
|
||||
static const char *_reasons[] = {
|
||||
@@ -72,38 +80,318 @@ static const char *_reason_text(dev_io_reason_t reason)
|
||||
return _reasons[(unsigned) reason];
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the memory holding the last data we read
|
||||
*/
|
||||
static void _release_devbuf(struct device_buffer *devbuf)
|
||||
{
|
||||
dm_free(devbuf->malloc_address);
|
||||
devbuf->malloc_address = NULL;
|
||||
}
|
||||
|
||||
void devbufs_release(struct device *dev)
|
||||
{
|
||||
if ((dev->flags & DEV_REGULAR))
|
||||
return;
|
||||
|
||||
_release_devbuf(&dev->last_devbuf);
|
||||
_release_devbuf(&dev->last_extra_devbuf);
|
||||
}
|
||||
|
||||
#ifdef AIO_SUPPORT
|
||||
|
||||
# include <libaio.h>
|
||||
|
||||
static io_context_t _aio_ctx = 0;
|
||||
static struct io_event *_aio_events = NULL;
|
||||
static int _aio_max = 0;
|
||||
static int64_t _aio_memory_max = 0;
|
||||
static int _aio_must_queue = 0; /* Have we reached AIO capacity? */
|
||||
|
||||
static DM_LIST_INIT(_aio_queue);
|
||||
|
||||
#define DEFAULT_AIO_COLLECTION_EVENTS 32
|
||||
|
||||
int dev_async_setup(struct cmd_context *cmd)
|
||||
{
|
||||
int r;
|
||||
|
||||
_aio_max = find_config_tree_int(cmd, devices_aio_max_CFG, NULL);
|
||||
_aio_memory_max = find_config_tree_int(cmd, devices_aio_memory_CFG, NULL) * 1024 * 1024;
|
||||
|
||||
/* Threshold is zero? */
|
||||
if (!_aio_max || !_aio_memory_max) {
|
||||
if (_aio_ctx)
|
||||
dev_async_exit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Already set up? */
|
||||
if (_aio_ctx)
|
||||
return 1;
|
||||
|
||||
log_debug_io("Setting up aio context for up to %" PRId64 " MB across %d events.", _aio_memory_max, _aio_max);
|
||||
|
||||
if (!_aio_events && !(_aio_events = dm_zalloc(sizeof(*_aio_events) * DEFAULT_AIO_COLLECTION_EVENTS))) {
|
||||
log_error("Failed to allocate io_event array for asynchronous I/O.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((r = io_setup(_aio_max, &_aio_ctx)) < 0) {
|
||||
/*
|
||||
* Possible errors:
|
||||
* ENOSYS - aio not available in current kernel
|
||||
* EAGAIN - _aio_max is too big
|
||||
* EFAULT - invalid pointer
|
||||
* EINVAL - _aio_ctx != 0 or kernel aio limits exceeded
|
||||
* ENOMEM
|
||||
*/
|
||||
log_warn("WARNING: Asynchronous I/O setup for %d events failed: %s", _aio_max, strerror(-r));
|
||||
log_warn("WARNING: Using only synchronous I/O.");
|
||||
dm_free(_aio_events);
|
||||
_aio_events = NULL;
|
||||
_aio_ctx = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Reset aio context after fork */
|
||||
int dev_async_reset(struct cmd_context *cmd)
|
||||
{
|
||||
log_debug_io("Resetting asynchronous I/O context.");
|
||||
_aio_ctx = 0;
|
||||
dm_free(_aio_events);
|
||||
_aio_events = NULL;
|
||||
|
||||
return dev_async_setup(cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Track the amount of in-flight async I/O.
|
||||
* If it exceeds the defined threshold set _aio_must_queue.
|
||||
*/
|
||||
static void _update_aio_counters(int nr, ssize_t bytes)
|
||||
{
|
||||
static int64_t aio_bytes = 0;
|
||||
static int aio_count = 0;
|
||||
|
||||
aio_bytes += bytes;
|
||||
aio_count += nr;
|
||||
|
||||
if (aio_count >= _aio_max || aio_bytes > _aio_memory_max)
|
||||
_aio_must_queue = 1;
|
||||
else
|
||||
_aio_must_queue = 0;
|
||||
}
|
||||
|
||||
static int _io(struct device_buffer *devbuf, unsigned ioflags);
|
||||
|
||||
int dev_async_getevents(void)
|
||||
{
|
||||
struct device_buffer *devbuf, *tmp;
|
||||
lvm_callback_fn_t dev_read_callback_fn;
|
||||
void *dev_read_callback_context;
|
||||
int r, event_nr;
|
||||
|
||||
if (!_aio_ctx)
|
||||
return 1;
|
||||
|
||||
do {
|
||||
/* FIXME Add timeout - currently NULL - waits for ever for at least 1 item */
|
||||
r = io_getevents(_aio_ctx, 1, DEFAULT_AIO_COLLECTION_EVENTS, _aio_events, NULL);
|
||||
if (r > 0)
|
||||
break;
|
||||
if (!r)
|
||||
return 1; /* Timeout elapsed */
|
||||
if (r == -EINTR)
|
||||
continue;
|
||||
if (r == -EAGAIN) {
|
||||
usleep(100);
|
||||
return 1; /* Give the caller the opportunity to do other work before repeating */
|
||||
}
|
||||
/*
|
||||
* ENOSYS - not supported by kernel
|
||||
* EFAULT - memory invalid
|
||||
* EINVAL - _aio_ctx invalid or min_nr/nr/timeout out of range
|
||||
*/
|
||||
log_error("Asynchronous event collection failed: %s", strerror(-r));
|
||||
return 0;
|
||||
} while (1);
|
||||
|
||||
for (event_nr = 0; event_nr < r; event_nr++) {
|
||||
devbuf = _aio_events[event_nr].obj->data;
|
||||
dm_free(_aio_events[event_nr].obj);
|
||||
|
||||
_update_aio_counters(-1, -devbuf->where.size);
|
||||
|
||||
dev_read_callback_fn = devbuf->dev_read_callback_fn;
|
||||
dev_read_callback_context = devbuf->dev_read_callback_context;
|
||||
|
||||
/* Clear the callbacks as a precaution */
|
||||
devbuf->dev_read_callback_context = NULL;
|
||||
devbuf->dev_read_callback_fn = NULL;
|
||||
|
||||
if (_aio_events[event_nr].res == devbuf->where.size) {
|
||||
if (dev_read_callback_fn)
|
||||
dev_read_callback_fn(0, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, (char *)devbuf->buf + devbuf->data_offset);
|
||||
} else {
|
||||
/* FIXME If partial read is possible, resubmit remainder */
|
||||
log_error_once("%s: Asynchronous I/O failed: read only %" PRIu64 " of %" PRIu64 " bytes at %" PRIu64,
|
||||
dev_name(devbuf->where.dev),
|
||||
(uint64_t) _aio_events[event_nr].res, (uint64_t) devbuf->where.size,
|
||||
(uint64_t) devbuf->where.start);
|
||||
_release_devbuf(devbuf);
|
||||
if (dev_read_callback_fn)
|
||||
dev_read_callback_fn(1, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, NULL);
|
||||
else
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit further queued events if we can */
|
||||
dm_list_iterate_items_gen_safe(devbuf, tmp, &_aio_queue, aio_queued) {
|
||||
if (_aio_must_queue)
|
||||
break;
|
||||
dm_list_del(&devbuf->aio_queued);
|
||||
_io(devbuf, 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _io_async(struct device_buffer *devbuf)
|
||||
{
|
||||
struct device_area *where = &devbuf->where;
|
||||
struct iocb *iocb;
|
||||
int r;
|
||||
|
||||
_update_aio_counters(1, devbuf->where.size);
|
||||
|
||||
if (!(iocb = dm_malloc(sizeof(*iocb)))) {
|
||||
log_error("Failed to allocate I/O control block array for asynchronous I/O.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
io_prep_pread(iocb, dev_fd(where->dev), devbuf->buf, where->size, where->start);
|
||||
iocb->data = devbuf;
|
||||
|
||||
do {
|
||||
r = io_submit(_aio_ctx, 1L, &iocb);
|
||||
if (r ==1)
|
||||
break; /* Success */
|
||||
if (r == -EAGAIN) {
|
||||
/* Try to release some resources then retry */
|
||||
usleep(100);
|
||||
if (dev_async_getevents())
|
||||
return_0;
|
||||
/* FIXME Add counter/timeout so we can't get stuck here for ever */
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Possible errors:
|
||||
* EFAULT - invalid data
|
||||
* ENOSYS - no aio support in kernel
|
||||
* EBADF - bad file descriptor in iocb
|
||||
* EINVAL - invalid _aio_ctx / iocb not initialised / invalid operation for this fd
|
||||
*/
|
||||
log_error("Asynchronous event submission failed: %s", strerror(-r));
|
||||
return 0;
|
||||
} while (1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dev_async_exit(void)
|
||||
{
|
||||
struct device_buffer *devbuf, *tmp;
|
||||
lvm_callback_fn_t dev_read_callback_fn;
|
||||
void *dev_read_callback_context;
|
||||
int r;
|
||||
|
||||
if (!_aio_ctx)
|
||||
return;
|
||||
|
||||
/* Discard any queued requests */
|
||||
dm_list_iterate_items_gen_safe(devbuf, tmp, &_aio_queue, aio_queued) {
|
||||
dm_list_del(&devbuf->aio_queued);
|
||||
|
||||
_update_aio_counters(-1, -devbuf->where.size);
|
||||
|
||||
dev_read_callback_fn = devbuf->dev_read_callback_fn;
|
||||
dev_read_callback_context = devbuf->dev_read_callback_context;
|
||||
|
||||
_release_devbuf(devbuf);
|
||||
|
||||
if (dev_read_callback_fn)
|
||||
dev_read_callback_fn(1, AIO_SUPPORTED_CODE_PATH, dev_read_callback_context, NULL);
|
||||
}
|
||||
|
||||
log_debug_io("Destroying aio context.");
|
||||
if ((r = io_destroy(_aio_ctx)) < 0)
|
||||
/* Returns -ENOSYS if aio not in kernel or -EINVAL if _aio_ctx invalid */
|
||||
log_error("Failed to destroy asynchronous I/O context: %s", strerror(-r));
|
||||
|
||||
dm_free(_aio_events);
|
||||
_aio_events = NULL;
|
||||
|
||||
_aio_ctx = 0;
|
||||
}
|
||||
|
||||
static void _queue_aio(struct device_buffer *devbuf)
|
||||
{
|
||||
dm_list_add(&_aio_queue, &devbuf->aio_queued);
|
||||
log_debug_io("Queueing aio.");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int _aio_ctx = 0;
|
||||
static int _aio_must_queue = 0;
|
||||
|
||||
int dev_async_setup(struct cmd_context *cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dev_async_reset(struct cmd_context *cmd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dev_async_getevents(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dev_async_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int _io_async(struct device_buffer *devbuf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _queue_aio(struct device_buffer *devbuf)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* AIO_SUPPORT */
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
* The standard io loop that keeps submitting an io until it's
|
||||
* all gone.
|
||||
*---------------------------------------------------------------*/
|
||||
static int _io(struct device_area *where, char *buffer, int should_write, dev_io_reason_t reason)
|
||||
static int _io_sync(struct device_buffer *devbuf)
|
||||
{
|
||||
struct device_area *where = &devbuf->where;
|
||||
int fd = dev_fd(where->dev);
|
||||
char *buffer = devbuf->buf;
|
||||
ssize_t n = 0;
|
||||
size_t total = 0;
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("Attempt to read an unopened device (%s).",
|
||||
dev_name(where->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug_io("%s %s:%8" PRIu64 " bytes (sync) at %" PRIu64 "%s (for %s)",
|
||||
should_write ? "Write" : "Read ", dev_name(where->dev),
|
||||
where->size, (uint64_t) where->start,
|
||||
(should_write && test_mode()) ? " (test mode - suppressed)" : "", _reason_text(reason));
|
||||
|
||||
/*
|
||||
* Skip all writes in test mode.
|
||||
*/
|
||||
if (should_write && test_mode())
|
||||
return 1;
|
||||
|
||||
if (where->size > SSIZE_MAX) {
|
||||
log_error("Read size too large: %" PRIu64, where->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lseek(fd, (off_t) where->start, SEEK_SET) == (off_t) -1) {
|
||||
log_error("%s: lseek %" PRIu64 " failed: %s",
|
||||
dev_name(where->dev), (uint64_t) where->start,
|
||||
@@ -113,7 +401,7 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io
|
||||
|
||||
while (total < (size_t) where->size) {
|
||||
do
|
||||
n = should_write ?
|
||||
n = devbuf->write ?
|
||||
write(fd, buffer, (size_t) where->size - total) :
|
||||
read(fd, buffer, (size_t) where->size - total);
|
||||
while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
|
||||
@@ -121,7 +409,7 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io
|
||||
if (n < 0)
|
||||
log_error_once("%s: %s failed after %" PRIu64 " of %" PRIu64
|
||||
" at %" PRIu64 ": %s", dev_name(where->dev),
|
||||
should_write ? "write" : "read",
|
||||
devbuf->write ? "write" : "read",
|
||||
(uint64_t) total,
|
||||
(uint64_t) where->size,
|
||||
(uint64_t) where->start, strerror(errno));
|
||||
@@ -136,6 +424,42 @@ static int _io(struct device_area *where, char *buffer, int should_write, dev_io
|
||||
return (total == (size_t) where->size);
|
||||
}
|
||||
|
||||
static int _io(struct device_buffer *devbuf, unsigned ioflags)
|
||||
{
|
||||
struct device_area *where = &devbuf->where;
|
||||
int fd = dev_fd(where->dev);
|
||||
int async = (!devbuf->write && _aio_ctx && aio_supported_code_path(ioflags) && devbuf->dev_read_callback_fn) ? 1 : 0;
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("Attempt to read an unopened device (%s).",
|
||||
dev_name(where->dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!devbuf->buf && !(devbuf->malloc_address = devbuf->buf = dm_malloc_aligned((size_t) devbuf->where.size, 0))) {
|
||||
log_error("I/O buffer malloc failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug_io("%s %s(fd %d):%8" PRIu64 " bytes (%ssync) at %" PRIu64 "%s (for %s)",
|
||||
devbuf->write ? "Write" : "Read ", dev_name(where->dev), fd,
|
||||
where->size, async ? "a" : "", (uint64_t) where->start,
|
||||
(devbuf->write && test_mode()) ? " (test mode - suppressed)" : "", _reason_text(devbuf->reason));
|
||||
|
||||
/*
|
||||
* Skip all writes in test mode.
|
||||
*/
|
||||
if (devbuf->write && test_mode())
|
||||
return 1;
|
||||
|
||||
if (where->size > SSIZE_MAX) {
|
||||
log_error("Read size too large: %" PRIu64, where->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return async ? _io_async(devbuf) : _io_sync(devbuf);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
* LVM2 uses O_DIRECT when performing metadata io, which requires
|
||||
* block size aligned accesses. If any io is not aligned we have
|
||||
@@ -197,7 +521,7 @@ int dev_get_block_size(struct device *dev, unsigned int *physical_block_size, un
|
||||
*physical_block_size = (unsigned int) dev->phys_block_size;
|
||||
*block_size = (unsigned int) dev->block_size;
|
||||
out:
|
||||
if (needs_open && !dev_close_immediate(dev))
|
||||
if (needs_open && !dev_close(dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
@@ -225,15 +549,16 @@ static void _widen_region(unsigned int block_size, struct device_area *region,
|
||||
result->size += block_size - delta;
|
||||
}
|
||||
|
||||
static int _aligned_io(struct device_area *where, char *buffer,
|
||||
int should_write, dev_io_reason_t reason)
|
||||
static int _aligned_io(struct device_area *where, char *write_buffer,
|
||||
int should_write, dev_io_reason_t reason,
|
||||
unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *dev_read_callback_context)
|
||||
{
|
||||
char *bounce, *bounce_buf;
|
||||
unsigned int physical_block_size = 0;
|
||||
unsigned int block_size = 0;
|
||||
unsigned buffer_was_widened = 0;
|
||||
uintptr_t mask;
|
||||
struct device_area widened;
|
||||
struct device_buffer *devbuf;
|
||||
int r = 0;
|
||||
|
||||
if (!(where->dev->flags & DEV_REGULAR) &&
|
||||
@@ -242,6 +567,11 @@ static int _aligned_io(struct device_area *where, char *buffer,
|
||||
|
||||
if (!block_size)
|
||||
block_size = lvm_getpagesize();
|
||||
|
||||
/* Apply minimum read size */
|
||||
if (!should_write && block_size < MIN_READ_SIZE)
|
||||
block_size = MIN_READ_SIZE;
|
||||
|
||||
mask = block_size - 1;
|
||||
|
||||
_widen_region(block_size, where, &widened);
|
||||
@@ -251,51 +581,80 @@ static int _aligned_io(struct device_area *where, char *buffer,
|
||||
buffer_was_widened = 1;
|
||||
log_debug_io("Widening request for %" PRIu64 " bytes at %" PRIu64 " to %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)",
|
||||
where->size, (uint64_t) where->start, widened.size, (uint64_t) widened.start, dev_name(where->dev), _reason_text(reason));
|
||||
} else if (!((uintptr_t) buffer & mask))
|
||||
/* Perform the I/O directly. */
|
||||
return _io(where, buffer, should_write, reason);
|
||||
}
|
||||
|
||||
/* Allocate a bounce buffer with an extra block */
|
||||
if (!(bounce_buf = bounce = dm_malloc((size_t) widened.size + block_size))) {
|
||||
log_error("Bounce buffer malloc failed");
|
||||
return 0;
|
||||
devbuf = DEV_DEVBUF(where->dev, reason);
|
||||
_release_devbuf(devbuf);
|
||||
devbuf->where.dev = where->dev;
|
||||
devbuf->where.start = widened.start;
|
||||
devbuf->where.size = widened.size;
|
||||
devbuf->write = should_write;
|
||||
devbuf->reason = reason;
|
||||
devbuf->dev_read_callback_fn = dev_read_callback_fn;
|
||||
devbuf->dev_read_callback_context = dev_read_callback_context;
|
||||
|
||||
/* Store location of requested data relative to start of buf */
|
||||
devbuf->data_offset = where->start - devbuf->where.start;
|
||||
|
||||
if (should_write && !buffer_was_widened && !((uintptr_t) write_buffer & mask))
|
||||
/* Perform the I/O directly. */
|
||||
devbuf->buf = write_buffer;
|
||||
else if (!should_write)
|
||||
/* Postpone buffer allocation until we're about to issue the I/O */
|
||||
devbuf->buf = NULL;
|
||||
else {
|
||||
/* Allocate a bounce buffer with an extra block */
|
||||
if (!(devbuf->malloc_address = devbuf->buf = dm_malloc((size_t) devbuf->where.size + block_size))) {
|
||||
log_error("Bounce buffer malloc failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Realign start of bounce buffer (using the extra sector)
|
||||
*/
|
||||
if (((uintptr_t) devbuf->buf) & mask)
|
||||
devbuf->buf = (char *) ((((uintptr_t) devbuf->buf) + mask) & ~mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Realign start of bounce buffer (using the extra sector)
|
||||
*/
|
||||
if (((uintptr_t) bounce) & mask)
|
||||
bounce = (char *) ((((uintptr_t) bounce) + mask) & ~mask);
|
||||
/* If we've reached our concurrent AIO limit, add this request to the queue */
|
||||
if (!devbuf->write && _aio_ctx && aio_supported_code_path(ioflags) && dev_read_callback_fn && _aio_must_queue) {
|
||||
_queue_aio(devbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
devbuf->write = 0;
|
||||
|
||||
/* Do we need to read into the bounce buffer? */
|
||||
if ((!should_write || buffer_was_widened) &&
|
||||
!_io(&widened, bounce, 0, reason)) {
|
||||
if ((!should_write || buffer_was_widened) && !_io(devbuf, ioflags)) {
|
||||
if (!should_write)
|
||||
goto_out;
|
||||
goto_bad;
|
||||
/* FIXME Handle errors properly! */
|
||||
/* FIXME pre-extend the file */
|
||||
memset(bounce, '\n', widened.size);
|
||||
memset(devbuf->buf, '\n', devbuf->where.size);
|
||||
}
|
||||
|
||||
if (should_write) {
|
||||
memcpy(bounce + (where->start - widened.start), buffer,
|
||||
(size_t) where->size);
|
||||
if (!should_write)
|
||||
return 1;
|
||||
|
||||
/* ... then we write */
|
||||
if (!(r = _io(&widened, bounce, 1, reason)))
|
||||
stack;
|
||||
|
||||
goto out;
|
||||
/* writes */
|
||||
|
||||
if (devbuf->malloc_address) {
|
||||
memcpy((char *) devbuf->buf + devbuf->data_offset, write_buffer, (size_t) where->size);
|
||||
log_debug_io("Overwriting %" PRIu64 " bytes at %" PRIu64 " (for %s)", where->size,
|
||||
(uint64_t) where->start, _reason_text(devbuf->reason));
|
||||
}
|
||||
|
||||
memcpy(buffer, bounce + (where->start - widened.start),
|
||||
(size_t) where->size);
|
||||
/* ... then we write */
|
||||
devbuf->write = 1;
|
||||
if (!(r = _io(devbuf, 0)))
|
||||
goto_bad;
|
||||
|
||||
r = 1;
|
||||
_release_devbuf(devbuf);
|
||||
return 1;
|
||||
|
||||
out:
|
||||
dm_free(bounce_buf);
|
||||
return r;
|
||||
bad:
|
||||
_release_devbuf(devbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dev_get_size_file(struct device *dev, uint64_t *size)
|
||||
@@ -328,8 +687,6 @@ static int _dev_get_size_file(struct device *dev, uint64_t *size)
|
||||
static int _dev_get_size_dev(struct device *dev, uint64_t *size)
|
||||
{
|
||||
const char *name = dev_name(dev);
|
||||
int fd = dev->bcache_fd;
|
||||
int do_close = 0;
|
||||
|
||||
if (dev->size_seqno == _dev_size_seqno) {
|
||||
log_very_verbose("%s: using cached size %" PRIu64 " sectors",
|
||||
@@ -338,16 +695,12 @@ static int _dev_get_size_dev(struct device *dev, uint64_t *size)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fd <= 0) {
|
||||
if (!dev_open_readonly(dev))
|
||||
return_0;
|
||||
fd = dev_fd(dev);
|
||||
do_close = 1;
|
||||
}
|
||||
if (!dev_open_readonly(dev))
|
||||
return_0;
|
||||
|
||||
if (ioctl(fd, BLKGETSIZE64, size) < 0) {
|
||||
if (ioctl(dev_fd(dev), BLKGETSIZE64, size) < 0) {
|
||||
log_sys_error("ioctl BLKGETSIZE64", name);
|
||||
if (do_close && !dev_close_immediate(dev))
|
||||
if (!dev_close(dev))
|
||||
log_sys_error("close", name);
|
||||
return 0;
|
||||
}
|
||||
@@ -356,11 +709,11 @@ static int _dev_get_size_dev(struct device *dev, uint64_t *size)
|
||||
dev->size = *size;
|
||||
dev->size_seqno = _dev_size_seqno;
|
||||
|
||||
log_very_verbose("%s: size is %" PRIu64 " sectors", name, *size);
|
||||
|
||||
if (do_close && !dev_close_immediate(dev))
|
||||
if (!dev_close(dev))
|
||||
log_sys_error("close", name);
|
||||
|
||||
log_very_verbose("%s: size is %" PRIu64 " sectors", name, *size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -378,7 +731,7 @@ static int _dev_read_ahead_dev(struct device *dev, uint32_t *read_ahead)
|
||||
|
||||
if (ioctl(dev->fd, BLKRAGET, &read_ahead_long) < 0) {
|
||||
log_sys_error("ioctl BLKRAGET", dev_name(dev));
|
||||
if (!dev_close_immediate(dev))
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
@@ -389,7 +742,7 @@ static int _dev_read_ahead_dev(struct device *dev, uint32_t *read_ahead)
|
||||
log_very_verbose("%s: read_ahead is %u sectors",
|
||||
dev_name(dev), *read_ahead);
|
||||
|
||||
if (!dev_close_immediate(dev))
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return 1;
|
||||
@@ -410,13 +763,13 @@ static int _dev_discard_blocks(struct device *dev, uint64_t offset_bytes, uint64
|
||||
if (ioctl(dev->fd, BLKDISCARD, &discard_range) < 0) {
|
||||
log_error("%s: BLKDISCARD ioctl at offset %" PRIu64 " size %" PRIu64 " failed: %s.",
|
||||
dev_name(dev), offset_bytes, size_bytes, strerror(errno));
|
||||
if (!dev_close_immediate(dev))
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
/* It doesn't matter if discard failed, so return success. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!dev_close_immediate(dev))
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return 1;
|
||||
@@ -595,6 +948,8 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
if ((flags & O_CREAT) && !(flags & O_TRUNC))
|
||||
dev->end = lseek(dev->fd, (off_t) 0, SEEK_END);
|
||||
|
||||
dm_list_add(&_open_devices, &dev->open_list);
|
||||
|
||||
log_debug_devs("Opened %s %s%s%s", dev_name(dev),
|
||||
dev->flags & DEV_OPENED_RW ? "RW" : "RO",
|
||||
dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
|
||||
@@ -631,12 +986,17 @@ int dev_open_readonly_quiet(struct device *dev)
|
||||
|
||||
int dev_test_excl(struct device *dev)
|
||||
{
|
||||
int flags = 0;
|
||||
int flags;
|
||||
int r;
|
||||
|
||||
flags = vg_write_lock_held() ? O_RDWR : O_RDONLY;
|
||||
flags |= O_EXCL;
|
||||
flags |= O_RDWR;
|
||||
|
||||
return dev_open_flags(dev, flags, 1, 1);
|
||||
r = dev_open_flags(dev, flags, 1, 1);
|
||||
if (r)
|
||||
dev_close_immediate(dev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void _close(struct device *dev)
|
||||
@@ -646,6 +1006,8 @@ static void _close(struct device *dev)
|
||||
dev->fd = -1;
|
||||
dev->phys_block_size = -1;
|
||||
dev->block_size = -1;
|
||||
dm_list_del(&dev->open_list);
|
||||
devbufs_release(dev);
|
||||
|
||||
log_debug_devs("Closed %s", dev_name(dev));
|
||||
|
||||
@@ -655,6 +1017,7 @@ static void _close(struct device *dev)
|
||||
|
||||
static int _dev_close(struct device *dev, int immediate)
|
||||
{
|
||||
|
||||
if (dev->fd < 0) {
|
||||
log_error("Attempt to close device '%s' "
|
||||
"which is not open.", dev_name(dev));
|
||||
@@ -673,7 +1036,9 @@ static int _dev_close(struct device *dev, int immediate)
|
||||
log_debug_devs("%s: Immediate close attempt while still referenced",
|
||||
dev_name(dev));
|
||||
|
||||
if (immediate || (dev->open_count < 1))
|
||||
/* Close unless device is known to belong to a locked VG */
|
||||
if (immediate ||
|
||||
(dev->open_count < 1 && !lvmcache_pvid_is_locked(dev->pvid)))
|
||||
_close(dev);
|
||||
|
||||
return 1;
|
||||
@@ -689,6 +1054,18 @@ int dev_close_immediate(struct device *dev)
|
||||
return _dev_close(dev, 1);
|
||||
}
|
||||
|
||||
void dev_close_all(void)
|
||||
{
|
||||
struct dm_list *doh, *doht;
|
||||
struct device *dev;
|
||||
|
||||
dm_list_iterate_safe(doh, doht, &_open_devices) {
|
||||
dev = dm_list_struct_base(doh, struct device, open_list);
|
||||
if (dev->open_count < 1)
|
||||
_close(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int _dev_is_valid(struct device *dev)
|
||||
{
|
||||
return (dev->max_error_count == NO_DEV_ERROR_COUNT_LIMIT ||
|
||||
@@ -703,57 +1080,125 @@ static void _dev_inc_error_count(struct device *dev)
|
||||
dev->max_error_count, dev_name(dev));
|
||||
}
|
||||
|
||||
int dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer)
|
||||
/*
|
||||
* Data is returned (read-only) at DEV_DEVBUF_DATA(dev, reason).
|
||||
* If dev_read_callback_fn is supplied, we always return 1 and take
|
||||
* responsibility for calling it exactly once. This might happen before the
|
||||
* function returns (if there's an error or the I/O is synchronous) or after.
|
||||
* Any error is passed to that function, which must track it if required.
|
||||
*/
|
||||
static int _dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
|
||||
unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context)
|
||||
{
|
||||
struct device_area where;
|
||||
int ret;
|
||||
struct device_buffer *devbuf;
|
||||
uint64_t buf_end;
|
||||
int cached = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev->open_count)
|
||||
return_0;
|
||||
if (!dev->open_count) {
|
||||
log_error(INTERNAL_ERROR "Attempt to access device %s while closed.", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!_dev_is_valid(dev))
|
||||
return 0;
|
||||
goto_out;
|
||||
|
||||
/*
|
||||
* Can we satisfy this from data we stored last time we read?
|
||||
*/
|
||||
if ((devbuf = DEV_DEVBUF(dev, reason)) && devbuf->malloc_address) {
|
||||
buf_end = devbuf->where.start + devbuf->where.size - 1;
|
||||
if (offset >= devbuf->where.start && offset <= buf_end && offset + len - 1 <= buf_end) {
|
||||
/* Reuse this buffer */
|
||||
cached = 1;
|
||||
devbuf->data_offset = offset - devbuf->where.start;
|
||||
log_debug_io("Cached read for %" PRIu64 " bytes at %" PRIu64 " on %s (for %s)",
|
||||
(uint64_t) len, (uint64_t) offset, dev_name(dev), _reason_text(reason));
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
where.dev = dev;
|
||||
where.start = offset;
|
||||
where.size = len;
|
||||
|
||||
ret = _aligned_io(&where, buffer, 0, reason);
|
||||
if (!ret)
|
||||
ret = _aligned_io(&where, NULL, 0, reason, ioflags, dev_read_callback_fn, callback_context);
|
||||
if (!ret) {
|
||||
log_error("Read from %s failed", dev_name(dev));
|
||||
_dev_inc_error_count(dev);
|
||||
}
|
||||
|
||||
out:
|
||||
/* If we had an error or this was sync I/O, pass the result to any callback fn */
|
||||
if ((!ret || !_aio_ctx || !aio_supported_code_path(ioflags) || cached) && dev_read_callback_fn) {
|
||||
dev_read_callback_fn(!ret, ioflags, callback_context, DEV_DEVBUF_DATA(dev, reason));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from 'dev' into 'buf', possibly in 2 distinct regions, denoted
|
||||
* by (offset,len) and (offset2,len2). Thus, the total size of
|
||||
* 'buf' should be len+len2.
|
||||
*/
|
||||
int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
|
||||
uint64_t offset2, size_t len2, dev_io_reason_t reason, char *buf)
|
||||
void dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
|
||||
unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context)
|
||||
{
|
||||
if (!dev_read(dev, offset, len, reason, buf)) {
|
||||
/* Always returns 1 if callback fn is supplied */
|
||||
if (!_dev_read_callback(dev, offset, len, reason, ioflags, dev_read_callback_fn, callback_context))
|
||||
log_error(INTERNAL_ERROR "_dev_read_callback failed");
|
||||
}
|
||||
|
||||
/* Returns pointer to read-only buffer. Caller does not free it. */
|
||||
const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason)
|
||||
{
|
||||
if (!_dev_read_callback(dev, offset, len, reason, 0, NULL, NULL))
|
||||
return_NULL;
|
||||
|
||||
return DEV_DEVBUF_DATA(dev, reason);
|
||||
}
|
||||
|
||||
/* Read into supplied retbuf owned by the caller. */
|
||||
int dev_read_buf(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *retbuf)
|
||||
{
|
||||
if (!_dev_read_callback(dev, offset, len, reason, 0, NULL, NULL)) {
|
||||
log_error("Read from %s failed", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The second region is optional, and allows for
|
||||
* a circular buffer on the device.
|
||||
*/
|
||||
if (!len2)
|
||||
return 1;
|
||||
|
||||
if (!dev_read(dev, offset2, len2, reason, buf + len)) {
|
||||
log_error("Circular read from %s failed",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(retbuf, DEV_DEVBUF_DATA(dev, reason), len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from 'dev' in 2 distinct regions, denoted by (offset,len) and (offset2,len2).
|
||||
* Caller is responsible for dm_free().
|
||||
*/
|
||||
const char *dev_read_circular(struct device *dev, uint64_t offset, size_t len,
|
||||
uint64_t offset2, size_t len2, dev_io_reason_t reason)
|
||||
{
|
||||
char *buf = NULL;
|
||||
|
||||
if (!(buf = dm_malloc(len + len2))) {
|
||||
log_error("Buffer allocation failed for split metadata.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dev_read_buf(dev, offset, len, reason, buf)) {
|
||||
log_error("Read from %s failed", dev_name(dev));
|
||||
dm_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!dev_read_buf(dev, offset2, len2, reason, buf + len)) {
|
||||
log_error("Circular read from %s failed", dev_name(dev));
|
||||
dm_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* FIXME If O_DIRECT can't extend file, dev_extend first; dev_truncate after.
|
||||
* But fails if concurrent processes writing
|
||||
*/
|
||||
@@ -797,7 +1242,7 @@ int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t r
|
||||
|
||||
dev->flags |= DEV_ACCESSED_W;
|
||||
|
||||
ret = _aligned_io(&where, buffer, 1, reason);
|
||||
ret = _aligned_io(&where, buffer, 1, reason, 0, NULL, NULL);
|
||||
if (!ret)
|
||||
_dev_inc_error_count(dev);
|
||||
|
||||
@@ -807,7 +1252,7 @@ int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t r
|
||||
int dev_set(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, int value)
|
||||
{
|
||||
size_t s;
|
||||
char buffer[4096] __attribute__((aligned(8)));
|
||||
char buffer[4096] __attribute__((aligned(4096)));
|
||||
|
||||
if (!dev_open(dev))
|
||||
return_0;
|
||||
|
@@ -18,22 +18,27 @@
|
||||
#define LUKS_SIGNATURE "LUKS\xba\xbe"
|
||||
#define LUKS_SIGNATURE_SIZE 6
|
||||
|
||||
int dev_is_luks(struct device *dev, uint64_t *offset_found, int full)
|
||||
int dev_is_luks(struct device *dev, uint64_t *offset_found)
|
||||
{
|
||||
char buf[LUKS_SIGNATURE_SIZE];
|
||||
int ret = -1;
|
||||
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (offset_found)
|
||||
*offset_found = 0;
|
||||
|
||||
if (!dev_read_bytes(dev, 0, LUKS_SIGNATURE_SIZE, buf))
|
||||
if (!dev_read_buf(dev, 0, LUKS_SIGNATURE_SIZE, DEV_IO_SIGNATURES, buf))
|
||||
goto_out;
|
||||
|
||||
ret = memcmp(buf, LUKS_SIGNATURE, LUKS_SIGNATURE_SIZE) ? 0 : 1;
|
||||
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "dev-type.h"
|
||||
#include "xlate.h"
|
||||
|
||||
/*
|
||||
* These lvm1 structs just used NAME_LEN in the previous format1 lvm2 code, but
|
||||
* NAME_LEN was defined as 128 in generic lvm2 code that was not lvm1-specific
|
||||
* and not disk-format-specific.
|
||||
*/
|
||||
|
||||
#define LVM1_NAME_LEN 128
|
||||
|
||||
struct data_area {
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct pv_disk {
|
||||
int8_t id[2];
|
||||
uint16_t version; /* lvm version */
|
||||
struct data_area pv_on_disk;
|
||||
struct data_area vg_on_disk;
|
||||
struct data_area pv_uuidlist_on_disk;
|
||||
struct data_area lv_on_disk;
|
||||
struct data_area pe_on_disk;
|
||||
int8_t pv_uuid[LVM1_NAME_LEN];
|
||||
int8_t vg_name[LVM1_NAME_LEN];
|
||||
int8_t system_id[LVM1_NAME_LEN]; /* for vgexport/vgimport */
|
||||
uint32_t pv_major;
|
||||
uint32_t pv_number;
|
||||
uint32_t pv_status;
|
||||
uint32_t pv_allocatable;
|
||||
uint32_t pv_size;
|
||||
uint32_t lv_cur;
|
||||
uint32_t pe_size;
|
||||
uint32_t pe_total;
|
||||
uint32_t pe_allocated;
|
||||
|
||||
/* only present on version == 2 pv's */
|
||||
uint32_t pe_start;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
|
||||
int dev_is_lvm1(struct device *dev, char *buf, int buflen)
|
||||
{
|
||||
struct pv_disk *pvd = (struct pv_disk *) buf;
|
||||
uint32_t version;
|
||||
int ret;
|
||||
|
||||
version = xlate16(pvd->version);
|
||||
|
||||
if (pvd->id[0] == 'H' && pvd->id[1] == 'M' &&
|
||||
(version == 1 || version == 2))
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#define POOL_MAGIC 0x011670
|
||||
#define POOL_NAME_SIZE 256
|
||||
|
||||
#define NSPMajorVersion 4
|
||||
#define NSPMinorVersion 1
|
||||
#define NSPUpdateLevel 3
|
||||
|
||||
/* When checking for version matching, the first two numbers **
|
||||
** are important for metadata formats, a.k.a pool labels. **
|
||||
** All the numbers are important when checking if the user **
|
||||
** space tools match up with the kernel module............. */
|
||||
|
||||
#define POOL_VERSION (NSPMajorVersion << 16 | \
|
||||
NSPMinorVersion << 8 | \
|
||||
NSPUpdateLevel)
|
||||
|
||||
struct pool_disk {
|
||||
uint64_t pl_magic; /* Pool magic number */
|
||||
uint64_t pl_pool_id; /* Unique pool identifier */
|
||||
char pl_pool_name[POOL_NAME_SIZE]; /* Name of pool */
|
||||
uint32_t pl_version; /* Pool version */
|
||||
uint32_t pl_subpools; /* Number of subpools in this pool */
|
||||
uint32_t pl_sp_id; /* Subpool number within pool */
|
||||
uint32_t pl_sp_devs; /* Number of data partitions in this subpool */
|
||||
uint32_t pl_sp_devid; /* Partition number within subpool */
|
||||
uint32_t pl_sp_type; /* Partition type */
|
||||
uint64_t pl_blocks; /* Number of blocks in this partition */
|
||||
uint32_t pl_striping; /* Striping size within subpool */
|
||||
/*
|
||||
* If the number of DMEP devices is zero, then the next field **
|
||||
* ** (pl_sp_dmepid) becomes the subpool ID for redirection. In **
|
||||
* ** other words, if this subpool does not have the capability **
|
||||
* ** to do DMEP, then it must specify which subpool will do it **
|
||||
* ** in it's place
|
||||
*/
|
||||
|
||||
/*
|
||||
* While the next 3 field are no longer used, they must stay to keep **
|
||||
* ** backward compatibility...........................................
|
||||
*/
|
||||
uint32_t pl_sp_dmepdevs;/* Number of dmep devices in this subpool */
|
||||
uint32_t pl_sp_dmepid; /* Dmep device number within subpool */
|
||||
uint32_t pl_sp_weight; /* if dmep dev, pref to using it */
|
||||
|
||||
uint32_t pl_minor; /* the pool minor number */
|
||||
uint32_t pl_padding; /* reminder - think about alignment */
|
||||
|
||||
/*
|
||||
* Even though we're zeroing out 8k at the front of the disk before
|
||||
* writing the label, putting this in
|
||||
*/
|
||||
char pl_reserve[184]; /* bump the structure size out to 512 bytes */
|
||||
};
|
||||
|
||||
#define CPIN_8(x, y, z) {memcpy((x), (y), (z));}
|
||||
#define CPIN_16(x, y) {(x) = xlate16_be((y));}
|
||||
#define CPIN_32(x, y) {(x) = xlate32_be((y));}
|
||||
#define CPIN_64(x, y) {(x) = xlate64_be((y));}
|
||||
|
||||
static void pool_label_in(struct pool_disk *pl, void *buf)
|
||||
{
|
||||
struct pool_disk *bufpl = (struct pool_disk *) buf;
|
||||
|
||||
CPIN_64(pl->pl_magic, bufpl->pl_magic);
|
||||
CPIN_64(pl->pl_pool_id, bufpl->pl_pool_id);
|
||||
CPIN_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE);
|
||||
CPIN_32(pl->pl_version, bufpl->pl_version);
|
||||
CPIN_32(pl->pl_subpools, bufpl->pl_subpools);
|
||||
CPIN_32(pl->pl_sp_id, bufpl->pl_sp_id);
|
||||
CPIN_32(pl->pl_sp_devs, bufpl->pl_sp_devs);
|
||||
CPIN_32(pl->pl_sp_devid, bufpl->pl_sp_devid);
|
||||
CPIN_32(pl->pl_sp_type, bufpl->pl_sp_type);
|
||||
CPIN_64(pl->pl_blocks, bufpl->pl_blocks);
|
||||
CPIN_32(pl->pl_striping, bufpl->pl_striping);
|
||||
CPIN_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs);
|
||||
CPIN_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid);
|
||||
CPIN_32(pl->pl_sp_weight, bufpl->pl_sp_weight);
|
||||
CPIN_32(pl->pl_minor, bufpl->pl_minor);
|
||||
CPIN_32(pl->pl_padding, bufpl->pl_padding);
|
||||
CPIN_8(pl->pl_reserve, bufpl->pl_reserve, 184);
|
||||
}
|
||||
|
||||
int dev_is_pool(struct device *dev, char *buf, int buflen)
|
||||
{
|
||||
struct pool_disk pd;
|
||||
int ret;
|
||||
|
||||
pool_label_in(&pd, buf);
|
||||
|
||||
/* can ignore 8 rightmost bits for ondisk format check */
|
||||
if ((pd.pl_magic == POOL_MAGIC) &&
|
||||
(pd.pl_version >> 8 == POOL_VERSION >> 8))
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -37,12 +37,9 @@ static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
|
||||
uint32_t md_magic;
|
||||
|
||||
/* Version 1 is little endian; version 0.90.0 is machine endian */
|
||||
|
||||
if (!dev_read_bytes(dev, sb_offset, sizeof(uint32_t), &md_magic))
|
||||
return_0;
|
||||
|
||||
if ((md_magic == MD_SB_MAGIC) ||
|
||||
((MD_SB_MAGIC != xlate32(MD_SB_MAGIC)) && (md_magic == xlate32(MD_SB_MAGIC))))
|
||||
if (dev_read_buf(dev, sb_offset, sizeof(uint32_t), DEV_IO_SIGNATURES, &md_magic) &&
|
||||
((md_magic == MD_SB_MAGIC) ||
|
||||
((MD_SB_MAGIC != xlate32(MD_SB_MAGIC)) && (md_magic == xlate32(MD_SB_MAGIC)))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -112,14 +109,11 @@ static int _udev_dev_is_md(struct device *dev)
|
||||
/*
|
||||
* Returns -1 on error
|
||||
*/
|
||||
static int _native_dev_is_md(struct device *dev, uint64_t *offset_found, int full)
|
||||
static int _native_dev_is_md(struct device *dev, uint64_t *offset_found)
|
||||
{
|
||||
int ret = 1;
|
||||
md_minor_version_t minor;
|
||||
uint64_t size, sb_offset;
|
||||
int ret;
|
||||
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
stack;
|
||||
@@ -129,73 +123,38 @@ static int _native_dev_is_md(struct device *dev, uint64_t *offset_found, int ful
|
||||
if (size < MD_RESERVED_SECTORS * 2)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Old md versions locate the magic number at the end of the device.
|
||||
* Those checks can't be satisfied with the initial bcache data, and
|
||||
* would require an extra read i/o at the end of every device. Issuing
|
||||
* an extra read to every device in every command, just to check for
|
||||
* the old md format is a bad tradeoff.
|
||||
*
|
||||
* When "full" is set, we check a the start and end of the device for
|
||||
* md magic numbers. When "full" is not set, we only check at the
|
||||
* start of the device for the magic numbers. We decide for each
|
||||
* command if it should do a full check (cmd->use_full_md_check),
|
||||
* and set it for commands that could possibly write to an md dev
|
||||
* (pvcreate/vgcreate/vgextend).
|
||||
*
|
||||
* For old md versions with magic numbers at the end of devices,
|
||||
* the md dev components won't be filtered out here when full is 0,
|
||||
* so they will be scanned, and appear as duplicate PVs in lvmcache.
|
||||
* The md device itself will be chosen as the primary duplicate,
|
||||
* and the components are dropped from the list of duplicates in,
|
||||
* i.e. a kind of post-scan filtering.
|
||||
*/
|
||||
if (!full) {
|
||||
sb_offset = 0;
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
log_debug_devs("Found md magic number at offset 0 of %s.", dev_name(dev));
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sb_offset = 8 << SECTOR_SHIFT;
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
log_debug_devs("Found md magic number at offset %d of %s.", (int)sb_offset, dev_name(dev));
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto out;
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check if it is an md component device. */
|
||||
/* Version 0.90.0 */
|
||||
sb_offset = MD_NEW_SIZE_SECTORS(size) << SECTOR_SHIFT;
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
ret = 1;
|
||||
if (_dev_has_md_magic(dev, sb_offset))
|
||||
goto out;
|
||||
}
|
||||
|
||||
minor = MD_MINOR_VERSION_MIN;
|
||||
/* Version 1, try v1.0 -> v1.2 */
|
||||
do {
|
||||
sb_offset = _v1_sb_offset(size, minor);
|
||||
if (_dev_has_md_magic(dev, sb_offset)) {
|
||||
ret = 1;
|
||||
if (_dev_has_md_magic(dev, sb_offset))
|
||||
goto out;
|
||||
}
|
||||
} while (++minor <= MD_MINOR_VERSION_MAX);
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
if (ret && offset_found)
|
||||
*offset_found = sb_offset;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dev_is_md(struct device *dev, uint64_t *offset_found, int full)
|
||||
int dev_is_md(struct device *dev, uint64_t *offset_found)
|
||||
{
|
||||
|
||||
/*
|
||||
@@ -204,7 +163,7 @@ int dev_is_md(struct device *dev, uint64_t *offset_found, int full)
|
||||
* information is not in udev db.
|
||||
*/
|
||||
if ((dev->ext.src == DEV_EXT_NONE) || offset_found)
|
||||
return _native_dev_is_md(dev, offset_found, full);
|
||||
return _native_dev_is_md(dev, offset_found);
|
||||
|
||||
if (dev->ext.src == DEV_EXT_UDEV)
|
||||
return _udev_dev_is_md(dev);
|
||||
|
@@ -35,21 +35,23 @@ static int _swap_detect_signature(const char *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dev_is_swap(struct device *dev, uint64_t *offset_found, int full)
|
||||
int dev_is_swap(struct device *dev, uint64_t *offset_found)
|
||||
{
|
||||
char buf[10];
|
||||
uint64_t size;
|
||||
unsigned page;
|
||||
int ret = 0;
|
||||
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) {
|
||||
/*
|
||||
* skip 32k pagesize since this does not seem to be supported
|
||||
@@ -58,7 +60,7 @@ int dev_is_swap(struct device *dev, uint64_t *offset_found, int full)
|
||||
continue;
|
||||
if (size < (page >> SECTOR_SHIFT))
|
||||
break;
|
||||
if (!dev_read_bytes(dev, page - SIGNATURE_SIZE, SIGNATURE_SIZE, buf)) {
|
||||
if (!dev_read_buf(dev, page - SIGNATURE_SIZE, SIGNATURE_SIZE, DEV_IO_SIGNATURES, buf)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
@@ -70,6 +72,9 @@ int dev_is_swap(struct device *dev, uint64_t *offset_found, int full)
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -17,8 +17,6 @@
|
||||
#include "xlate.h"
|
||||
#include "config.h"
|
||||
#include "metadata.h"
|
||||
#include "bcache.h"
|
||||
#include "label.h"
|
||||
|
||||
#include <libgen.h>
|
||||
#include <ctype.h>
|
||||
@@ -215,9 +213,6 @@ int dev_subsystem_part_major(struct dev_types *dt, struct device *dev)
|
||||
if (MAJOR(dev->dev) == dt->device_mapper_major)
|
||||
return 1;
|
||||
|
||||
if (MAJOR(dev->dev) == dt->md_major)
|
||||
return 1;
|
||||
|
||||
if (MAJOR(dev->dev) == dt->drbd_major)
|
||||
return 1;
|
||||
|
||||
@@ -368,7 +363,7 @@ static int _has_partition_table(struct device *dev)
|
||||
uint16_t magic;
|
||||
} __attribute__((packed)) buf; /* sizeof() == SECTOR_SIZE */
|
||||
|
||||
if (!dev_read_bytes(dev, UINT64_C(0), sizeof(buf), &buf))
|
||||
if (!dev_read_buf(dev, UINT64_C(0), sizeof(buf), DEV_IO_SIGNATURES, &buf))
|
||||
return_0;
|
||||
|
||||
/* FIXME Check for other types of partition table too */
|
||||
@@ -437,9 +432,6 @@ static int _native_dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!scan_bcache)
|
||||
return -EAGAIN;
|
||||
|
||||
if (!_is_partitionable(dt, dev))
|
||||
return 0;
|
||||
|
||||
@@ -447,8 +439,17 @@ static int _native_dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
if ((MAJOR(dev->dev) == dt->dasd_major) && dasd_is_cdl_formatted(dev))
|
||||
return 1;
|
||||
|
||||
if (!dev_open_readonly_quiet(dev)) {
|
||||
log_debug_devs("%s: failed to open device, considering device "
|
||||
"is partitioned", dev_name(dev));
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = _has_partition_table(dev);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -674,7 +675,7 @@ static int _blkid_wipe(blkid_probe probe, struct device *dev, const char *name,
|
||||
} else
|
||||
log_verbose(_msg_wiping, type, name);
|
||||
|
||||
if (!dev_write_zeros(dev, offset_value, len)) {
|
||||
if (!dev_set(dev, offset_value, len, DEV_IO_SIGNATURES, 0)) {
|
||||
log_error("Failed to wipe %s signature on %s.", type, name);
|
||||
return 0;
|
||||
}
|
||||
@@ -747,12 +748,12 @@ out:
|
||||
|
||||
static int _wipe_signature(struct device *dev, const char *type, const char *name,
|
||||
int wipe_len, int yes, force_t force, int *wiped,
|
||||
int (*signature_detection_fn)(struct device *dev, uint64_t *offset_found, int full))
|
||||
int (*signature_detection_fn)(struct device *dev, uint64_t *offset_found))
|
||||
{
|
||||
int wipe;
|
||||
uint64_t offset_found;
|
||||
|
||||
wipe = signature_detection_fn(dev, &offset_found, 1);
|
||||
wipe = signature_detection_fn(dev, &offset_found);
|
||||
if (wipe == -1) {
|
||||
log_error("Fatal error while trying to detect %s on %s.",
|
||||
type, name);
|
||||
@@ -771,7 +772,7 @@ static int _wipe_signature(struct device *dev, const char *type, const char *nam
|
||||
}
|
||||
|
||||
log_print_unless_silent("Wiping %s on %s.", type, name);
|
||||
if (!dev_write_zeros(dev, offset_found, wipe_len)) {
|
||||
if (!dev_set(dev, offset_found, wipe_len, DEV_IO_SIGNATURES, 0)) {
|
||||
log_error("Failed to wipe %s on %s.", type, name);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "device.h"
|
||||
#include "display.h"
|
||||
#include "label.h"
|
||||
|
||||
#define NUMBER_OF_MAJORS 4096
|
||||
|
||||
@@ -57,15 +56,12 @@ const char *dev_subsystem_name(struct dev_types *dt, struct device *dev);
|
||||
int major_is_scsi_device(struct dev_types *dt, int major);
|
||||
|
||||
/* Signature/superblock recognition with position returned where found. */
|
||||
int dev_is_md(struct device *dev, uint64_t *sb, int full);
|
||||
int dev_is_swap(struct device *dev, uint64_t *signature, int full);
|
||||
int dev_is_luks(struct device *dev, uint64_t *signature, int full);
|
||||
int dev_is_md(struct device *dev, uint64_t *sb);
|
||||
int dev_is_swap(struct device *dev, uint64_t *signature);
|
||||
int dev_is_luks(struct device *dev, uint64_t *signature);
|
||||
int dasd_is_cdl_formatted(struct device *dev);
|
||||
int udev_dev_is_mpath_component(struct device *dev);
|
||||
|
||||
int dev_is_lvm1(struct device *dev, char *buf, int buflen);
|
||||
int dev_is_pool(struct device *dev, char *buf, int buflen);
|
||||
|
||||
/* Signature wiping. */
|
||||
#define TYPE_LVM1_MEMBER 0x001
|
||||
#define TYPE_LVM2_MEMBER 0x002
|
||||
|
@@ -31,10 +31,18 @@
|
||||
#define DEV_USED_FOR_LV 0x00000100 /* Is device used for an LV */
|
||||
#define DEV_ASSUMED_FOR_LV 0x00000200 /* Is device assumed for an LV */
|
||||
#define DEV_NOT_O_NOATIME 0x00000400 /* Don't use O_NOATIME */
|
||||
#define DEV_IN_BCACHE 0x00000800 /* dev fd is open and used in bcache */
|
||||
#define DEV_BCACHE_EXCL 0x00001000 /* bcache_fd should be open EXCL */
|
||||
#define DEV_FILTER_AFTER_SCAN 0x00002000 /* apply filter after bcache has data */
|
||||
#define DEV_FILTER_OUT_SCAN 0x00004000 /* filtered out during label scan */
|
||||
|
||||
/* ioflags */
|
||||
#define AIO_SUPPORTED_CODE_PATH 0x00000001 /* Set if the code path supports AIO */
|
||||
|
||||
#define aio_supported_code_path(ioflags) (((ioflags) & AIO_SUPPORTED_CODE_PATH) ? 1 : 0)
|
||||
|
||||
/*
|
||||
* Standard format for callback functions.
|
||||
* When provided, callback functions are called exactly once.
|
||||
* If failed is set, data cannot be accessed.
|
||||
*/
|
||||
typedef void (*lvm_callback_fn_t)(int failed, unsigned ioflags, void *context, const void *data);
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
@@ -53,36 +61,6 @@ struct dev_ext {
|
||||
void *handle;
|
||||
};
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
struct dm_list aliases; /* struct dm_str_list */
|
||||
dev_t dev;
|
||||
|
||||
/* private */
|
||||
int fd;
|
||||
int open_count;
|
||||
int error_count;
|
||||
int max_error_count;
|
||||
int phys_block_size;
|
||||
int block_size;
|
||||
int read_ahead;
|
||||
int bcache_fd;
|
||||
uint32_t flags;
|
||||
unsigned size_seqno;
|
||||
uint64_t size;
|
||||
uint64_t end;
|
||||
struct dev_ext ext;
|
||||
|
||||
const char *vgid; /* if device is an LV */
|
||||
const char *lvid; /* if device is an LV */
|
||||
|
||||
char pvid[ID_LEN + 1]; /* if device is a PV */
|
||||
char _padding[7];
|
||||
};
|
||||
|
||||
/*
|
||||
* All I/O is annotated with the reason it is performed.
|
||||
*/
|
||||
@@ -99,10 +77,12 @@ typedef enum dev_io_reason {
|
||||
DEV_IO_LOG /* Logging messages */
|
||||
} dev_io_reason_t;
|
||||
|
||||
struct device_list {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
};
|
||||
/*
|
||||
* Is this I/O for a device's extra metadata area?
|
||||
*/
|
||||
#define EXTRA_IO(reason) ((reason) == DEV_IO_MDA_EXTRA_HEADER || (reason) == DEV_IO_MDA_EXTRA_CONTENT)
|
||||
#define DEV_DEVBUF(dev, reason) (EXTRA_IO((reason)) ? &(dev)->last_extra_devbuf : &(dev)->last_devbuf)
|
||||
#define DEV_DEVBUF_DATA(dev, reason) ((char *) DEV_DEVBUF((dev), (reason))->buf + DEV_DEVBUF((dev), (reason))->data_offset)
|
||||
|
||||
struct device_area {
|
||||
struct device *dev;
|
||||
@@ -110,6 +90,56 @@ struct device_area {
|
||||
uint64_t size; /* Bytes */
|
||||
};
|
||||
|
||||
struct device_buffer {
|
||||
uint64_t data_offset; /* Offset to start of requested data within buf */
|
||||
void *malloc_address; /* Start of allocated memory */
|
||||
void *buf; /* Aligned buffer that contains data within it */
|
||||
struct device_area where; /* Location of buf */
|
||||
dev_io_reason_t reason;
|
||||
unsigned write:1; /* 1 if write; 0 if read */
|
||||
|
||||
lvm_callback_fn_t dev_read_callback_fn;
|
||||
void *dev_read_callback_context;
|
||||
struct dm_list aio_queued; /* Queue of async I/O waiting to be issued */
|
||||
};
|
||||
|
||||
/*
|
||||
* All devices in LVM will be represented by one of these.
|
||||
* pointer comparisons are valid.
|
||||
*/
|
||||
struct device {
|
||||
struct dm_list aliases; /* struct dm_str_list */
|
||||
dev_t dev;
|
||||
|
||||
/* private */
|
||||
int fd;
|
||||
int open_count;
|
||||
int error_count;
|
||||
int max_error_count;
|
||||
int phys_block_size;
|
||||
int block_size;
|
||||
int read_ahead;
|
||||
uint32_t flags;
|
||||
unsigned size_seqno;
|
||||
uint64_t size;
|
||||
uint64_t end;
|
||||
struct dm_list open_list;
|
||||
struct dev_ext ext;
|
||||
struct device_buffer last_devbuf; /* Last data buffer read from the device */
|
||||
struct device_buffer last_extra_devbuf; /* Last data buffer read from the device for extra metadata area */
|
||||
|
||||
const char *vgid; /* if device is an LV */
|
||||
const char *lvid; /* if device is an LV */
|
||||
|
||||
char pvid[ID_LEN + 1]; /* if device is a PV */
|
||||
char _padding[7];
|
||||
};
|
||||
|
||||
struct device_list {
|
||||
struct dm_list list;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
*/
|
||||
@@ -143,14 +173,25 @@ int dev_open_readonly_buffered(struct device *dev);
|
||||
int dev_open_readonly_quiet(struct device *dev);
|
||||
int dev_close(struct device *dev);
|
||||
int dev_close_immediate(struct device *dev);
|
||||
void dev_close_all(void);
|
||||
int dev_test_excl(struct device *dev);
|
||||
|
||||
int dev_fd(struct device *dev);
|
||||
const char *dev_name(const struct device *dev);
|
||||
|
||||
int dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer);
|
||||
int dev_read_circular(struct device *dev, uint64_t offset, size_t len,
|
||||
uint64_t offset2, size_t len2, dev_io_reason_t reason, char *buf);
|
||||
/* Returns a read-only buffer */
|
||||
const char *dev_read(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason);
|
||||
const char *dev_read_circular(struct device *dev, uint64_t offset, size_t len,
|
||||
uint64_t offset2, size_t len2, dev_io_reason_t reason);
|
||||
|
||||
/* Passes the data (or error) to dev_read_callback_fn */
|
||||
void dev_read_callback(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason,
|
||||
unsigned ioflags, lvm_callback_fn_t dev_read_callback_fn, void *callback_context);
|
||||
|
||||
/* Read data and copy it into a supplied private buffer. */
|
||||
/* Only use for tiny reads or on unimportant code paths. */
|
||||
int dev_read_buf(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *retbuf);
|
||||
|
||||
int dev_write(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, void *buffer);
|
||||
int dev_append(struct device *dev, size_t len, dev_io_reason_t reason, char *buffer);
|
||||
int dev_set(struct device *dev, uint64_t offset, size_t len, dev_io_reason_t reason, int value);
|
||||
@@ -160,7 +201,15 @@ struct device *dev_create_file(const char *filename, struct device *dev,
|
||||
struct dm_str_list *alias, int use_malloc);
|
||||
void dev_destroy_file(struct device *dev);
|
||||
|
||||
void devbufs_release(struct device *dev);
|
||||
|
||||
/* Return a valid device name from the alias list; NULL otherwise */
|
||||
const char *dev_name_confirmed(struct device *dev, int quiet);
|
||||
|
||||
struct cmd_context;
|
||||
int dev_async_getevents(void);
|
||||
int dev_async_setup(struct cmd_context *cmd);
|
||||
void dev_async_exit(void);
|
||||
int dev_async_reset(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
|
@@ -15,19 +15,14 @@
|
||||
|
||||
#include "lib.h"
|
||||
#include "filter.h"
|
||||
#include "device.h"
|
||||
|
||||
static int _and_p(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct dev_filter **filters;
|
||||
int ret;
|
||||
|
||||
for (filters = (struct dev_filter **) f->private; *filters; ++filters) {
|
||||
ret = (*filters)->passes_filter(*filters, dev);
|
||||
|
||||
if (!ret)
|
||||
for (filters = (struct dev_filter **) f->private; *filters; ++filters)
|
||||
if (!(*filters)->passes_filter(*filters, dev))
|
||||
return 0; /* No 'stack': a filter, not an error. */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -57,13 +52,13 @@ static void _composite_destroy(struct dev_filter *f)
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
static int _dump(struct dev_filter *f, int merge_existing)
|
||||
static int _dump(struct dev_filter *f, struct dm_pool *mem, int merge_existing)
|
||||
{
|
||||
struct dev_filter **filters;
|
||||
|
||||
for (filters = (struct dev_filter **) f->private; *filters; ++filters)
|
||||
if ((*filters)->dump &&
|
||||
!(*filters)->dump(*filters, merge_existing))
|
||||
!(*filters)->dump(*filters, mem, merge_existing))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
|
@@ -20,21 +20,15 @@
|
||||
|
||||
#define MSG_SKIPPING "%s: Skipping md component device"
|
||||
|
||||
static int _ignore_md(struct device *dev, int full)
|
||||
static int _ignore_md(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!md_filtering())
|
||||
return 1;
|
||||
|
||||
ret = dev_is_md(dev, NULL, full);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
/* let pass, call again after scan */
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
log_debug_devs("filter md deferred %s", dev_name(dev));
|
||||
return 1;
|
||||
}
|
||||
ret = dev_is_md(dev, NULL);
|
||||
|
||||
if (ret == 1) {
|
||||
if (dev->ext.src == DEV_EXT_NONE)
|
||||
@@ -54,18 +48,6 @@ static int _ignore_md(struct device *dev, int full)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _ignore_md_lite(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
{
|
||||
return _ignore_md(dev, 0);
|
||||
}
|
||||
|
||||
static int _ignore_md_full(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
{
|
||||
return _ignore_md(dev, 1);
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
if (f->use_count)
|
||||
@@ -74,7 +56,7 @@ static void _destroy(struct dev_filter *f)
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *md_filter_create(struct cmd_context *cmd, struct dev_types *dt)
|
||||
struct dev_filter *md_filter_create(struct dev_types *dt)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
@@ -83,18 +65,7 @@ struct dev_filter *md_filter_create(struct cmd_context *cmd, struct dev_types *d
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: for commands that want a full md check (pvcreate, vgcreate,
|
||||
* vgextend), we do an extra read at the end of every device that the
|
||||
* filter looks at. This isn't necessary; we only need to do the full
|
||||
* md check on the PVs that these commands are trying to use.
|
||||
*/
|
||||
|
||||
if (cmd->use_full_md_check)
|
||||
f->passes_filter = _ignore_md_full;
|
||||
else
|
||||
f->passes_filter = _ignore_md_lite;
|
||||
|
||||
f->passes_filter = _ignore_md;
|
||||
f->destroy = _destroy;
|
||||
f->use_count = 0;
|
||||
f->private = dt;
|
||||
|
@@ -21,18 +21,8 @@
|
||||
static int _passes_partitioned_filter(struct dev_filter *f, struct device *dev)
|
||||
{
|
||||
struct dev_types *dt = (struct dev_types *) f->private;
|
||||
int ret;
|
||||
|
||||
ret = dev_is_partitioned(dt, dev);
|
||||
|
||||
if (ret == -EAGAIN) {
|
||||
/* let pass, call again after scan */
|
||||
log_debug_devs("filter partitioned deferred %s", dev_name(dev));
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
if (dev_is_partitioned(dt, dev)) {
|
||||
if (dev->ext.src == DEV_EXT_NONE)
|
||||
log_debug_devs(MSG_SKIPPING, dev_name(dev));
|
||||
else
|
||||
|
@@ -26,30 +26,6 @@ struct pfilter {
|
||||
struct dev_types *dt;
|
||||
};
|
||||
|
||||
/*
|
||||
* The persistent filter is filter layer that sits above the other filters and
|
||||
* caches the final result of those other filters. When a device is first
|
||||
* checked against filters, it will not be in this cache, so this filter will
|
||||
* pass the device down to the other filters to check it. The other filters
|
||||
* will run and either include the device (good/pass) or exclude the device
|
||||
* (bad/fail). That good or bad result propagates up through this filter which
|
||||
* saves the result. The next time some code checks the filters against the
|
||||
* device, this persistent/cache filter is checked first. This filter finds
|
||||
* the previous result in its cache and returns it without reevaluating the
|
||||
* other real filters.
|
||||
*
|
||||
* FIXME: a cache like this should not be needed. The fact it's needed is a
|
||||
* symptom of code that should be fixed to not reevaluate filters multiple
|
||||
* times. A device should be checked against the filter once, and then not
|
||||
* need to be checked again. With scanning now controlled, we could probably
|
||||
* do this.
|
||||
*
|
||||
* FIXME: "persistent" isn't a great name for this caching filter. This filter
|
||||
* at one time saved its cache results to a file, which is how it got the name.
|
||||
* That .cache file does not work well, causes problems, and is no longer used
|
||||
* by default. The old code for it should be removed.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The hash table holds one of these two states
|
||||
* against each entry.
|
||||
@@ -72,7 +48,11 @@ static void _persistent_filter_wipe(struct dev_filter *f)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
|
||||
log_verbose("Wiping cache of LVM-capable devices");
|
||||
dm_hash_wipe(pf->devices);
|
||||
|
||||
/* Trigger complete device scan */
|
||||
dev_cache_scan(1);
|
||||
}
|
||||
|
||||
static int _read_array(struct pfilter *pf, struct dm_config_tree *cft,
|
||||
@@ -107,7 +87,7 @@ static int _read_array(struct pfilter *pf, struct dm_config_tree *cft,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out)
|
||||
int persistent_filter_load(struct dm_pool *mem, struct dev_filter *f, struct dm_config_tree **cft_out)
|
||||
{
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
struct dm_config_tree *cft;
|
||||
@@ -136,7 +116,7 @@ int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out
|
||||
if (!(cft = config_open(CONFIG_FILE_SPECIAL, pf->file, 1)))
|
||||
return_0;
|
||||
|
||||
if (!config_file_read(cft))
|
||||
if (!config_file_read(mem, cft))
|
||||
goto_out;
|
||||
|
||||
log_debug_devs("Loading persistent filter cache from %s", pf->file);
|
||||
@@ -146,6 +126,15 @@ int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out
|
||||
/* _read_array(pf, cft, "persistent_filter_cache/invalid_devices",
|
||||
PF_BAD_DEVICE); */
|
||||
|
||||
/* Did we find anything? */
|
||||
if (dm_hash_get_num_entries(pf->devices)) {
|
||||
/* We populated dev_cache ourselves */
|
||||
dev_cache_scan(0);
|
||||
if (!dev_cache_index_devs())
|
||||
stack;
|
||||
r = 1;
|
||||
}
|
||||
|
||||
log_very_verbose("Loaded persistent filter cache from %s", pf->file);
|
||||
|
||||
out:
|
||||
@@ -186,7 +175,7 @@ static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
|
||||
fprintf(fp, "\n\t]\n");
|
||||
}
|
||||
|
||||
static int _persistent_filter_dump(struct dev_filter *f, int merge_existing)
|
||||
static int _persistent_filter_dump(struct dev_filter *f, struct dm_pool *mem, int merge_existing)
|
||||
{
|
||||
struct pfilter *pf;
|
||||
char *tmp_file;
|
||||
@@ -245,7 +234,7 @@ static int _persistent_filter_dump(struct dev_filter *f, int merge_existing)
|
||||
lvm_stat_ctim(&ts, &info);
|
||||
if (merge_existing && timespeccmp(&ts, &pf->ctime, !=))
|
||||
/* Keep cft open to avoid losing lock */
|
||||
persistent_filter_load(f, &cft);
|
||||
persistent_filter_load(mem, f, &cft);
|
||||
|
||||
tmp_file = alloca(strlen(pf->file) + 5);
|
||||
sprintf(tmp_file, "%s.tmp", pf->file);
|
||||
@@ -288,51 +277,27 @@ static int _lookup_p(struct dev_filter *f, struct device *dev)
|
||||
struct pfilter *pf = (struct pfilter *) f->private;
|
||||
void *l = dm_hash_lookup(pf->devices, dev_name(dev));
|
||||
struct dm_str_list *sl;
|
||||
int pass = 1;
|
||||
|
||||
/* Cached bad, skip dev */
|
||||
/* Cached BAD? */
|
||||
if (l == PF_BAD_DEVICE) {
|
||||
log_debug_devs("%s: filter cache skipping (cached bad)", dev_name(dev));
|
||||
log_debug_devs("%s: Skipping (cached)", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cached good, use dev */
|
||||
if (l == PF_GOOD_DEVICE) {
|
||||
log_debug_devs("%s: filter cache using (cached good)", dev_name(dev));
|
||||
return 1;
|
||||
/* Test dm devices every time, so cache them as GOOD. */
|
||||
if (MAJOR(dev->dev) == pf->dt->device_mapper_major) {
|
||||
if (!l)
|
||||
dm_list_iterate_items(sl, &dev->aliases)
|
||||
if (!dm_hash_insert(pf->devices, sl->str, PF_GOOD_DEVICE)) {
|
||||
log_error("Failed to hash device to filter.");
|
||||
return 0;
|
||||
}
|
||||
return pf->real->passes_filter(pf->real, dev);
|
||||
}
|
||||
|
||||
/* Uncached, check filters and cache the result */
|
||||
/* Uncached */
|
||||
if (!l) {
|
||||
dev->flags &= ~DEV_FILTER_AFTER_SCAN;
|
||||
|
||||
pass = pf->real->passes_filter(pf->real, dev);
|
||||
|
||||
if (!pass) {
|
||||
/*
|
||||
* A device that does not pass one filter is excluded
|
||||
* even if the result of another filter is deferred,
|
||||
* because the deferred result won't change the exclude.
|
||||
*/
|
||||
l = PF_BAD_DEVICE;
|
||||
|
||||
} else if ((pass == -EAGAIN) || (dev->flags & DEV_FILTER_AFTER_SCAN)) {
|
||||
/*
|
||||
* When the filter result is deferred, we let the device
|
||||
* pass for now, but do not cache the result. We need to
|
||||
* rerun the filters later. At that point the final result
|
||||
* will be cached.
|
||||
*/
|
||||
log_debug_devs("filter cache deferred %s", dev_name(dev));
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
pass = 1;
|
||||
goto out;
|
||||
|
||||
} else if (pass) {
|
||||
l = PF_GOOD_DEVICE;
|
||||
}
|
||||
|
||||
log_debug_devs("filter caching %s %s", pass ? "good" : "bad", dev_name(dev));
|
||||
l = pf->real->passes_filter(pf->real, dev) ? PF_GOOD_DEVICE : PF_BAD_DEVICE;
|
||||
|
||||
dm_list_iterate_items(sl, &dev->aliases)
|
||||
if (!dm_hash_insert(pf->devices, sl->str, l)) {
|
||||
@@ -340,8 +305,8 @@ static int _lookup_p(struct dev_filter *f, struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
out:
|
||||
return pass;
|
||||
|
||||
return (l == PF_BAD_DEVICE) ? 0 : 1;
|
||||
}
|
||||
|
||||
static void _persistent_destroy(struct dev_filter *f)
|
||||
|
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Luca Berra
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use,
|
||||
* modify, copy, or redistribute it subject to the terms and conditions
|
||||
* of the GNU Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "filter.h"
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
static int _ignore_signature(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
int ret = 0;
|
||||
|
||||
if (!scan_bcache) {
|
||||
/* let pass, call again after scan */
|
||||
log_debug_devs("filter signature deferred %s", dev_name(dev));
|
||||
dev->flags |= DEV_FILTER_AFTER_SCAN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0, BUFSIZE);
|
||||
|
||||
if (!dev_read_bytes(dev, 0, BUFSIZE, buf)) {
|
||||
log_debug_devs("%s: Skipping: error in signature detection",
|
||||
dev_name(dev));
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev_is_lvm1(dev, buf, BUFSIZE)) {
|
||||
log_debug_devs("%s: Skipping lvm1 device", dev_name(dev));
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev_is_pool(dev, buf, BUFSIZE)) {
|
||||
log_debug_devs("%s: Skipping gfs-pool device", dev_name(dev));
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
ret = 1;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
if (f->use_count)
|
||||
log_error(INTERNAL_ERROR "Destroying signature filter while in use %u times.", f->use_count);
|
||||
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = dm_zalloc(sizeof(*f)))) {
|
||||
log_error("md filter allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = _ignore_signature;
|
||||
f->destroy = _destroy;
|
||||
f->use_count = 0;
|
||||
f->private = dt;
|
||||
|
||||
log_debug_devs("signature filter initialised.");
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
@@ -27,6 +27,12 @@ static int _native_check_pv_min_size(struct device *dev)
|
||||
uint64_t size;
|
||||
int ret = 0;
|
||||
|
||||
/* Check it's accessible */
|
||||
if (!dev_open_readonly_quiet(dev)) {
|
||||
log_debug_devs("%s: Skipping: open failed", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check it's not too small */
|
||||
if (!dev_get_size(dev, &size)) {
|
||||
log_debug_devs("%s: Skipping: dev_get_size failed", dev_name(dev));
|
||||
@@ -41,6 +47,9 @@ static int _native_check_pv_min_size(struct device *dev)
|
||||
|
||||
ret = 1;
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@
|
||||
struct dev_filter *composite_filter_create(int n, int use_dev_ext_info, struct dev_filter **filters);
|
||||
|
||||
struct dev_filter *lvm_type_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *md_filter_create(struct cmd_context *cmd, struct dev_types *dt);
|
||||
struct dev_filter *md_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *fwraid_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *mpath_filter_create(struct dev_types *dt);
|
||||
struct dev_filter *partitioned_filter_create(struct dev_types *dt);
|
||||
@@ -31,7 +31,6 @@ struct dev_filter *persistent_filter_create(struct dev_types *dt,
|
||||
struct dev_filter *f,
|
||||
const char *file);
|
||||
struct dev_filter *sysfs_filter_create(void);
|
||||
struct dev_filter *signature_filter_create(struct dev_types *dt);
|
||||
|
||||
struct dev_filter *internal_filter_create(void);
|
||||
int internal_filter_allow(struct dm_pool *mem, struct device *dev);
|
||||
@@ -54,6 +53,6 @@ typedef enum {
|
||||
} filter_mode_t;
|
||||
struct dev_filter *usable_filter_create(struct dev_types *dt, filter_mode_t mode);
|
||||
|
||||
int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out);
|
||||
int persistent_filter_load(struct dm_pool *mem, struct dev_filter *f, struct dm_config_tree **cft_out);
|
||||
|
||||
#endif /* _LVM_FILTER_H */
|
||||
|
@@ -205,7 +205,7 @@ int munge_pvd(struct device *dev, struct pv_disk *pvd)
|
||||
|
||||
static int _read_pvd(struct device *dev, struct pv_disk *pvd)
|
||||
{
|
||||
if (!dev_read_bytes(dev, UINT64_C(0), sizeof(*pvd), pvd)) {
|
||||
if (!dev_read_buf(dev, UINT64_C(0), sizeof(*pvd), DEV_IO_FMT1, pvd)) {
|
||||
log_very_verbose("Failed to read PV data from %s",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
@@ -216,7 +216,7 @@ static int _read_pvd(struct device *dev, struct pv_disk *pvd)
|
||||
|
||||
static int _read_lvd(struct device *dev, uint64_t pos, struct lv_disk *disk)
|
||||
{
|
||||
if (!dev_read_bytes(dev, pos, sizeof(*disk), disk))
|
||||
if (!dev_read_buf(dev, pos, sizeof(*disk), DEV_IO_FMT1, disk))
|
||||
return_0;
|
||||
|
||||
_xlate_lvd(disk);
|
||||
@@ -228,7 +228,7 @@ int read_vgd(struct device *dev, struct vg_disk *vgd, struct pv_disk *pvd)
|
||||
{
|
||||
uint64_t pos = pvd->vg_on_disk.base;
|
||||
|
||||
if (!dev_read_bytes(dev, pos, sizeof(*vgd), vgd))
|
||||
if (!dev_read_buf(dev, pos, sizeof(*vgd), DEV_IO_FMT1, vgd))
|
||||
return_0;
|
||||
|
||||
_xlate_vgd(vgd);
|
||||
@@ -252,7 +252,7 @@ static int _read_uuids(struct disk_list *data)
|
||||
uint64_t end = pos + data->pvd.pv_uuidlist_on_disk.size;
|
||||
|
||||
while (pos < end && num_read < data->vgd.pv_cur) {
|
||||
if (!dev_read_bytes(data->dev, pos, sizeof(buffer), buffer))
|
||||
if (!dev_read_buf(data->dev, pos, sizeof(buffer), DEV_IO_FMT1, buffer))
|
||||
return_0;
|
||||
|
||||
if (!(ul = dm_pool_alloc(data->mem, sizeof(*ul))))
|
||||
@@ -311,7 +311,7 @@ static int _read_extents(struct disk_list *data)
|
||||
if (!extents)
|
||||
return_0;
|
||||
|
||||
if (!dev_read_bytes(data->dev, pos, len, extents))
|
||||
if (!dev_read_buf(data->dev, pos, len, DEV_IO_FMT1, extents))
|
||||
return_0;
|
||||
|
||||
_xlate_extents(extents, data->pvd.pe_total);
|
||||
@@ -337,6 +337,7 @@ static void __update_lvmcache(const struct format_type *fmt,
|
||||
|
||||
lvmcache_set_device_size(info, ((uint64_t)xlate32(dl->pvd.pv_size)) << SECTOR_SHIFT);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_make_valid(info);
|
||||
}
|
||||
|
||||
static struct disk_list *__read_disk(const struct format_type *fmt,
|
||||
|
@@ -181,7 +181,8 @@ static struct volume_group *_format1_vg_read(struct format_instance *fid,
|
||||
const char *vg_name,
|
||||
struct metadata_area *mda __attribute__((unused)),
|
||||
struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)),
|
||||
unsigned *use_previous_vg __attribute__((unused)))
|
||||
unsigned *use_previous_vg __attribute__((unused)),
|
||||
int single_device __attribute__((unused)), unsigned ioflags)
|
||||
{
|
||||
struct volume_group *vg;
|
||||
struct disk_list *dl;
|
||||
|
@@ -54,15 +54,17 @@ static int _lvm1_write(struct label *label __attribute__((unused)), void *buf __
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _lvm1_read(struct labeller *l, struct device *dev, void *buf,
|
||||
struct label **label)
|
||||
static int _lvm1_read(struct labeller *l, struct device *dev, void *buf, unsigned ioflags,
|
||||
lvm_callback_fn_t read_label_callback_fn, void *read_label_callback_context)
|
||||
{
|
||||
struct pv_disk *pvd = (struct pv_disk *) buf;
|
||||
struct vg_disk vgd;
|
||||
struct lvmcache_info *info;
|
||||
struct label *label = NULL;
|
||||
const char *vgid = FMT_LVM1_ORPHAN_VG_NAME;
|
||||
const char *vgname = FMT_LVM1_ORPHAN_VG_NAME;
|
||||
unsigned exported = 0;
|
||||
int r = 0;
|
||||
|
||||
munge_pvd(dev, pvd);
|
||||
|
||||
@@ -76,16 +78,24 @@ static int _lvm1_read(struct labeller *l, struct device *dev, void *buf,
|
||||
|
||||
if (!(info = lvmcache_add(l, (char *)pvd->pv_uuid, dev, vgname, vgid,
|
||||
exported)))
|
||||
return_0;
|
||||
*label = lvmcache_get_label(info);
|
||||
goto_out;
|
||||
|
||||
label = lvmcache_get_label(info);
|
||||
|
||||
lvmcache_set_device_size(info, ((uint64_t)xlate32(pvd->pv_size)) << SECTOR_SHIFT);
|
||||
lvmcache_set_ext_version(info, 0);
|
||||
lvmcache_set_ext_flags(info, 0);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_del_bas(info);
|
||||
lvmcache_make_valid(info);
|
||||
|
||||
return 1;
|
||||
r = 1;
|
||||
|
||||
out:
|
||||
if (read_label_callback_fn)
|
||||
read_label_callback_fn(!r, 0, read_label_callback_context, label);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _lvm1_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
|
||||
|
@@ -40,7 +40,7 @@ static int __read_pool_disk(const struct format_type *fmt, struct device *dev,
|
||||
char buf[512] __attribute__((aligned(8)));
|
||||
|
||||
/* FIXME: Need to check the cache here first */
|
||||
if (!dev_read(dev, UINT64_C(0), 512, DEV_IO_POOL, buf)) {
|
||||
if (!dev_read_buf(dev, UINT64_C(0), 512, DEV_IO_POOL, buf)) {
|
||||
log_very_verbose("Failed to read PV data from %s",
|
||||
dev_name(dev));
|
||||
return 0;
|
||||
@@ -111,6 +111,7 @@ int read_pool_label(struct pool_list *pl, struct labeller *l,
|
||||
lvmcache_set_ext_flags(info, 0);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_del_bas(info);
|
||||
lvmcache_make_valid(info);
|
||||
|
||||
pl->dev = dev;
|
||||
pl->pv = NULL;
|
||||
@@ -378,6 +379,8 @@ int read_pool_pds(const struct format_type *fmt, const char *vg_name,
|
||||
vg_name);
|
||||
return 0;
|
||||
}
|
||||
if (full_scan > 0)
|
||||
lvmcache_force_next_label_scan();
|
||||
lvmcache_label_scan(fmt->cmd);
|
||||
|
||||
} while (1);
|
||||
|
@@ -102,7 +102,8 @@ static struct volume_group *_pool_vg_read(struct format_instance *fid,
|
||||
const char *vg_name,
|
||||
struct metadata_area *mda __attribute__((unused)),
|
||||
struct cached_vg_fmtdata **vg_fmtdata __attribute__((unused)),
|
||||
unsigned *use_previous_vg __attribute__((unused)))
|
||||
unsigned *use_previous_vg __attribute__((unused)),
|
||||
int single_device __attribute__((unused)), unsigned ioflags)
|
||||
{
|
||||
struct volume_group *vg;
|
||||
struct user_subpool *usp;
|
||||
|
@@ -55,12 +55,19 @@ static int _pool_write(struct label *label __attribute__((unused)), void *buf __
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _pool_read(struct labeller *l, struct device *dev, void *buf,
|
||||
struct label **label)
|
||||
static int _pool_read(struct labeller *l, struct device *dev, void *buf, unsigned ioflags,
|
||||
lvm_callback_fn_t read_label_callback_fn, void *read_label_callback_context)
|
||||
{
|
||||
struct pool_list pl;
|
||||
struct label *label;
|
||||
int r;
|
||||
|
||||
return read_pool_label(&pl, l, dev, buf, label);
|
||||
r = read_pool_label(&pl, l, dev, buf, &label);
|
||||
|
||||
if (read_label_callback_fn)
|
||||
read_label_callback_fn(!r, 0, read_label_callback_context, label);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _pool_initialise_label(struct labeller *l __attribute__((unused)), struct label *label)
|
||||
|
@@ -80,7 +80,8 @@ static int _split_vg(const char *filename, char *vgname, size_t vgsize,
|
||||
if (vg_len + 1 > vgsize)
|
||||
return 0;
|
||||
|
||||
(void) dm_strncpy(vgname, filename, vg_len + 1);
|
||||
strncpy(vgname, filename, vg_len);
|
||||
vgname[vg_len] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -135,8 +136,8 @@ static struct dm_list *_scan_archive(struct dm_pool *mem,
|
||||
|
||||
dm_list_init(results);
|
||||
|
||||
/* Sort fails beyond 5-digit indexes */
|
||||
if ((count = scandir(dir, &dirent, NULL, alphasort)) < 0) {
|
||||
/* Use versionsort to handle numbers beyond 5 digits */
|
||||
if ((count = scandir(dir, &dirent, NULL, versionsort)) < 0) {
|
||||
log_error("Couldn't scan the archive directory (%s).", dir);
|
||||
return 0;
|
||||
}
|
||||
@@ -320,7 +321,7 @@ static void _display_archive(struct cmd_context *cmd, struct archive_file *af)
|
||||
* retrieve the archive time and description.
|
||||
*/
|
||||
/* FIXME Use variation on _vg_read */
|
||||
if (!(vg = text_read_metadata_file(tf, af->path, &when, &desc))) {
|
||||
if (!(vg = text_vg_import_file(tf, af->path, &when, &desc))) {
|
||||
log_error("Unable to read archive file.");
|
||||
tf->fmt->ops->destroy_instance(tf);
|
||||
return;
|
||||
|
@@ -320,7 +320,7 @@ struct volume_group *backup_read_vg(struct cmd_context *cmd,
|
||||
}
|
||||
|
||||
dm_list_iterate_items(mda, &tf->metadata_areas_in_use) {
|
||||
if (!(vg = mda->ops->vg_read(tf, vg_name, mda, NULL, NULL)))
|
||||
if (!(vg = mda->ops->vg_read(tf, vg_name, mda, NULL, NULL, 0, 0)))
|
||||
stack;
|
||||
break;
|
||||
}
|
||||
@@ -488,11 +488,19 @@ int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
}
|
||||
|
||||
log_verbose("Zeroing start of device %s", pv_name);
|
||||
|
||||
if (!dev_write_zeros(dev, 0, 2048)) {
|
||||
log_error("%s not wiped: aborting", pv_name);
|
||||
if (!dev_open_quiet(dev)) {
|
||||
log_error("%s not opened: device not zeroed", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_set(dev, UINT64_C(0), (size_t) 2048, DEV_IO_LABEL, 0)) {
|
||||
log_error("%s not wiped: aborting", pv_name);
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "lvm-version.h"
|
||||
#include "toolcontext.h"
|
||||
#include "config-util.h"
|
||||
#include "layout.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
@@ -123,11 +124,12 @@ static int _extend_buffer(struct formatter *f)
|
||||
|
||||
log_debug_metadata("Doubling metadata output buffer to " FMTu32,
|
||||
f->data.buf.size * 2);
|
||||
if (!(newbuf = dm_realloc(f->data.buf.start,
|
||||
f->data.buf.size * 2))) {
|
||||
log_error("Buffer reallocation failed.");
|
||||
return 0;
|
||||
}
|
||||
if (!(newbuf = dm_malloc_aligned(f->data.buf.size * 2, 0)))
|
||||
return_0;
|
||||
|
||||
memcpy(newbuf, f->data.buf.start, f->data.buf.size);
|
||||
free(f->data.buf.start);
|
||||
|
||||
f->data.buf.start = newbuf;
|
||||
f->data.buf.size *= 2;
|
||||
|
||||
@@ -1064,7 +1066,7 @@ size_t text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf)
|
||||
return_0;
|
||||
|
||||
f->data.buf.size = 65536; /* Initial metadata limit */
|
||||
if (!(f->data.buf.start = dm_malloc(f->data.buf.size))) {
|
||||
if (!(f->data.buf.start = dm_malloc_aligned(f->data.buf.size, 0))) {
|
||||
log_error("text_export buffer allocation failed");
|
||||
goto out;
|
||||
}
|
||||
@@ -1079,7 +1081,12 @@ size_t text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf)
|
||||
goto_out;
|
||||
}
|
||||
|
||||
r = f->data.buf.used + 1;
|
||||
f->data.buf.used += 1; /* Terminating NUL */
|
||||
|
||||
/* Zero fill up to next alignment boundary */
|
||||
memset(f->data.buf.start + f->data.buf.used, 0, MDA_ALIGNMENT - f->data.buf.used % MDA_ALIGNMENT);
|
||||
|
||||
r = f->data.buf.used;
|
||||
*buf = f->data.buf.start;
|
||||
|
||||
out:
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,7 @@ struct text_vg_version_ops {
|
||||
int (*check_version) (const struct dm_config_tree * cf);
|
||||
struct volume_group *(*read_vg) (struct format_instance * fid,
|
||||
const struct dm_config_tree *cf,
|
||||
unsigned use_cached_pvs,
|
||||
unsigned allow_lvmetad_extensions);
|
||||
void (*read_desc) (struct dm_pool * mem, const struct dm_config_tree *cf,
|
||||
time_t *when, char **desc);
|
||||
@@ -67,26 +68,29 @@ int read_segtype_lvflags(uint64_t *status, char *segtype_str);
|
||||
|
||||
int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp);
|
||||
size_t text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf);
|
||||
struct volume_group *text_read_metadata_file(struct format_instance *fid,
|
||||
struct volume_group *text_vg_import_file(struct format_instance *fid,
|
||||
const char *file,
|
||||
time_t *when, char **desc);
|
||||
struct volume_group *text_read_metadata(struct format_instance *fid,
|
||||
struct volume_group *text_vg_import_fd(struct format_instance *fid,
|
||||
const char *file,
|
||||
struct cached_vg_fmtdata **vg_fmtdata,
|
||||
unsigned *use_previous_vg,
|
||||
int single_device,
|
||||
struct device *dev, int primary_mda,
|
||||
off_t offset, uint32_t size,
|
||||
off_t offset2, uint32_t size2,
|
||||
checksum_fn_t checksum_fn,
|
||||
uint32_t checksum,
|
||||
uint32_t checksum, unsigned ioflags,
|
||||
time_t *when, char **desc);
|
||||
|
||||
int text_read_metadata_summary(const struct format_type *fmt,
|
||||
int text_vgsummary_import(const struct format_type *fmt,
|
||||
struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, uint32_t size,
|
||||
off_t offset2, uint32_t size2,
|
||||
checksum_fn_t checksum_fn,
|
||||
int checksum_only,
|
||||
struct lvmcache_vgsummary *vgsummary);
|
||||
int checksum_only, unsigned ioflags,
|
||||
struct lvmcache_vgsummary *vgsummary,
|
||||
lvm_callback_fn_t process_vgsummary_fn,
|
||||
void *process_vgsummary_context);
|
||||
|
||||
#endif
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "lib.h"
|
||||
#include "metadata.h"
|
||||
#include "import-export.h"
|
||||
#include "toolcontext.h"
|
||||
|
||||
/* FIXME Use tidier inclusion method */
|
||||
static struct text_vg_version_ops *(_text_vsn_list[2]);
|
||||
@@ -32,70 +33,102 @@ static void _init_text_import(void)
|
||||
_text_import_initialised = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out vgname on a given device.
|
||||
*/
|
||||
int text_read_metadata_summary(const struct format_type *fmt,
|
||||
struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, uint32_t size,
|
||||
off_t offset2, uint32_t size2,
|
||||
checksum_fn_t checksum_fn,
|
||||
int checksum_only,
|
||||
struct lvmcache_vgsummary *vgsummary)
|
||||
{
|
||||
struct import_vgsummary_params {
|
||||
const struct format_type *fmt;
|
||||
struct dm_config_tree *cft;
|
||||
int checksum_only;
|
||||
struct lvmcache_vgsummary *vgsummary;
|
||||
lvm_callback_fn_t process_vgsummary_fn;
|
||||
void *process_vgsummary_context;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static void _import_vgsummary(int failed, unsigned ioflags, void *context, const void *data)
|
||||
{
|
||||
struct import_vgsummary_params *ivsp = context;
|
||||
struct text_vg_version_ops **vsn;
|
||||
int r = 0;
|
||||
|
||||
_init_text_import();
|
||||
|
||||
if (!(cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0)))
|
||||
return_0;
|
||||
|
||||
if (dev) {
|
||||
log_debug_metadata("Reading metadata summary from %s at %llu size %d (+%d)",
|
||||
dev_name(dev), (unsigned long long)offset,
|
||||
size, size2);
|
||||
|
||||
if (!config_file_read_fd(cft, dev, reason, offset, size,
|
||||
offset2, size2, checksum_fn,
|
||||
vgsummary->mda_checksum,
|
||||
checksum_only, 1)) {
|
||||
/* FIXME: handle errors */
|
||||
log_error("Couldn't read volume group metadata from %s.", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (!config_file_read(cft)) {
|
||||
log_error("Couldn't read volume group metadata from file.");
|
||||
goto out;
|
||||
}
|
||||
if (failed) {
|
||||
ivsp->ret = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
if (checksum_only) {
|
||||
if (ivsp->checksum_only)
|
||||
/* Checksum matches already-cached content - no need to reparse. */
|
||||
log_debug_metadata("Skipped parsing metadata on %s", dev_name(dev));
|
||||
r = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a set of version functions that can read this file
|
||||
*/
|
||||
for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
|
||||
if (!(*vsn)->check_version(cft))
|
||||
if (!(*vsn)->check_version(ivsp->cft))
|
||||
continue;
|
||||
|
||||
if (!(*vsn)->read_vgsummary(fmt, cft, vgsummary))
|
||||
if (!(*vsn)->read_vgsummary(ivsp->fmt, ivsp->cft, ivsp->vgsummary)) {
|
||||
ivsp->ret = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
r = 1;
|
||||
break;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
config_destroy(cft);
|
||||
return r;
|
||||
/* Nothing found */
|
||||
ivsp->ret = 0;
|
||||
|
||||
out:
|
||||
config_destroy(ivsp->cft);
|
||||
|
||||
if (ivsp->process_vgsummary_fn)
|
||||
ivsp->process_vgsummary_fn(!ivsp->ret, ioflags, ivsp->process_vgsummary_context, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find out vgname on a given device.
|
||||
*/
|
||||
int text_vgsummary_import(const struct format_type *fmt,
|
||||
struct device *dev, dev_io_reason_t reason,
|
||||
off_t offset, uint32_t size,
|
||||
off_t offset2, uint32_t size2,
|
||||
checksum_fn_t checksum_fn,
|
||||
int checksum_only, unsigned ioflags,
|
||||
struct lvmcache_vgsummary *vgsummary,
|
||||
lvm_callback_fn_t process_vgsummary_fn,
|
||||
void *process_vgsummary_context)
|
||||
{
|
||||
struct import_vgsummary_params *ivsp;
|
||||
|
||||
_init_text_import();
|
||||
|
||||
if (!(ivsp = dm_pool_zalloc(fmt->cmd->mem, sizeof(*ivsp)))) {
|
||||
log_error("Failed to allocate import_vgsummary_params struct.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(ivsp->cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0)))
|
||||
return_0;
|
||||
|
||||
ivsp->fmt = fmt;
|
||||
ivsp->checksum_only = checksum_only;
|
||||
ivsp->vgsummary = vgsummary;
|
||||
ivsp->process_vgsummary_fn = process_vgsummary_fn;
|
||||
ivsp->process_vgsummary_context = process_vgsummary_context;
|
||||
ivsp->ret = 1;
|
||||
|
||||
if (!dev) {
|
||||
if (!config_file_read(fmt->cmd->mem, ivsp->cft)) {
|
||||
log_error("Couldn't read volume group metadata.");
|
||||
ivsp->ret = 0;
|
||||
}
|
||||
_import_vgsummary(!ivsp->ret, ioflags, ivsp, NULL);
|
||||
} else if (!config_file_read_fd(fmt->cmd->mem, ivsp->cft, dev, reason, offset, size,
|
||||
offset2, size2, checksum_fn,
|
||||
vgsummary->mda_checksum,
|
||||
checksum_only, 1, ioflags, &_import_vgsummary, ivsp)) {
|
||||
log_error("Couldn't read volume group metadata.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ivsp->ret;
|
||||
}
|
||||
|
||||
struct cached_vg_fmtdata {
|
||||
@@ -103,74 +136,30 @@ struct cached_vg_fmtdata {
|
||||
size_t cached_mda_size;
|
||||
};
|
||||
|
||||
struct volume_group *text_read_metadata(struct format_instance *fid,
|
||||
const char *file,
|
||||
struct cached_vg_fmtdata **vg_fmtdata,
|
||||
unsigned *use_previous_vg,
|
||||
struct device *dev, int primary_mda,
|
||||
off_t offset, uint32_t size,
|
||||
off_t offset2, uint32_t size2,
|
||||
checksum_fn_t checksum_fn,
|
||||
uint32_t checksum,
|
||||
time_t *when, char **desc)
|
||||
{
|
||||
struct volume_group *vg = NULL;
|
||||
struct import_vg_params {
|
||||
struct format_instance *fid;
|
||||
struct dm_config_tree *cft;
|
||||
struct text_vg_version_ops **vsn;
|
||||
int single_device;
|
||||
int skip_parse;
|
||||
unsigned *use_previous_vg;
|
||||
struct volume_group *vg;
|
||||
uint32_t checksum;
|
||||
uint32_t total_size;
|
||||
time_t *when;
|
||||
struct cached_vg_fmtdata **vg_fmtdata;
|
||||
char **desc;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct holds the checksum and size of the VG metadata
|
||||
* that was read from a previous device. When we read the VG
|
||||
* metadata from this device, we can skip parsing it into a
|
||||
* cft (saving time) if the checksum of the metadata buffer
|
||||
* we read from this device matches the size/checksum saved in
|
||||
* the mda_header/rlocn struct on this device, and matches the
|
||||
* size/checksum from the previous device.
|
||||
*/
|
||||
if (vg_fmtdata && !*vg_fmtdata &&
|
||||
!(*vg_fmtdata = dm_pool_zalloc(fid->mem, sizeof(**vg_fmtdata)))) {
|
||||
log_error("Failed to allocate VG fmtdata for text format.");
|
||||
return NULL;
|
||||
}
|
||||
static void _import_vg(int failed, unsigned ioflags, void *context, const void *data)
|
||||
{
|
||||
struct import_vg_params *ivp = context;
|
||||
struct text_vg_version_ops **vsn;
|
||||
|
||||
_init_text_import();
|
||||
ivp->vg = NULL;
|
||||
|
||||
*desc = NULL;
|
||||
*when = 0;
|
||||
|
||||
if (!(cft = config_open(CONFIG_FILE_SPECIAL, file, 0)))
|
||||
return_NULL;
|
||||
|
||||
/* Does the metadata match the already-cached VG? */
|
||||
skip_parse = vg_fmtdata &&
|
||||
((*vg_fmtdata)->cached_mda_checksum == checksum) &&
|
||||
((*vg_fmtdata)->cached_mda_size == (size + size2));
|
||||
|
||||
|
||||
if (dev) {
|
||||
log_debug_metadata("Reading metadata from %s at %llu size %d (+%d)",
|
||||
dev_name(dev), (unsigned long long)offset,
|
||||
size, size2);
|
||||
|
||||
if (!config_file_read_fd(cft, dev, MDA_CONTENT_REASON(primary_mda), offset, size,
|
||||
offset2, size2, checksum_fn, checksum,
|
||||
skip_parse, 1)) {
|
||||
/* FIXME: handle errors */
|
||||
log_error("Couldn't read volume group metadata from %s.", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (!config_file_read(cft)) {
|
||||
log_error("Couldn't read volume group metadata from file.");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip_parse) {
|
||||
if (use_previous_vg)
|
||||
*use_previous_vg = 1;
|
||||
log_debug_metadata("Skipped parsing metadata on %s", dev_name(dev));
|
||||
if (ivp->skip_parse) {
|
||||
if (ivp->use_previous_vg)
|
||||
*ivp->use_previous_vg = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -178,36 +167,98 @@ struct volume_group *text_read_metadata(struct format_instance *fid,
|
||||
* Find a set of version functions that can read this file
|
||||
*/
|
||||
for (vsn = &_text_vsn_list[0]; *vsn; vsn++) {
|
||||
if (!(*vsn)->check_version(cft))
|
||||
if (!(*vsn)->check_version(ivp->cft))
|
||||
continue;
|
||||
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, 0)))
|
||||
if (!(ivp->vg = (*vsn)->read_vg(ivp->fid, ivp->cft, ivp->single_device, 0)))
|
||||
goto_out;
|
||||
|
||||
(*vsn)->read_desc(vg->vgmem, cft, when, desc);
|
||||
(*vsn)->read_desc(ivp->vg->vgmem, ivp->cft, ivp->when, ivp->desc);
|
||||
break;
|
||||
}
|
||||
|
||||
if (vg && vg_fmtdata && *vg_fmtdata) {
|
||||
(*vg_fmtdata)->cached_mda_size = (size + size2);
|
||||
(*vg_fmtdata)->cached_mda_checksum = checksum;
|
||||
if (ivp->vg && ivp->vg_fmtdata && *ivp->vg_fmtdata) {
|
||||
(*ivp->vg_fmtdata)->cached_mda_size = ivp->total_size;
|
||||
(*ivp->vg_fmtdata)->cached_mda_checksum = ivp->checksum;
|
||||
}
|
||||
|
||||
if (use_previous_vg)
|
||||
*use_previous_vg = 0;
|
||||
if (ivp->use_previous_vg)
|
||||
*ivp->use_previous_vg = 0;
|
||||
|
||||
out:
|
||||
config_destroy(cft);
|
||||
return vg;
|
||||
out:
|
||||
config_destroy(ivp->cft);
|
||||
}
|
||||
|
||||
struct volume_group *text_read_metadata_file(struct format_instance *fid,
|
||||
struct volume_group *text_vg_import_fd(struct format_instance *fid,
|
||||
const char *file,
|
||||
struct cached_vg_fmtdata **vg_fmtdata,
|
||||
unsigned *use_previous_vg,
|
||||
int single_device,
|
||||
struct device *dev, int primary_mda,
|
||||
off_t offset, uint32_t size,
|
||||
off_t offset2, uint32_t size2,
|
||||
checksum_fn_t checksum_fn,
|
||||
uint32_t checksum, unsigned ioflags,
|
||||
time_t *when, char **desc)
|
||||
{
|
||||
struct import_vg_params *ivp;
|
||||
|
||||
if (vg_fmtdata && !*vg_fmtdata &&
|
||||
!(*vg_fmtdata = dm_pool_zalloc(fid->mem, sizeof(**vg_fmtdata)))) {
|
||||
log_error("Failed to allocate VG fmtdata for text format.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(ivp = dm_pool_zalloc(fid->fmt->cmd->mem, sizeof(*ivp)))) {
|
||||
log_error("Failed to allocate import_vgsummary_params struct.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_init_text_import();
|
||||
|
||||
ivp->fid = fid;
|
||||
ivp->when = when;
|
||||
*ivp->when = 0;
|
||||
ivp->desc = desc;
|
||||
*ivp->desc = NULL;
|
||||
ivp->single_device = single_device;
|
||||
ivp->use_previous_vg = use_previous_vg;
|
||||
ivp->checksum = checksum;
|
||||
ivp->total_size = size + size2;
|
||||
ivp->vg_fmtdata = vg_fmtdata;
|
||||
|
||||
if (!(ivp->cft = config_open(CONFIG_FILE_SPECIAL, file, 0)))
|
||||
return_NULL;
|
||||
|
||||
/* Does the metadata match the already-cached VG? */
|
||||
ivp->skip_parse = vg_fmtdata &&
|
||||
((*vg_fmtdata)->cached_mda_checksum == checksum) &&
|
||||
((*vg_fmtdata)->cached_mda_size == ivp->total_size);
|
||||
|
||||
if (!dev && !config_file_read(fid->mem, ivp->cft)) {
|
||||
config_destroy(ivp->cft);
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
if (!config_file_read_fd(fid->mem, ivp->cft, dev, MDA_CONTENT_REASON(primary_mda), offset, size,
|
||||
offset2, size2, checksum_fn, checksum,
|
||||
ivp->skip_parse, 1, ioflags, &_import_vg, ivp)) {
|
||||
config_destroy(ivp->cft);
|
||||
return_NULL;
|
||||
}
|
||||
} else
|
||||
_import_vg(0, 0, ivp, NULL);
|
||||
|
||||
return ivp->vg;
|
||||
}
|
||||
|
||||
struct volume_group *text_vg_import_file(struct format_instance *fid,
|
||||
const char *file,
|
||||
time_t *when, char **desc)
|
||||
{
|
||||
return text_read_metadata(fid, file, NULL, NULL, NULL, 0,
|
||||
(off_t)0, 0, (off_t)0, 0, NULL, 0,
|
||||
when, desc);
|
||||
return text_vg_import_fd(fid, file, NULL, NULL, 0, NULL, 0, (off_t)0, 0, (off_t)0, 0, NULL, 0,
|
||||
0, when, desc);
|
||||
}
|
||||
|
||||
static struct volume_group *_import_vg_from_config_tree(const struct dm_config_tree *cft,
|
||||
@@ -227,7 +278,7 @@ static struct volume_group *_import_vg_from_config_tree(const struct dm_config_t
|
||||
* The only path to this point uses cached vgmetadata,
|
||||
* so it can use cached PV state too.
|
||||
*/
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, allow_lvmetad_extensions)))
|
||||
if (!(vg = (*vsn)->read_vg(fid, cft, 1, allow_lvmetad_extensions)))
|
||||
stack;
|
||||
else if ((vg_missing = vg_missing_pv_count(vg))) {
|
||||
log_verbose("There are %d physical volumes missing.",
|
||||
|
@@ -32,7 +32,9 @@ typedef int (*section_fn) (struct format_instance * fid,
|
||||
struct volume_group * vg, const struct dm_config_node * pvn,
|
||||
const struct dm_config_node * vgn,
|
||||
struct dm_hash_table * pv_hash,
|
||||
struct dm_hash_table * lv_hash);
|
||||
struct dm_hash_table * lv_hash,
|
||||
unsigned *scan_done_once,
|
||||
unsigned report_missing_devices);
|
||||
|
||||
#define _read_int32(root, path, result) \
|
||||
dm_config_get_uint32(root, path, (uint32_t *) (result))
|
||||
@@ -178,7 +180,9 @@ static int _read_pv(struct format_instance *fid,
|
||||
struct volume_group *vg, const struct dm_config_node *pvn,
|
||||
const struct dm_config_node *vgn __attribute__((unused)),
|
||||
struct dm_hash_table *pv_hash,
|
||||
struct dm_hash_table *lv_hash __attribute__((unused)))
|
||||
struct dm_hash_table *lv_hash __attribute__((unused)),
|
||||
unsigned *scan_done_once,
|
||||
unsigned report_missing_devices)
|
||||
{
|
||||
struct dm_pool *mem = vg->vgmem;
|
||||
struct physical_volume *pv;
|
||||
@@ -216,16 +220,16 @@ static int _read_pv(struct format_instance *fid,
|
||||
/*
|
||||
* Convert the uuid into a device.
|
||||
*/
|
||||
if (!(pv->dev = lvmcache_device_from_pvid(fid->fmt->cmd, &pv->id, &pv->label_sector))) {
|
||||
if (!(pv->dev = lvmcache_device_from_pvid(fid->fmt->cmd, &pv->id, scan_done_once,
|
||||
&pv->label_sector))) {
|
||||
char buffer[64] __attribute__((aligned(8)));
|
||||
|
||||
if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
|
||||
buffer[0] = '\0';
|
||||
|
||||
if (fid->fmt->cmd && !fid->fmt->cmd->pvscan_cache_single)
|
||||
if (report_missing_devices)
|
||||
log_error_once("Couldn't find device with uuid %s.", buffer);
|
||||
else
|
||||
log_debug_metadata("Couldn't find device with uuid %s.", buffer);
|
||||
log_very_verbose("Couldn't find device with uuid %s.", buffer);
|
||||
}
|
||||
|
||||
if (!(pv->vg_name = dm_pool_strdup(mem, vg->name)))
|
||||
@@ -570,7 +574,9 @@ static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
|
||||
struct volume_group *vg, const struct dm_config_node *lvn,
|
||||
const struct dm_config_node *vgn __attribute__((unused)),
|
||||
struct dm_hash_table *pv_hash __attribute__((unused)),
|
||||
struct dm_hash_table *lv_hash)
|
||||
struct dm_hash_table *lv_hash,
|
||||
unsigned *scan_done_once __attribute__((unused)),
|
||||
unsigned report_missing_devices __attribute__((unused)))
|
||||
{
|
||||
struct dm_pool *mem = vg->vgmem;
|
||||
struct logical_volume *lv;
|
||||
@@ -725,7 +731,9 @@ static int _read_historical_lvnames(struct format_instance *fid __attribute__((u
|
||||
struct volume_group *vg, const struct dm_config_node *hlvn,
|
||||
const struct dm_config_node *vgn __attribute__((unused)),
|
||||
struct dm_hash_table *pv_hash __attribute__((unused)),
|
||||
struct dm_hash_table *lv_hash __attribute__((unused)))
|
||||
struct dm_hash_table *lv_hash __attribute__((unused)),
|
||||
unsigned *scan_done_once __attribute__((unused)),
|
||||
unsigned report_missing_devices __attribute__((unused)))
|
||||
{
|
||||
struct dm_pool *mem = vg->vgmem;
|
||||
struct generic_logical_volume *glv;
|
||||
@@ -794,7 +802,9 @@ static int _read_historical_lvnames_interconnections(struct format_instance *fid
|
||||
struct volume_group *vg, const struct dm_config_node *hlvn,
|
||||
const struct dm_config_node *vgn __attribute__((unused)),
|
||||
struct dm_hash_table *pv_hash __attribute__((unused)),
|
||||
struct dm_hash_table *lv_hash __attribute__((unused)))
|
||||
struct dm_hash_table *lv_hash __attribute__((unused)),
|
||||
unsigned *scan_done_once __attribute__((unused)),
|
||||
unsigned report_missing_devices __attribute__((unused)))
|
||||
{
|
||||
struct dm_pool *mem = vg->vgmem;
|
||||
const char *historical_lv_name, *origin_name = NULL;
|
||||
@@ -904,7 +914,9 @@ static int _read_lvsegs(struct format_instance *fid,
|
||||
struct volume_group *vg, const struct dm_config_node *lvn,
|
||||
const struct dm_config_node *vgn __attribute__((unused)),
|
||||
struct dm_hash_table *pv_hash,
|
||||
struct dm_hash_table *lv_hash)
|
||||
struct dm_hash_table *lv_hash,
|
||||
unsigned *scan_done_once __attribute__((unused)),
|
||||
unsigned report_missing_devices __attribute__((unused)))
|
||||
{
|
||||
struct logical_volume *lv;
|
||||
|
||||
@@ -965,9 +977,12 @@ static int _read_sections(struct format_instance *fid,
|
||||
struct volume_group *vg, const struct dm_config_node *vgn,
|
||||
struct dm_hash_table *pv_hash,
|
||||
struct dm_hash_table *lv_hash,
|
||||
int optional)
|
||||
int optional,
|
||||
unsigned *scan_done_once)
|
||||
{
|
||||
const struct dm_config_node *n;
|
||||
/* Only report missing devices when doing a scan */
|
||||
unsigned report_missing_devices = scan_done_once ? !*scan_done_once : 1;
|
||||
|
||||
if (!dm_config_get_section(vgn, section, &n)) {
|
||||
if (!optional) {
|
||||
@@ -979,7 +994,8 @@ static int _read_sections(struct format_instance *fid,
|
||||
}
|
||||
|
||||
for (n = n->child; n; n = n->sib) {
|
||||
if (!fn(fid, vg, n, vgn, pv_hash, lv_hash))
|
||||
if (!fn(fid, vg, n, vgn, pv_hash, lv_hash,
|
||||
scan_done_once, report_missing_devices))
|
||||
return_0;
|
||||
}
|
||||
|
||||
@@ -988,6 +1004,7 @@ static int _read_sections(struct format_instance *fid,
|
||||
|
||||
static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
const struct dm_config_tree *cft,
|
||||
unsigned use_cached_pvs,
|
||||
unsigned allow_lvmetad_extensions)
|
||||
{
|
||||
const struct dm_config_node *vgn;
|
||||
@@ -995,6 +1012,7 @@ static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
const char *str, *format_str, *system_id;
|
||||
struct volume_group *vg;
|
||||
struct dm_hash_table *pv_hash = NULL, *lv_hash = NULL;
|
||||
unsigned scan_done_once = use_cached_pvs;
|
||||
uint64_t vgstatus;
|
||||
|
||||
/* skip any top-level values */
|
||||
@@ -1149,7 +1167,7 @@ static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
}
|
||||
|
||||
if (!_read_sections(fid, "physical_volumes", _read_pv, vg,
|
||||
vgn, pv_hash, lv_hash, 0)) {
|
||||
vgn, pv_hash, lv_hash, 0, &scan_done_once)) {
|
||||
log_error("Couldn't find all physical volumes for volume "
|
||||
"group %s.", vg->name);
|
||||
goto bad;
|
||||
@@ -1157,7 +1175,7 @@ static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
|
||||
if (allow_lvmetad_extensions)
|
||||
_read_sections(fid, "outdated_pvs", _read_pv, vg,
|
||||
vgn, pv_hash, lv_hash, 1);
|
||||
vgn, pv_hash, lv_hash, 1, &scan_done_once);
|
||||
else if (dm_config_has_node(vgn, "outdated_pvs"))
|
||||
log_error(INTERNAL_ERROR "Unexpected outdated_pvs section in metadata of VG %s.", vg->name);
|
||||
|
||||
@@ -1169,28 +1187,28 @@ static struct volume_group *_read_vg(struct format_instance *fid,
|
||||
}
|
||||
|
||||
if (!_read_sections(fid, "logical_volumes", _read_lvnames, vg,
|
||||
vgn, pv_hash, lv_hash, 1)) {
|
||||
vgn, pv_hash, lv_hash, 1, NULL)) {
|
||||
log_error("Couldn't read all logical volume names for volume "
|
||||
"group %s.", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_sections(fid, "historical_logical_volumes", _read_historical_lvnames, vg,
|
||||
vgn, pv_hash, lv_hash, 1)) {
|
||||
vgn, pv_hash, lv_hash, 1, NULL)) {
|
||||
log_error("Couldn't read all historical logical volumes for volume "
|
||||
"group %s.", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_sections(fid, "logical_volumes", _read_lvsegs, vg,
|
||||
vgn, pv_hash, lv_hash, 1)) {
|
||||
vgn, pv_hash, lv_hash, 1, NULL)) {
|
||||
log_error("Couldn't read all logical volumes for "
|
||||
"volume group %s.", vg->name);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_read_sections(fid, "historical_logical_volumes", _read_historical_lvnames_interconnections,
|
||||
vg, vgn, pv_hash, lv_hash, 1)) {
|
||||
vg, vgn, pv_hash, lv_hash, 1, NULL)) {
|
||||
log_error("Couldn't read all removed logical volume interconnections "
|
||||
"for volume group %s.", vg->name);
|
||||
goto bad;
|
||||
@@ -1292,12 +1310,6 @@ static int _read_vgsummary(const struct format_type *fmt, const struct dm_config
|
||||
(!(vgsummary->lock_type = dm_pool_strdup(mem, str))))
|
||||
return_0;
|
||||
|
||||
if (!_read_int32(vgn, "seqno", &vgsummary->seqno)) {
|
||||
log_error("Couldn't read seqno for volume group %s.",
|
||||
vgsummary->vgname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#define _LVM_TEXT_LAYOUT_H
|
||||
|
||||
#include "config.h"
|
||||
#include "format-text.h"
|
||||
#include "metadata.h"
|
||||
#include "lvmcache.h"
|
||||
#include "uuid.h"
|
||||
@@ -80,8 +81,9 @@ struct mda_header {
|
||||
struct raw_locn raw_locns[0]; /* NULL-terminated list */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct mda_header *raw_read_mda_header(const struct format_type *fmt,
|
||||
struct device_area *dev_area, int primary_mda);
|
||||
struct mda_header *raw_read_mda_header(struct dm_pool *mem, struct device_area *dev_area, int primary_mda);
|
||||
int raw_read_mda_header_callback(struct dm_pool *mem, struct device_area *dev_area, int primary_mda,
|
||||
unsigned ioflags, lvm_callback_fn_t mdah_callback_fn, void *mdah_callback_context);
|
||||
|
||||
struct mda_lists {
|
||||
struct dm_list dirs;
|
||||
@@ -103,9 +105,11 @@ struct mda_context {
|
||||
#define LVM2_LABEL "LVM2 001"
|
||||
#define MDA_SIZE_MIN (8 * (unsigned) lvm_getpagesize())
|
||||
#define MDA_ORIGINAL_ALIGNMENT 512 /* Original alignment used for start of VG metadata content */
|
||||
#define MDA_ALIGNMENT 4096 /* Default alignment in bytes since 2.02.177 for start of VG metadata content. */
|
||||
|
||||
int read_metadata_location_summary(const struct format_type *fmt, struct mda_header *mdah, int primary_mda,
|
||||
int vgname_from_mda(const struct format_type *fmt, const struct mda_header *mdah, int primary_mda,
|
||||
struct device_area *dev_area, struct lvmcache_vgsummary *vgsummary,
|
||||
uint64_t *mda_free_sectors);
|
||||
uint64_t *mda_free_sectors, unsigned ioflags,
|
||||
lvm_callback_fn_t update_vgsummary_callback_fn, void *update_vgsummary_callback_context);
|
||||
|
||||
#endif
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "label.h"
|
||||
#include "xlate.h"
|
||||
#include "lvmcache.h"
|
||||
#include "toolcontext.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -35,14 +36,14 @@ static int _text_can_handle(struct labeller *l __attribute__((unused)),
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct _dl_setup_baton {
|
||||
struct dl_setup_baton {
|
||||
struct disk_locn *pvh_dlocn_xl;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int _da_setup(struct disk_locn *da, void *baton)
|
||||
{
|
||||
struct _dl_setup_baton *p = baton;
|
||||
struct dl_setup_baton *p = baton;
|
||||
p->pvh_dlocn_xl->offset = xlate64(da->offset);
|
||||
p->pvh_dlocn_xl->size = xlate64(da->size);
|
||||
p->pvh_dlocn_xl++;
|
||||
@@ -56,7 +57,7 @@ static int _ba_setup(struct disk_locn *ba, void *baton)
|
||||
|
||||
static int _mda_setup(struct metadata_area *mda, void *baton)
|
||||
{
|
||||
struct _dl_setup_baton *p = baton;
|
||||
struct dl_setup_baton *p = baton;
|
||||
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
|
||||
|
||||
if (mdac->area.dev != p->dev)
|
||||
@@ -71,7 +72,7 @@ static int _mda_setup(struct metadata_area *mda, void *baton)
|
||||
|
||||
static int _dl_null_termination(void *baton)
|
||||
{
|
||||
struct _dl_setup_baton *p = baton;
|
||||
struct dl_setup_baton *p = baton;
|
||||
|
||||
p->pvh_dlocn_xl->offset = xlate64(UINT64_C(0));
|
||||
p->pvh_dlocn_xl->size = xlate64(UINT64_C(0));
|
||||
@@ -86,7 +87,7 @@ static int _text_write(struct label *label, void *buf)
|
||||
struct pv_header *pvhdr;
|
||||
struct pv_header_extension *pvhdr_ext;
|
||||
struct lvmcache_info *info;
|
||||
struct _dl_setup_baton baton;
|
||||
struct dl_setup_baton baton;
|
||||
char buffer[64] __attribute__((aligned(8)));
|
||||
int ba1, da1, mda1, mda2;
|
||||
|
||||
@@ -318,77 +319,165 @@ static int _text_initialise_label(struct labeller *l __attribute__((unused)),
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct _update_mda_baton {
|
||||
struct update_mda_baton {
|
||||
struct lvmcache_info *info;
|
||||
struct label *label;
|
||||
int nr_outstanding_mdas;
|
||||
unsigned ioflags;
|
||||
lvm_callback_fn_t read_label_callback_fn;
|
||||
void *read_label_callback_context;
|
||||
int ret;
|
||||
};
|
||||
|
||||
static int _read_mda_header_and_metadata(struct metadata_area *mda, void *baton)
|
||||
{
|
||||
struct _update_mda_baton *p = baton;
|
||||
const struct format_type *fmt = p->label->labeller->fmt;
|
||||
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
|
||||
struct mda_header *mdah;
|
||||
struct lvmcache_vgsummary vgsummary = { 0 };
|
||||
struct process_mda_header_params {
|
||||
struct update_mda_baton *umb;
|
||||
struct metadata_area *mda;
|
||||
struct device *dev;
|
||||
struct lvmcache_vgsummary vgsummary;
|
||||
int ret;
|
||||
};
|
||||
|
||||
if (!(mdah = raw_read_mda_header(fmt, &mdac->area, mda_is_primary(mda)))) {
|
||||
log_error("Failed to read mda header from %s", dev_name(mdac->area.dev));
|
||||
goto fail;
|
||||
static void _process_vgsummary(int failed, unsigned ioflags, void *context, const void *data)
|
||||
{
|
||||
struct process_mda_header_params *pmp = context;
|
||||
const struct lvmcache_vgsummary *vgsummary = data;
|
||||
|
||||
--pmp->umb->nr_outstanding_mdas;
|
||||
|
||||
/* FIXME Need to distinguish genuine errors here */
|
||||
if (failed)
|
||||
goto_out;
|
||||
|
||||
if (!lvmcache_update_vgname_and_id(pmp->umb->info, vgsummary)) {
|
||||
pmp->umb->ret = 0;
|
||||
pmp->ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (!pmp->umb->nr_outstanding_mdas && pmp->umb->ret)
|
||||
lvmcache_make_valid(pmp->umb->info);
|
||||
|
||||
if (!dev_close(pmp->dev))
|
||||
stack;
|
||||
|
||||
if (!pmp->umb->nr_outstanding_mdas && pmp->umb->read_label_callback_fn)
|
||||
pmp->umb->read_label_callback_fn(!pmp->umb->ret, ioflags, pmp->umb->read_label_callback_context, pmp->umb->label);
|
||||
}
|
||||
|
||||
static void _process_mda_header(int failed, unsigned ioflags, void *context, const void *data)
|
||||
{
|
||||
struct process_mda_header_params *pmp = context;
|
||||
const struct mda_header *mdah = data;
|
||||
struct update_mda_baton *umb = pmp->umb;
|
||||
const struct format_type *fmt = umb->label->labeller->fmt;
|
||||
struct metadata_area *mda = pmp->mda;
|
||||
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
|
||||
|
||||
if (failed)
|
||||
goto_bad;
|
||||
|
||||
mda_set_ignored(mda, rlocn_is_ignored(mdah->raw_locns));
|
||||
|
||||
if (mda_is_ignored(mda)) {
|
||||
log_debug_metadata("Ignoring mda on device %s at offset " FMTu64,
|
||||
dev_name(mdac->area.dev),
|
||||
mdac->area.start);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!vgname_from_mda(fmt, mdah, mda_is_primary(mda), &mdac->area, &pmp->vgsummary, &mdac->free_sectors, ioflags, _process_vgsummary, pmp)) {
|
||||
/* FIXME Separate fatal and non-fatal error cases? */
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
bad:
|
||||
_process_vgsummary(1, ioflags, pmp, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
static int _count_mda(struct metadata_area *mda, void *baton)
|
||||
{
|
||||
struct update_mda_baton *umb = baton;
|
||||
|
||||
umb->nr_outstanding_mdas++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _update_mda(struct metadata_area *mda, void *baton)
|
||||
{
|
||||
struct process_mda_header_params *pmp;
|
||||
struct update_mda_baton *umb = baton;
|
||||
const struct format_type *fmt = umb->label->labeller->fmt;
|
||||
struct dm_pool *mem = umb->label->labeller->fmt->cmd->mem;
|
||||
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
|
||||
unsigned ioflags = umb->ioflags;
|
||||
|
||||
if (!(pmp = dm_pool_zalloc(mem, sizeof(*pmp)))) {
|
||||
log_error("struct process_mda_header_params allocation failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Using the labeller struct to preserve info about
|
||||
* the last parsed vgname, vgid, creation host
|
||||
*
|
||||
* TODO: make lvmcache smarter and move this cache logic there
|
||||
*/
|
||||
|
||||
pmp->dev = mdac->area.dev;
|
||||
pmp->umb = umb;
|
||||
pmp->mda = mda;
|
||||
|
||||
if (!dev_open_readonly(mdac->area.dev)) {
|
||||
mda_set_ignored(mda, 1);
|
||||
stack;
|
||||
if (!--umb->nr_outstanding_mdas && umb->read_label_callback_fn)
|
||||
umb->read_label_callback_fn(!umb->ret, ioflags, umb->read_label_callback_context, umb->label);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!read_metadata_location_summary(fmt, mdah, mda_is_primary(mda), &mdac->area,
|
||||
&vgsummary, &mdac->free_sectors)) {
|
||||
if (vgsummary.zero_offset)
|
||||
return 1;
|
||||
pmp->ret = 1;
|
||||
|
||||
log_error("Failed to read metadata summary from %s", dev_name(mdac->area.dev));
|
||||
goto fail;
|
||||
if (!raw_read_mda_header_callback(fmt->cmd->mem, &mdac->area, mda_is_primary(mda), ioflags, _process_mda_header, pmp)) {
|
||||
_process_vgsummary(1, ioflags, pmp, NULL);
|
||||
stack;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!lvmcache_update_vgname_and_id(p->info, &vgsummary)) {
|
||||
log_error("Failed to save lvm summary for %s", dev_name(mdac->area.dev));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
lvmcache_del(p->info);
|
||||
return 0;
|
||||
if (umb->read_label_callback_fn)
|
||||
return 1;
|
||||
else
|
||||
return pmp->ret;
|
||||
}
|
||||
|
||||
static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
|
||||
struct label **label)
|
||||
static int _text_read(struct labeller *l, struct device *dev, void *buf, unsigned ioflags,
|
||||
lvm_callback_fn_t read_label_callback_fn, void *read_label_callback_context)
|
||||
{
|
||||
struct label_header *lh = (struct label_header *) label_buf;
|
||||
struct label_header *lh = (struct label_header *) buf;
|
||||
struct pv_header *pvhdr;
|
||||
struct pv_header_extension *pvhdr_ext;
|
||||
struct lvmcache_info *info;
|
||||
struct disk_locn *dlocn_xl;
|
||||
uint64_t offset;
|
||||
uint32_t ext_version;
|
||||
struct _update_mda_baton baton;
|
||||
struct dm_pool *mem = l->fmt->cmd->mem;
|
||||
struct update_mda_baton *umb;
|
||||
struct label *label;
|
||||
|
||||
/*
|
||||
* PV header base
|
||||
*/
|
||||
pvhdr = (struct pv_header *) ((char *) label_buf + xlate32(lh->offset_xl));
|
||||
pvhdr = (struct pv_header *) ((char *) buf + xlate32(lh->offset_xl));
|
||||
|
||||
if (!(info = lvmcache_add(l, (char *)pvhdr->pv_uuid, dev,
|
||||
FMT_TEXT_ORPHAN_VG_NAME,
|
||||
FMT_TEXT_ORPHAN_VG_NAME, 0)))
|
||||
return_0;
|
||||
goto_bad;
|
||||
|
||||
*label = lvmcache_get_label(info);
|
||||
label = lvmcache_get_label(info);
|
||||
|
||||
lvmcache_set_device_size(info, xlate64(pvhdr->device_size_xl));
|
||||
|
||||
@@ -434,23 +523,41 @@ static int _text_read(struct labeller *l, struct device *dev, void *label_buf,
|
||||
lvmcache_add_ba(info, offset, xlate64(dlocn_xl->size));
|
||||
dlocn_xl++;
|
||||
}
|
||||
out:
|
||||
baton.info = info;
|
||||
baton.label = *label;
|
||||
|
||||
/*
|
||||
* In the vg_read phase, we compare all mdas and decide which to use
|
||||
* which are bad and need repair.
|
||||
*
|
||||
* FIXME: this quits if the first mda is bad, but we need something
|
||||
* smarter to be able to use the second mda if it's good.
|
||||
*/
|
||||
if (!lvmcache_foreach_mda(info, _read_mda_header_and_metadata, &baton)) {
|
||||
log_error("Failed to scan VG from %s", dev_name(dev));
|
||||
return 0;
|
||||
out:
|
||||
if (!(umb = dm_pool_zalloc(mem, sizeof(*umb)))) {
|
||||
log_error("baton allocation failed");
|
||||
goto_bad;
|
||||
}
|
||||
|
||||
umb->info = info;
|
||||
umb->label = label;
|
||||
umb->ioflags = ioflags;
|
||||
umb->read_label_callback_fn = read_label_callback_fn;
|
||||
umb->read_label_callback_context = read_label_callback_context;
|
||||
|
||||
umb->ret = 1;
|
||||
|
||||
if (!lvmcache_foreach_mda(info, _count_mda, umb))
|
||||
goto_bad;
|
||||
|
||||
if (!umb->nr_outstanding_mdas) {
|
||||
lvmcache_make_valid(info);
|
||||
if (read_label_callback_fn)
|
||||
read_label_callback_fn(0, ioflags, read_label_callback_context, label);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!lvmcache_foreach_mda(info, _update_mda, umb))
|
||||
goto_bad;
|
||||
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
if (read_label_callback_fn)
|
||||
read_label_callback_fn(1, ioflags, read_label_callback_context, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _text_destroy_label(struct labeller *l __attribute__((unused)),
|
||||
|
1161
lib/label/label.c
1161
lib/label/label.c
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,6 @@
|
||||
|
||||
#include "uuid.h"
|
||||
#include "device.h"
|
||||
#include "bcache.h"
|
||||
#include "toolcontext.h"
|
||||
|
||||
#define LABEL_ID "LABELONE"
|
||||
#define LABEL_SIZE SECTOR_SIZE /* Think very carefully before changing this */
|
||||
@@ -64,8 +62,8 @@ struct label_ops {
|
||||
/*
|
||||
* Read a label from a volume.
|
||||
*/
|
||||
int (*read) (struct labeller * l, struct device * dev,
|
||||
void *label_buf, struct label ** label);
|
||||
int (*read) (struct labeller *l, struct device *dev, void *buf,
|
||||
unsigned ioflags, lvm_callback_fn_t label_read_callback_fn, void *label_read_callback_context);
|
||||
|
||||
/*
|
||||
* Populate label_type etc.
|
||||
@@ -96,33 +94,12 @@ int label_register_handler(struct labeller *handler);
|
||||
struct labeller *label_get_handler(const char *name);
|
||||
|
||||
int label_remove(struct device *dev);
|
||||
int label_read(struct device *dev, struct label **result,
|
||||
uint64_t scan_sector);
|
||||
int label_read_callback(struct device *dev, uint64_t scan_sector,
|
||||
unsigned ioflags, lvm_callback_fn_t process_label_data_fn, void *process_label_data_context);
|
||||
int label_write(struct device *dev, struct label *label);
|
||||
struct label *label_create(struct labeller *labeller);
|
||||
void label_destroy(struct label *label);
|
||||
|
||||
extern struct bcache *scan_bcache;
|
||||
|
||||
int label_scan(struct cmd_context *cmd);
|
||||
int label_scan_devs(struct cmd_context *cmd, struct dev_filter *f, struct dm_list *devs);
|
||||
int label_scan_devs_excl(struct dm_list *devs);
|
||||
void label_scan_invalidate(struct device *dev);
|
||||
void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv);
|
||||
void label_scan_drop(struct cmd_context *cmd);
|
||||
void label_scan_destroy(struct cmd_context *cmd);
|
||||
int label_read(struct device *dev);
|
||||
int label_read_sector(struct device *dev, uint64_t scan_sector);
|
||||
void label_scan_confirm(struct device *dev);
|
||||
int label_scan_setup_bcache(void);
|
||||
int label_scan_open(struct device *dev);
|
||||
int label_scan_open_excl(struct device *dev);
|
||||
|
||||
/*
|
||||
* Wrappers around bcache equivalents.
|
||||
* (these make it easier to disable bcache and revert to direct rw if needed)
|
||||
*/
|
||||
bool dev_read_bytes(struct device *dev, uint64_t start, size_t len, void *data);
|
||||
bool dev_write_bytes(struct device *dev, uint64_t start, size_t len, void *data);
|
||||
bool dev_write_zeros(struct device *dev, uint64_t start, size_t len);
|
||||
bool dev_set_bytes(struct device *dev, uint64_t start, size_t len, uint8_t val);
|
||||
|
||||
#endif
|
||||
|
@@ -211,10 +211,11 @@ static int _cluster_request(char clvmd_cmd, const char *node, void *data, int le
|
||||
|
||||
*num = 0;
|
||||
|
||||
if (_clvmd_sock == -1) {
|
||||
if ((_clvmd_sock = _open_local_sock(0)) == -1)
|
||||
return_0;
|
||||
}
|
||||
if (_clvmd_sock == -1)
|
||||
_clvmd_sock = _open_local_sock(0);
|
||||
|
||||
if (_clvmd_sock == -1)
|
||||
return 0;
|
||||
|
||||
/* 1 byte is used from struct clvm_header.args[1], so -> len - 1 */
|
||||
_build_header(head, clvmd_cmd, node, len - 1);
|
||||
@@ -223,7 +224,7 @@ static int _cluster_request(char clvmd_cmd, const char *node, void *data, int le
|
||||
status = _send_request(outbuf, sizeof(struct clvm_header) +
|
||||
strlen(head->node) + len - 1, &retbuf);
|
||||
if (!status)
|
||||
goto_out;
|
||||
goto out;
|
||||
|
||||
/* Count the number of responses we got */
|
||||
head = (struct clvm_header *) retbuf;
|
||||
@@ -244,7 +245,7 @@ static int _cluster_request(char clvmd_cmd, const char *node, void *data, int le
|
||||
if (!(rarray = dm_malloc(sizeof(lvm_response_t) * num_responses))) {
|
||||
errno = ENOMEM;
|
||||
status = 0;
|
||||
goto_out;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Unpack the response into an lvm_response_t array */
|
||||
@@ -266,7 +267,7 @@ static int _cluster_request(char clvmd_cmd, const char *node, void *data, int le
|
||||
dm_free(rarray);
|
||||
errno = ENOMEM;
|
||||
status = 0;
|
||||
goto_out;
|
||||
goto out;
|
||||
}
|
||||
|
||||
strcpy(rarray[i].response, inptr);
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include "defaults.h"
|
||||
#include "lvm-string.h"
|
||||
#include "lvm-flock.h"
|
||||
#include "lvmcache.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
@@ -59,8 +60,11 @@ static int _file_lock_resource(struct cmd_context *cmd, const char *resource,
|
||||
return_0;
|
||||
break;
|
||||
case LCK_VG:
|
||||
if (!strcmp(resource, VG_SYNC_NAMES))
|
||||
if (!strcmp(resource, VG_SYNC_NAMES)) {
|
||||
fs_unlock();
|
||||
} else if (strcmp(resource, VG_GLOBAL))
|
||||
/* Skip cache refresh for VG_GLOBAL - the caller handles it */
|
||||
lvmcache_drop_metadata(resource, 0);
|
||||
|
||||
/* LCK_CACHE does not require a real lock */
|
||||
if (flags & LCK_CACHE)
|
||||
|
@@ -252,7 +252,6 @@ static int _lock_vol(struct cmd_context *cmd, const char *resource,
|
||||
uint32_t lck_type = flags & LCK_TYPE_MASK;
|
||||
uint32_t lck_scope = flags & LCK_SCOPE_MASK;
|
||||
int ret = 0;
|
||||
const struct logical_volume *active_lv;
|
||||
|
||||
block_signals(flags);
|
||||
_lock_memory(cmd, lv_op);
|
||||
@@ -265,17 +264,7 @@ static int _lock_vol(struct cmd_context *cmd, const char *resource,
|
||||
}
|
||||
|
||||
if ((is_orphan_vg(resource) || is_global_vg(resource)) && (flags & LCK_CACHE)) {
|
||||
log_error(INTERNAL_ERROR "P_%s referenced.", resource);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* When trying activating component LV, make sure none of
|
||||
* sub component LV or LVs that are using it are active */
|
||||
if (lv && ((lck_type == LCK_READ) || (lck_type == LCK_EXCL)) &&
|
||||
((!lv_is_visible(lv) && (active_lv = lv_holder_is_active(lv))) ||
|
||||
(active_lv = lv_component_is_active(lv)))) {
|
||||
log_error("Activation of logical volume %s is prohibited while logical volume %s is active.",
|
||||
display_lvname(lv), display_lvname(active_lv));
|
||||
log_error(INTERNAL_ERROR "P_%s referenced", resource);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -347,13 +336,6 @@ int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags, const str
|
||||
!lvmcache_verify_lock_order(vol))
|
||||
return_0;
|
||||
|
||||
if ((flags == LCK_VG_DROP_CACHE) ||
|
||||
(strcmp(vol, VG_GLOBAL) && strcmp(vol, VG_SYNC_NAMES))) {
|
||||
/* Skip dropping cache for internal VG names #global, #sync_names */
|
||||
log_debug_locking("Dropping cache for %s.", vol);
|
||||
lvmcache_drop_metadata(vol, 0);
|
||||
}
|
||||
|
||||
/* Lock VG to change on-disk metadata. */
|
||||
/* If LVM1 driver knows about the VG, it can't be accessed. */
|
||||
if (!check_lvm1_vg_inactive(cmd, vol))
|
||||
@@ -369,10 +351,8 @@ int lock_vol(struct cmd_context *cmd, const char *vol, uint32_t flags, const str
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dm_strncpy(resource, vol, sizeof(resource))) {
|
||||
log_error(INTERNAL_ERROR "Resource name %s is too long.", vol);
|
||||
return 0;
|
||||
}
|
||||
strncpy(resource, vol, sizeof(resource) - 1);
|
||||
resource[sizeof(resource) - 1] = '\0';
|
||||
|
||||
if (!_lock_vol(cmd, resource, flags, lv_op, lv))
|
||||
return_0;
|
||||
|
@@ -20,6 +20,7 @@
|
||||
|
||||
static daemon_handle _lvmlockd;
|
||||
static const char *_lvmlockd_socket = NULL;
|
||||
static struct cmd_context *_lvmlockd_cmd = NULL;
|
||||
static int _use_lvmlockd = 0; /* is 1 if command is configured to use lvmlockd */
|
||||
static int _lvmlockd_connected = 0; /* is 1 if command is connected to lvmlockd */
|
||||
static int _lvmlockd_init_failed = 0; /* used to suppress further warnings */
|
||||
@@ -68,6 +69,8 @@ void lvmlockd_init(struct cmd_context *cmd)
|
||||
} else {
|
||||
_lvmlockd_init_failed = 0;
|
||||
}
|
||||
|
||||
_lvmlockd_cmd = cmd;
|
||||
}
|
||||
|
||||
void lvmlockd_connect(void)
|
||||
@@ -100,6 +103,7 @@ void lvmlockd_disconnect(void)
|
||||
if (_lvmlockd_connected)
|
||||
daemon_close(_lvmlockd);
|
||||
_lvmlockd_connected = 0;
|
||||
_lvmlockd_cmd = NULL;
|
||||
}
|
||||
|
||||
/* Translate the result strings from lvmlockd to bit flags. */
|
||||
@@ -403,17 +407,22 @@ static int _extend_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!label_scan_open(dev)) {
|
||||
if (!dev_open_quiet(dev)) {
|
||||
log_error("Extend sanlock LV %s cannot open device.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_write_zeros(dev, old_size_bytes, new_size_bytes - old_size_bytes)) {
|
||||
if (!dev_set(dev, old_size_bytes, new_size_bytes - old_size_bytes, DEV_IO_LV, 0)) {
|
||||
log_error("Extend sanlock LV %s cannot zero device.", display_lvname(lv));
|
||||
dev_close_immediate(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
label_scan_invalidate(dev);
|
||||
dev_flush(dev);
|
||||
|
||||
if (!dev_close_immediate(dev))
|
||||
stack;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -376,8 +376,7 @@ void fin_syslog(void)
|
||||
void init_msg_prefix(const char *prefix)
|
||||
{
|
||||
if (prefix)
|
||||
/* Cut away too long prefix */
|
||||
(void) dm_strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
|
||||
dm_strncpy(_msg_prefix, prefix, sizeof(_msg_prefix));
|
||||
}
|
||||
|
||||
void init_indent(int indent)
|
||||
|
@@ -184,7 +184,7 @@ int update_cache_pool_params(struct cmd_context *cmd,
|
||||
* keep user informed he might be using things in untintended direction
|
||||
*/
|
||||
log_print_unless_silent("Using %s chunk size instead of default %s, "
|
||||
"so cache pool has less than " FMTu64 " chunks.",
|
||||
"so cache pool has less then " FMTu64 " chunks.",
|
||||
display_size(cmd, min_chunk_size),
|
||||
display_size(cmd, *chunk_size),
|
||||
max_chunks);
|
||||
@@ -193,7 +193,7 @@ int update_cache_pool_params(struct cmd_context *cmd,
|
||||
log_verbose("Setting chunk size to %s.",
|
||||
display_size(cmd, *chunk_size));
|
||||
} else if (*chunk_size < min_chunk_size) {
|
||||
log_error("Chunk size %s is less than required minimal chunk size %s "
|
||||
log_error("Chunk size %s is less then required minimal chunk size %s "
|
||||
"for a cache pool of %s size and limit " FMTu64 " chunks.",
|
||||
display_size(cmd, *chunk_size),
|
||||
display_size(cmd, min_chunk_size),
|
||||
@@ -540,7 +540,7 @@ int lv_cache_remove(struct logical_volume *cache_lv)
|
||||
}
|
||||
|
||||
if (lv_is_pending_delete(cache_lv)) {
|
||||
log_debug(INTERNAL_ERROR "LV %s is already dropped cache volume.",
|
||||
log_error(INTERNAL_ERROR "LV %s is already dropped cache volume.",
|
||||
display_lvname(cache_lv));
|
||||
goto remove; /* Already dropped */
|
||||
}
|
||||
@@ -548,8 +548,11 @@ int lv_cache_remove(struct logical_volume *cache_lv)
|
||||
/* Localy active volume is needed for writeback */
|
||||
if (!lv_info(cache_lv->vg->cmd, cache_lv, 1, NULL, 0, 0)) {
|
||||
/* Give up any remote locks */
|
||||
if (!deactivate_lv_with_sub_lv(cache_lv))
|
||||
return_0;
|
||||
if (!deactivate_lv(cache_lv->vg->cmd, cache_lv)) {
|
||||
log_error("Cannot deactivate remotely active cache volume %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (first_seg(cache_seg->pool_lv)->cache_mode) {
|
||||
case CACHE_MODE_WRITETHROUGH:
|
||||
@@ -841,18 +844,6 @@ int cache_set_metadata_format(struct lv_segment *seg, cache_metadata_format_t fo
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If policy is unselected, but format 2 is selected, policy smq is enforced.
|
||||
* ATM no other then smq policy is allowed to select format 2.
|
||||
*/
|
||||
if (!seg->policy_name) {
|
||||
if (format == CACHE_METADATA_FORMAT_2)
|
||||
seg->policy_name = "smq";
|
||||
} else if (strcmp(seg->policy_name, "smq")) {
|
||||
seg->cache_metadata_format = CACHE_METADATA_FORMAT_1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check if we need to search for configured cache metadata format */
|
||||
if (format == CACHE_METADATA_FORMAT_UNSELECTED) {
|
||||
if (seg->cache_metadata_format != CACHE_METADATA_FORMAT_UNSELECTED)
|
||||
@@ -906,15 +897,15 @@ int cache_set_params(struct lv_segment *seg,
|
||||
struct lv_segment *pool_seg;
|
||||
struct cmd_context *cmd = seg->lv->vg->cmd;
|
||||
|
||||
if (!cache_set_metadata_format(seg, format))
|
||||
return_0;
|
||||
|
||||
if (!cache_set_cache_mode(seg, mode))
|
||||
return_0;
|
||||
|
||||
if (!cache_set_policy(seg, policy_name, policy_settings))
|
||||
return_0;
|
||||
|
||||
if (!cache_set_metadata_format(seg, format))
|
||||
return_0;
|
||||
|
||||
pool_seg = seg_is_cache(seg) ? first_seg(seg->pool_lv) : seg;
|
||||
|
||||
if (chunk_size) {
|
||||
|
@@ -298,7 +298,7 @@ char *lvseg_monitor_dup(struct dm_pool *mem, const struct lv_segment *seg)
|
||||
|
||||
#ifdef DMEVENTD
|
||||
struct lvinfo info;
|
||||
int pending = 0, monitored = 0;
|
||||
int pending = 0, monitored;
|
||||
struct lv_segment *segm = (struct lv_segment *) seg;
|
||||
|
||||
if (lv_is_cow(seg->lv) && !lv_is_merging_cow(seg->lv))
|
||||
@@ -314,13 +314,11 @@ char *lvseg_monitor_dup(struct dm_pool *mem, const struct lv_segment *seg)
|
||||
else if (!seg_monitored(segm) || (segm->status & PVMOVE))
|
||||
s = "not monitored";
|
||||
else if (lv_info(seg->lv->vg->cmd, seg->lv, 1, &info, 0, 0) && info.exists) {
|
||||
if (segm->segtype->ops->target_monitored(segm, &pending, &monitored)) {
|
||||
if (pending)
|
||||
s = "pending";
|
||||
else
|
||||
s = (monitored) ? "monitored" : "not monitored";
|
||||
} else
|
||||
s = "not monitored";
|
||||
monitored = segm->segtype->ops->target_monitored(segm, &pending);
|
||||
if (pending)
|
||||
s = "pending";
|
||||
else
|
||||
s = (monitored) ? "monitored" : "not monitored";
|
||||
} // else log_debug("Not active");
|
||||
#endif
|
||||
return dm_pool_strdup(mem, s);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2018 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2017 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "lvm-exec.h"
|
||||
#include "memlock.h"
|
||||
#include "lvmlockd.h"
|
||||
#include "label.h"
|
||||
|
||||
typedef enum {
|
||||
PREFERRED,
|
||||
@@ -1050,13 +1049,12 @@ static int _release_and_discard_lv_segment_area(struct lv_segment *seg, uint32_t
|
||||
}
|
||||
|
||||
if (lv_is_raid_image(lv)) {
|
||||
/* Calculate the amount of extents to reduce per rmeta/rimage LV */
|
||||
/* Calculate the amount of extents to reduce per rmate/rimage LV */
|
||||
uint32_t rimage_extents;
|
||||
struct lv_segment *seg1 = first_seg(lv);
|
||||
|
||||
/* FIXME: avoid extra seg_is_*() conditionals here */
|
||||
rimage_extents = raid_rimage_extents(seg1->segtype, area_reduction,
|
||||
seg_is_any_raid0(seg) ? 0 : _raid_stripes_count(seg),
|
||||
rimage_extents = raid_rimage_extents(seg1->segtype, area_reduction, seg_is_any_raid0(seg) ? 0 : _raid_stripes_count(seg),
|
||||
seg_is_raid10(seg) ? 1 :_raid_data_copies(seg));
|
||||
if (!rimage_extents)
|
||||
return 0;
|
||||
@@ -1519,13 +1517,6 @@ int lv_reduce(struct logical_volume *lv, uint32_t extents)
|
||||
if (lv_is_raid(lv) && extents != lv->le_count)
|
||||
extents =_round_to_stripe_boundary(lv->vg, extents,
|
||||
seg_is_raid1(seg) ? 0 : _raid_stripes_count(seg), 0);
|
||||
|
||||
if ((extents == lv->le_count) && lv_is_component(lv) && lv_is_active(lv)) {
|
||||
/* When LV is removed, make sure it is inactive */
|
||||
log_error(INTERNAL_ERROR "Removing still active LV %s.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _lv_reduce(lv, extents, 1);
|
||||
}
|
||||
|
||||
@@ -1668,9 +1659,9 @@ struct alloc_handle {
|
||||
#define BYTE_SHIFT 3
|
||||
static uint32_t _mirror_log_extents(uint32_t region_size, uint32_t pe_size, uint32_t area_len)
|
||||
{
|
||||
uint64_t area_size, region_count, bitset_size, log_size;
|
||||
size_t area_size, bitset_size, log_size, region_count;
|
||||
|
||||
area_size = (uint64_t) area_len * pe_size;
|
||||
area_size = (size_t)area_len * pe_size;
|
||||
region_count = dm_div_up(area_size, region_size);
|
||||
|
||||
/* Work out how many "unsigned long"s we need to hold the bitset. */
|
||||
@@ -1682,14 +1673,19 @@ static uint32_t _mirror_log_extents(uint32_t region_size, uint32_t pe_size, uint
|
||||
log_size >>= SECTOR_SHIFT;
|
||||
log_size = dm_div_up(log_size, pe_size);
|
||||
|
||||
if (log_size > UINT32_MAX) {
|
||||
log_error("Log size needs too many extents "FMTu64" with region size of %u sectors.",
|
||||
log_size, region_size);
|
||||
log_size = UINT32_MAX;
|
||||
/* VG likely will not have enough free space for this allocation -> error */
|
||||
}
|
||||
|
||||
return (uint32_t) log_size;
|
||||
/*
|
||||
* Kernel requires a mirror to be at least 1 region large. So,
|
||||
* if our mirror log is itself a mirror, it must be at least
|
||||
* 1 region large. This restriction may not be necessary for
|
||||
* non-mirrored logs, but we apply the rule anyway.
|
||||
*
|
||||
* (The other option is to make the region size of the log
|
||||
* mirror smaller than the mirror it is acting as a log for,
|
||||
* but that really complicates things. It's much easier to
|
||||
* keep the region_size the same for both.)
|
||||
*/
|
||||
return (log_size > (region_size / pe_size)) ? log_size :
|
||||
(region_size / pe_size);
|
||||
}
|
||||
|
||||
/* Is there enough total space or should we give up immediately? */
|
||||
@@ -3440,12 +3436,11 @@ static struct alloc_handle *_alloc_init(struct cmd_context *cmd,
|
||||
(existing_extents + new_extents) / ah->area_multiple);
|
||||
}
|
||||
|
||||
if (total_extents || existing_extents)
|
||||
log_debug("Adjusted allocation request to " FMTu32 " logical extents. Existing size " FMTu32 ". New size " FMTu32 ".",
|
||||
total_extents, existing_extents, total_extents + existing_extents);
|
||||
log_debug("Adjusted allocation request to %" PRIu32 " logical extents. Existing size %" PRIu32 ". New size %" PRIu32 ".",
|
||||
total_extents, existing_extents, total_extents + existing_extents);
|
||||
if (ah->log_len)
|
||||
log_debug("Mirror log of " FMTu32 " extents of size " FMTu32 " sectors needed for region size %s.",
|
||||
ah->log_len, extent_size, display_size(cmd, (uint64_t)ah->region_size));
|
||||
log_debug("Mirror log of %" PRIu32 " extents of size %" PRIu32 " sectors needed for region size %" PRIu32 ".",
|
||||
ah->log_len, extent_size, ah->region_size);
|
||||
|
||||
if (mirrors || stripes)
|
||||
total_extents += existing_extents;
|
||||
@@ -3806,7 +3801,7 @@ int lv_add_mirror_lvs(struct logical_volume *lv,
|
||||
return_0;
|
||||
|
||||
if (region_size && region_size != seg->region_size) {
|
||||
log_error("Conflicting region_size %u != %u.", region_size, seg->region_size);
|
||||
log_error("Conflicting region_size.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4097,16 +4092,9 @@ static int _lv_extend_layered_lv(struct alloc_handle *ah,
|
||||
str_list_wipe(&meta_lv->tags);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
if (fail)
|
||||
/* Fail, after trying to deactivate all we could */
|
||||
struct volume_group *vg = lv->vg;
|
||||
|
||||
if (!lv_remove(lv))
|
||||
log_error("Failed to remove LV");
|
||||
else if (!vg_write(vg) || !vg_commit(vg))
|
||||
log_error("Failed to commit VG %s", vg->name);
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
|
||||
for (s = 0; s < seg->area_count; s++)
|
||||
@@ -4189,7 +4177,7 @@ int lv_extend(struct logical_volume *lv,
|
||||
if (segtype_is_pool(segtype)) {
|
||||
if (!(r = create_pool(lv, segtype, ah, stripes, stripe_size)))
|
||||
stack;
|
||||
} else if (!segtype_is_mirror(segtype) && !segtype_is_raid(segtype)) {
|
||||
} else if (!segtype_is_mirrored(segtype) && !segtype_is_raid(segtype)) {
|
||||
if (!(r = lv_add_segment(ah, 0, ah->area_count, lv, segtype,
|
||||
stripe_size, 0u, 0)))
|
||||
stack;
|
||||
@@ -4353,62 +4341,53 @@ static int _rename_cb(struct logical_volume *lv, void *data)
|
||||
return _rename_sub_lv(lv, lv_names->old, lv_names->new);
|
||||
}
|
||||
|
||||
static int _rename_skip_pools_externals_cb(struct logical_volume *lv, void *data)
|
||||
{
|
||||
if (lv_is_pool(lv) || lv_is_external_origin(lv))
|
||||
return -1; /* and skip subLVs */
|
||||
|
||||
return _rename_cb(lv, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop down sub LVs and call fn for each.
|
||||
* fn is responsible to log necessary information on failure.
|
||||
* Return value '0' stops whole traversal.
|
||||
* Return value '-1' stops subtree traversal.
|
||||
*/
|
||||
static int _for_each_sub_lv(struct logical_volume *lv, int level,
|
||||
static int _for_each_sub_lv(struct logical_volume *lv, int skip_pools,
|
||||
int (*fn)(struct logical_volume *lv, void *data),
|
||||
void *data)
|
||||
{
|
||||
struct logical_volume *org;
|
||||
struct lv_segment *seg;
|
||||
uint32_t s;
|
||||
int r;
|
||||
|
||||
if (!lv)
|
||||
return 1;
|
||||
|
||||
if (level++) {
|
||||
if (!(r = fn(lv, data)))
|
||||
return_0;
|
||||
if (r == -1)
|
||||
return 1;
|
||||
/* Only r != -1 continues with for_each_sub_lv()... */
|
||||
}
|
||||
|
||||
if (lv_is_cow(lv) && lv_is_virtual_origin(org = origin_from_cow(lv))) {
|
||||
if (!_for_each_sub_lv(org, level, fn, data))
|
||||
if (!fn(org, data))
|
||||
return_0;
|
||||
if (!_for_each_sub_lv(org, skip_pools, fn, data))
|
||||
return_0;
|
||||
}
|
||||
|
||||
dm_list_iterate_items(seg, &lv->segments) {
|
||||
if (!_for_each_sub_lv(seg->external_lv, level, fn, data))
|
||||
return_0;
|
||||
if (seg->log_lv) {
|
||||
if (!fn(seg->log_lv, data))
|
||||
return_0;
|
||||
if (!_for_each_sub_lv(seg->log_lv, skip_pools, fn, data))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!_for_each_sub_lv(seg->log_lv, level, fn, data))
|
||||
return_0;
|
||||
if (seg->metadata_lv) {
|
||||
if (!fn(seg->metadata_lv, data))
|
||||
return_0;
|
||||
if (!_for_each_sub_lv(seg->metadata_lv, skip_pools, fn, data))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!_for_each_sub_lv(seg->metadata_lv, level, fn, data))
|
||||
return_0;
|
||||
|
||||
if (!_for_each_sub_lv(seg->pool_lv, level, fn, data))
|
||||
return_0;
|
||||
if (seg->pool_lv && !skip_pools) {
|
||||
if (!fn(seg->pool_lv, data))
|
||||
return_0;
|
||||
if (!_for_each_sub_lv(seg->pool_lv, skip_pools, fn, data))
|
||||
return_0;
|
||||
}
|
||||
|
||||
for (s = 0; s < seg->area_count; s++) {
|
||||
if (seg_type(seg, s) != AREA_LV)
|
||||
continue;
|
||||
if (!_for_each_sub_lv(seg_lv(seg, s), level, fn, data))
|
||||
if (!fn(seg_lv(seg, s), data))
|
||||
return_0;
|
||||
if (!_for_each_sub_lv(seg_lv(seg, s), skip_pools, fn, data))
|
||||
return_0;
|
||||
}
|
||||
|
||||
@@ -4419,7 +4398,9 @@ static int _for_each_sub_lv(struct logical_volume *lv, int level,
|
||||
for (s = 0; s < seg->area_count; s++) {
|
||||
if ((seg_metatype(seg, s) != AREA_LV) || !seg_metalv(seg, s))
|
||||
continue;
|
||||
if (!_for_each_sub_lv(seg_metalv(seg, s), level, fn, data))
|
||||
if (!fn(seg_metalv(seg, s), data))
|
||||
return_0;
|
||||
if (!_for_each_sub_lv(seg_metalv(seg, s), skip_pools, fn, data))
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
@@ -4434,6 +4415,13 @@ int for_each_sub_lv(struct logical_volume *lv,
|
||||
return _for_each_sub_lv(lv, 0, fn, data);
|
||||
}
|
||||
|
||||
int for_each_sub_lv_except_pools(struct logical_volume *lv,
|
||||
int (*fn)(struct logical_volume *lv, void *data),
|
||||
void *data)
|
||||
{
|
||||
return _for_each_sub_lv(lv, 1, fn, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Core of LV renaming routine.
|
||||
* VG must be locked by caller.
|
||||
@@ -4487,7 +4475,7 @@ int lv_rename_update(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
}
|
||||
|
||||
/* rename sub LVs */
|
||||
if (!for_each_sub_lv(lv, _rename_skip_pools_externals_cb, (void *) &lv_names))
|
||||
if (!for_each_sub_lv_except_pools(lv, _rename_cb, (void *) &lv_names))
|
||||
return_0;
|
||||
|
||||
/* rename main LV */
|
||||
@@ -4876,12 +4864,6 @@ static int _lvresize_check(struct logical_volume *lv,
|
||||
lp->mirrors = 0;
|
||||
}
|
||||
|
||||
if (lv_component_is_active(lv)) {
|
||||
log_error("Cannot resize logical volume %s with active component LV(s).",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -5352,7 +5334,7 @@ static int _lvresize_check_type(const struct logical_volume *lv,
|
||||
|
||||
/* Validate thin target supports bigger size of thin volume then external origin */
|
||||
if (lv_is_thin_volume(lv) && first_seg(lv)->external_lv &&
|
||||
(lp->extents > first_seg(lv)->external_lv->le_count) &&
|
||||
(lv->size > first_seg(lv)->external_lv->size) &&
|
||||
!thin_pool_feature_supported(first_seg(lv)->pool_lv, THIN_FEATURE_EXTERNAL_ORIGIN_EXTEND)) {
|
||||
log_error("Thin target does not support external origin smaller then thin volume.");
|
||||
return 0;
|
||||
@@ -6051,13 +6033,15 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
/* FIXME Ensure not referred to by another existing LVs */
|
||||
ask_discard = find_config_tree_bool(cmd, devices_issue_discards_CFG, NULL);
|
||||
|
||||
if (lv_is_active(lv)) {
|
||||
if (!lv_is_cache_pool(lv) && /* cache pool cannot be active */
|
||||
lv_is_active(lv)) {
|
||||
if (!lv_check_not_in_use(lv, 1))
|
||||
return_0;
|
||||
|
||||
if ((force == PROMPT) &&
|
||||
!lv_is_pending_delete(lv) &&
|
||||
lv_is_visible(lv)) {
|
||||
lv_is_visible(lv) &&
|
||||
lv_is_active(lv)) {
|
||||
if (yes_no_prompt("Do you really want to remove%s active "
|
||||
"%slogical volume %s? [y/n]: ",
|
||||
ask_discard ? " and DISCARD" : "",
|
||||
@@ -6093,16 +6077,6 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
is_last_pool = 1;
|
||||
}
|
||||
|
||||
/* Used cache pool, COW or historical LV cannot be activated */
|
||||
if (!lv_is_used_cache_pool(lv) &&
|
||||
!lv_is_cow(lv) && !lv_is_historical(lv) &&
|
||||
!deactivate_lv_with_sub_lv(lv))
|
||||
/* FIXME Review and fix the snapshot error paths! */
|
||||
return_0;
|
||||
|
||||
if (!archive(vg))
|
||||
return 0;
|
||||
|
||||
/* Special case removing a striped raid LV with allocated reshape space */
|
||||
if (seg && seg->reshape_len) {
|
||||
if (!(seg->segtype = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED)))
|
||||
@@ -6110,6 +6084,19 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
lv->le_count = seg->len = seg->area_len = seg_lv(seg, 0)->le_count * seg->area_count;
|
||||
}
|
||||
|
||||
/* Used cache pool, COW or historical LV cannot be activated */
|
||||
if ((!lv_is_cache_pool(lv) || dm_list_empty(&lv->segs_using_this_lv)) &&
|
||||
!lv_is_cow(lv) && !lv_is_historical(lv) &&
|
||||
!deactivate_lv(cmd, lv)) {
|
||||
/* FIXME Review and fix the snapshot error paths! */
|
||||
log_error("Unable to deactivate logical volume %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!archive(vg))
|
||||
return 0;
|
||||
|
||||
/* Clear thin pool stacked messages */
|
||||
if (pool_lv && !pool_has_message(first_seg(pool_lv), lv, 0) &&
|
||||
!update_pool_lv(pool_lv, 1)) {
|
||||
@@ -6148,14 +6135,16 @@ int lv_remove_single(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
}
|
||||
}
|
||||
|
||||
if (lv_is_used_cache_pool(lv)) {
|
||||
if (lv_is_cache_pool(lv)) {
|
||||
/* Cache pool removal drops cache layer
|
||||
* If the cache pool is not linked, we can simply remove it. */
|
||||
if (!(cache_seg = get_only_segment_using_this_lv(lv)))
|
||||
return_0;
|
||||
/* TODO: polling */
|
||||
if (!lv_cache_remove(cache_seg->lv))
|
||||
return_0;
|
||||
if (!dm_list_empty(&lv->segs_using_this_lv)) {
|
||||
if (!(cache_seg = get_only_segment_using_this_lv(lv)))
|
||||
return_0;
|
||||
/* TODO: polling */
|
||||
if (!lv_cache_remove(cache_seg->lv))
|
||||
return_0;
|
||||
}
|
||||
}
|
||||
|
||||
visible = lv_is_visible(lv);
|
||||
@@ -7151,12 +7140,6 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp)
|
||||
/* nothing to do */
|
||||
return 1;
|
||||
|
||||
if (!lv_is_active_locally(lv)) {
|
||||
log_error("Volume \"%s/%s\" is not active locally (volume_list activation filter?).",
|
||||
lv->vg->name, lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Wait until devices are available */
|
||||
if (!sync_local_dev_names(lv->vg->cmd)) {
|
||||
log_error("Failed to sync local devices before wiping LV %s.",
|
||||
@@ -7164,6 +7147,12 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_is_active_locally(lv)) {
|
||||
log_error("Volume \"%s/%s\" is not active locally.",
|
||||
lv->vg->name, lv->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME:
|
||||
* <clausen> also, more than 4k
|
||||
@@ -7182,10 +7171,8 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!label_scan_open(dev)) {
|
||||
log_error("Failed to open %s/%s for wiping and zeroing.", lv->vg->name, lv->name);
|
||||
goto out;
|
||||
}
|
||||
if (!dev_open_quiet(dev))
|
||||
return_0;
|
||||
|
||||
if (wp.do_wipe_signatures) {
|
||||
log_verbose("Wiping known signatures on logical volume \"%s/%s\"",
|
||||
@@ -7206,17 +7193,15 @@ int wipe_lv(struct logical_volume *lv, struct wipe_params wp)
|
||||
display_size(lv->vg->cmd, zero_sectors),
|
||||
lv->vg->name, lv->name, wp.zero_value);
|
||||
|
||||
if (!wp.zero_value) {
|
||||
if (!dev_write_zeros(dev, UINT64_C(0), (size_t) zero_sectors << SECTOR_SHIFT))
|
||||
stack;
|
||||
} else {
|
||||
if (!dev_set_bytes(dev, UINT64_C(0), (size_t) zero_sectors << SECTOR_SHIFT, (uint8_t)wp.zero_value))
|
||||
stack;
|
||||
}
|
||||
if (!dev_set(dev, UINT64_C(0), (size_t) zero_sectors << SECTOR_SHIFT, DEV_IO_LV, wp.zero_value))
|
||||
stack;
|
||||
}
|
||||
|
||||
label_scan_invalidate(dev);
|
||||
out:
|
||||
dev_flush(dev);
|
||||
|
||||
if (!dev_close_immediate(dev))
|
||||
stack;
|
||||
|
||||
lv->status &= ~LV_NOSCAN;
|
||||
|
||||
return 1;
|
||||
@@ -7343,7 +7328,7 @@ static int _vg_check_features(struct volume_group *vg,
|
||||
if (!(features & FMT_SEGMENTS) &&
|
||||
(seg_is_cache(lp) ||
|
||||
seg_is_cache_pool(lp) ||
|
||||
seg_is_mirror(lp) ||
|
||||
seg_is_mirrored(lp) ||
|
||||
seg_is_raid(lp) ||
|
||||
seg_is_thin(lp))) {
|
||||
log_error("Metadata does not support %s segments.",
|
||||
@@ -7392,7 +7377,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
struct logical_volume *lv, *origin_lv = NULL;
|
||||
struct logical_volume *pool_lv = NULL;
|
||||
struct logical_volume *tmp_lv;
|
||||
const struct logical_volume *lock_lv;
|
||||
struct lv_segment *seg, *pool_seg;
|
||||
int thin_pool_was_active = -1; /* not scanned, inactive, active */
|
||||
int historical;
|
||||
@@ -7556,12 +7540,11 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
}
|
||||
|
||||
/* Validate cache origin is exclusively active */
|
||||
lock_lv = lv_lock_holder(origin_lv);
|
||||
if (vg_is_clustered(origin_lv->vg) &&
|
||||
locking_is_clustered() &&
|
||||
locking_supports_remote_queries() &&
|
||||
lv_is_active(lock_lv) &&
|
||||
!lv_is_active_exclusive(lock_lv)) {
|
||||
lv_is_active(origin_lv) &&
|
||||
!lv_is_active_exclusive(origin_lv)) {
|
||||
log_error("Cannot cache not exclusively active origin volume %s.",
|
||||
display_lvname(origin_lv));
|
||||
return NULL;
|
||||
@@ -7588,13 +7571,6 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(lp->region_size = adjusted_mirror_region_size(vg->cmd,
|
||||
vg->extent_size,
|
||||
lp->extents,
|
||||
lp->region_size, 0,
|
||||
vg_is_clustered(vg))))
|
||||
return_NULL;
|
||||
|
||||
/* FIXME This will not pass cluster lock! */
|
||||
init_mirror_in_sync(lp->nosync);
|
||||
|
||||
@@ -7604,6 +7580,19 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
lp->segtype->name);
|
||||
status |= LV_NOTSYNCED;
|
||||
}
|
||||
|
||||
if (seg_is_raid(lp)) {
|
||||
/* Value raid target constraint */
|
||||
if (lp->region_size > (uint64_t)vg->extent_size * lp->extents) {
|
||||
log_error("Cannot create RAID LV with region size larger than LV size.");
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
lp->region_size = adjusted_mirror_region_size(vg->cmd,
|
||||
vg->extent_size,
|
||||
lp->extents,
|
||||
lp->region_size, 0,
|
||||
vg_is_clustered(vg));
|
||||
} else if (pool_lv && seg_is_thin_volume(lp)) {
|
||||
if (!lv_is_thin_pool(pool_lv)) {
|
||||
log_error("Logical volume %s is not a thin pool.",
|
||||
@@ -7784,7 +7773,8 @@ static struct logical_volume *_lv_create_an_lv(struct volume_group *vg,
|
||||
return_NULL;
|
||||
|
||||
/* FIXME Log allocation and attachment should have happened inside lv_extend. */
|
||||
if (lp->log_count && segtype_is_mirror(create_segtype)) {
|
||||
if (lp->log_count &&
|
||||
!seg_is_raid(first_seg(lv)) && seg_is_mirrored(first_seg(lv))) {
|
||||
if (!add_mirror_log(cmd, lv, lp->log_count,
|
||||
first_seg(lv)->region_size,
|
||||
lp->pvh, lp->alloc)) {
|
||||
|
@@ -71,12 +71,12 @@ int lv_merge_segments(struct logical_volume *lv)
|
||||
if (error_count++ > ERROR_MAX) \
|
||||
goto out
|
||||
|
||||
#define seg_error(msg) do { \
|
||||
#define seg_error(msg) { \
|
||||
log_error("LV %s, segment %u invalid: %s for %s segment.", \
|
||||
seg->lv->name, seg_count, (msg), lvseg_name(seg)); \
|
||||
if ((*error_count)++ > ERROR_MAX) \
|
||||
return; \
|
||||
} while (0)
|
||||
}
|
||||
|
||||
/*
|
||||
* RAID segment property checks.
|
||||
@@ -84,19 +84,19 @@ int lv_merge_segments(struct logical_volume *lv)
|
||||
* Checks in here shall catch any
|
||||
* bogus segment structure setup.
|
||||
*/
|
||||
#define raid_seg_error(msg) do { \
|
||||
#define raid_seg_error(msg) { \
|
||||
log_error("LV %s invalid: %s for %s segment", \
|
||||
seg->lv->name, (msg), lvseg_name(seg)); \
|
||||
if ((*error_count)++ > ERROR_MAX) \
|
||||
return; \
|
||||
} while (0)
|
||||
}
|
||||
|
||||
#define raid_seg_error_val(msg, val) do { \
|
||||
#define raid_seg_error_val(msg, val) { \
|
||||
log_error("LV %s invalid: %s (is %u) for %s segment", \
|
||||
seg->lv->name, (msg), (val), lvseg_name(seg)); \
|
||||
if ((*error_count)++ > ERROR_MAX) \
|
||||
return; \
|
||||
} while(0)
|
||||
}
|
||||
|
||||
/* Check segment LV for reshape flags. */
|
||||
static int _check_raid_seg_reshape_flags(struct lv_segment *seg)
|
||||
@@ -354,7 +354,7 @@ static void _check_lv_segment(struct logical_volume *lv, struct lv_segment *seg,
|
||||
case CACHE_MODE_PASSTHROUGH:
|
||||
break;
|
||||
default:
|
||||
seg_error("has invalid cache's feature flag");
|
||||
seg_error("has invalid cache's feature flag")
|
||||
}
|
||||
if (!seg->policy_name)
|
||||
seg_error("is missing cache policy name");
|
||||
@@ -391,14 +391,6 @@ static void _check_lv_segment(struct logical_volume *lv, struct lv_segment *seg,
|
||||
if (!(seg2 = first_seg(seg->log_lv)) || (find_mirror_seg(seg2) != seg))
|
||||
seg_error("log LV does not point back to mirror segment");
|
||||
}
|
||||
if (seg_is_mirror(seg)) {
|
||||
if (!seg->region_size)
|
||||
seg_error("region size is zero");
|
||||
else if (seg->region_size > seg->lv->size)
|
||||
seg_error("region size is bigger then LV itself");
|
||||
else if (!is_power_of_2(seg->region_size))
|
||||
seg_error("region size is non power of 2");
|
||||
}
|
||||
} else { /* !mirrored */
|
||||
if (seg->log_lv) {
|
||||
if (lv_is_raid_image(lv))
|
||||
|
@@ -253,17 +253,6 @@
|
||||
|
||||
#define lv_is_removed(lv) (((lv)->status & LV_REMOVED) ? 1 : 0)
|
||||
|
||||
/* Recognize component LV (matching lib/misc/lvm-string.c _lvname_has_reserved_component_string()) */
|
||||
#define lv_is_component(lv) (lv_is_cache_origin(lv) || ((lv)->status & (\
|
||||
CACHE_POOL_DATA |\
|
||||
CACHE_POOL_METADATA |\
|
||||
MIRROR_IMAGE |\
|
||||
MIRROR_LOG |\
|
||||
RAID_IMAGE |\
|
||||
RAID_META |\
|
||||
THIN_POOL_DATA |\
|
||||
THIN_POOL_METADATA)) ? 1 : 0)
|
||||
|
||||
int lv_layout_and_role(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
struct dm_list **layout, struct dm_list **role);
|
||||
|
||||
@@ -377,19 +366,6 @@ struct pv_segment {
|
||||
*/
|
||||
#define FMT_INSTANCE_PRIVATE_MDAS 0x00000008U
|
||||
|
||||
/*
|
||||
* Each VG has its own fid struct. The fid for a VG describes where
|
||||
* the metadata for that VG can be found. The lists hold mda locations.
|
||||
*
|
||||
* label scan finds the metadata locations (devs and offsets) for a VG,
|
||||
* and saves this info in lvmcache vginfo/info lists.
|
||||
*
|
||||
* vg_read() then creates an fid for a given VG, and the mda locations
|
||||
* from lvmcache are copied onto the fid lists. Those mda locations
|
||||
* are read again by vg_read() to get VG metadata that is used to
|
||||
* create the 'vg' struct.
|
||||
*/
|
||||
|
||||
struct format_instance {
|
||||
unsigned ref_count; /* Refs to this fid from VG and PV structs */
|
||||
struct dm_pool *mem;
|
||||
@@ -462,7 +438,7 @@ struct lv_segment {
|
||||
struct logical_volume *merge_lv; /* thin, merge descendent lv into this ancestor */
|
||||
struct logical_volume *cow;
|
||||
struct dm_list origin_list;
|
||||
uint32_t region_size; /* For raids/mirrors - in sectors */
|
||||
uint32_t region_size; /* For mirrors, replicators - in sectors */
|
||||
uint32_t data_copies; /* For RAID: number of data copies (e.g. 3 for RAID 6 */
|
||||
uint32_t extents_copied;/* Number of extents synced for raids/mirrors */
|
||||
struct logical_volume *log_lv;
|
||||
@@ -645,7 +621,6 @@ void pvcreate_params_set_defaults(struct pvcreate_params *pp);
|
||||
*/
|
||||
#define WARN_PV_READ 0x00000001
|
||||
#define WARN_INCONSISTENT 0x00000002
|
||||
#define SKIP_RESCAN 0x00000004
|
||||
|
||||
/*
|
||||
* Utility functions
|
||||
@@ -725,7 +700,7 @@ int pv_resize_single(struct cmd_context *cmd,
|
||||
const uint64_t new_size,
|
||||
int yes);
|
||||
|
||||
int pv_analyze(struct cmd_context *cmd, struct device *dev,
|
||||
int pv_analyze(struct cmd_context *cmd, const char *pv_name,
|
||||
uint64_t label_sector);
|
||||
|
||||
/* FIXME: move internal to library */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user