mirror of
git://sourceware.org/git/lvm2.git
synced 2025-09-26 01:44:19 +03:00
Compare commits
13 Commits
v2_03_07
...
dev-bmr-dm
Author | SHA1 | Date | |
---|---|---|---|
|
9e3ef29562 | ||
|
146edfcdaa | ||
|
42f2193cd2 | ||
|
50d124652c | ||
|
fe0997d1ed | ||
|
2f949bec23 | ||
|
2573f714d0 | ||
|
811355194a | ||
|
61552a6c96 | ||
|
65db92d396 | ||
|
7c89429db8 | ||
|
d94c4e7678 | ||
|
aa5c026d61 |
20
configure
vendored
20
configure
vendored
@@ -737,6 +737,7 @@ CLDNOWHOLEARCHIVE
|
||||
CLDFLAGS
|
||||
CACHE
|
||||
BUILD_NOTIFYDBUS
|
||||
BUILD_DMFILEMAPD
|
||||
BUILD_LOCKDDLM
|
||||
BUILD_LOCKDSANLOCK
|
||||
BUILD_LVMLOCKD
|
||||
@@ -958,6 +959,7 @@ enable_use_lvmetad
|
||||
with_lvmetad_pidfile
|
||||
enable_use_lvmpolld
|
||||
with_lvmpolld_pidfile
|
||||
enable_dmfilemapd
|
||||
enable_notify_dbus
|
||||
enable_blkid_wiping
|
||||
enable_udev_systemd_background_jobs
|
||||
@@ -1692,6 +1694,7 @@ Optional Features:
|
||||
--disable-use-lvmlockd disable usage of LVM lock daemon
|
||||
--disable-use-lvmetad disable usage of LVM Metadata Daemon
|
||||
--disable-use-lvmpolld disable usage of LVM Poll Daemon
|
||||
--enable-dmfilemapd enable the dmstats filemap daemon
|
||||
--enable-notify-dbus enable LVM notification using dbus
|
||||
--disable-blkid_wiping disable libblkid detection of signatures when wiping
|
||||
and use native code instead
|
||||
@@ -11876,6 +11879,19 @@ cat >>confdefs.h <<_ACEOF
|
||||
_ACEOF
|
||||
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build dmfilemapd" >&5
|
||||
$as_echo_n "checking whether to build dmfilemapd... " >&6; }
|
||||
# Check whether --enable-dmfilemapd was given.
|
||||
if test "${enable_dmfilemapd+set}" = set; then :
|
||||
enableval=$enable_dmfilemapd; DMFILEMAPD=$enableval
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $DMFILEMAPD" >&5
|
||||
$as_echo "$DMFILEMAPD" >&6; }
|
||||
|
||||
BUILD_DMFILEMAPD=$DMFILEMAPD
|
||||
|
||||
################################################################################
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build notifydbus" >&5
|
||||
$as_echo_n "checking whether to build notifydbus... " >&6; }
|
||||
@@ -15385,10 +15401,11 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
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/lvmdbusd/Makefile 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 lib/replicator/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"
|
||||
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/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 lib/replicator/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
|
||||
@@ -16096,6 +16113,7 @@ do
|
||||
"daemons/dmeventd/plugins/mirror/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/mirror/Makefile" ;;
|
||||
"daemons/dmeventd/plugins/snapshot/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/snapshot/Makefile" ;;
|
||||
"daemons/dmeventd/plugins/thin/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmeventd/plugins/thin/Makefile" ;;
|
||||
"daemons/dmfilemapd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/dmfilemapd/Makefile" ;;
|
||||
"daemons/lvmdbusd/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/Makefile" ;;
|
||||
"daemons/lvmdbusd/path.py") CONFIG_FILES="$CONFIG_FILES daemons/lvmdbusd/path.py" ;;
|
||||
"daemons/lvmetad/Makefile") CONFIG_FILES="$CONFIG_FILES daemons/lvmetad/Makefile" ;;
|
||||
|
12
configure.in
12
configure.in
@@ -1269,6 +1269,16 @@ fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMPOLLD, [$DEFAULT_USE_LVMPOLLD],
|
||||
[Use lvmpolld by default.])
|
||||
|
||||
################################################################################
|
||||
dnl -- Check dmfilemapd
|
||||
AC_MSG_CHECKING(whether to build dmfilemapd)
|
||||
AC_ARG_ENABLE(dmfilemapd, AC_HELP_STRING([--enable-dmfilemapd],
|
||||
[enable the dmstats filemap daemon]),
|
||||
DMFILEMAPD=$enableval)
|
||||
AC_MSG_RESULT($DMFILEMAPD)
|
||||
|
||||
BUILD_DMFILEMAPD=$DMFILEMAPD
|
||||
|
||||
################################################################################
|
||||
dnl -- Build notifydbus
|
||||
AC_MSG_CHECKING(whether to build notifydbus)
|
||||
@@ -1992,6 +2002,7 @@ AC_SUBST(BUILD_LVMPOLLD)
|
||||
AC_SUBST(BUILD_LVMLOCKD)
|
||||
AC_SUBST(BUILD_LOCKDSANLOCK)
|
||||
AC_SUBST(BUILD_LOCKDDLM)
|
||||
AC_SUBST(BUILD_DMFILEMAPD)
|
||||
AC_SUBST(BUILD_NOTIFYDBUS)
|
||||
AC_SUBST(CACHE)
|
||||
AC_SUBST(CFLAGS)
|
||||
@@ -2156,6 +2167,7 @@ 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/path.py
|
||||
daemons/lvmetad/Makefile
|
||||
|
@@ -48,8 +48,12 @@ ifeq ("@BUILD_LVMDBUSD@", "yes")
|
||||
SUBDIRS += lvmdbusd
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_DMFILEMAPD@", "yes")
|
||||
SUBDIRS += dmfilemapd
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd dmfilemapd
|
||||
endif
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
1
daemons/dmfilemapd/.gitignore
vendored
Normal file
1
daemons/dmfilemapd/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dmfilemapd
|
69
daemons/dmfilemapd/Makefile.in
Normal file
69
daemons/dmfilemapd/Makefile.in
Normal file
@@ -0,0 +1,69 @@
|
||||
#
|
||||
# Copyright (C) 2016 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
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
SOURCES = dmfilemapd.c
|
||||
|
||||
TARGETS = dmfilemapd
|
||||
|
||||
.PHONY: install_dmeventd install_dmeventd_static
|
||||
|
||||
INSTALL_DMFILEMAPD_TARGETS = install_dmfilemapd_dynamic
|
||||
|
||||
CLEAN_TARGETS = dmfilemapd.static
|
||||
|
||||
CFLOW_LIST = $(SOURCES)
|
||||
CFLOW_LIST_TARGET = $(LIB_NAME).cflow
|
||||
CFLOW_TARGET = dmfilemapd
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
all: device-mapper
|
||||
device-mapper: $(TARGETS)
|
||||
|
||||
LIBS += -ldevmapper
|
||||
LVMLIBS += -ldevmapper-event $(PTHREAD_LIBS)
|
||||
|
||||
CFLAGS_dmeventd.o += $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
dmfilemapd: $(LIB_SHARED) dmfilemapd.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) -L. -o $@ dmfilemapd.o \
|
||||
$(DL_LIBS) $(LVMLIBS) $(LIBS) -rdynamic
|
||||
|
||||
dmfilemapd.static: $(LIB_STATIC) dmfilemapd.o $(interfacebuilddir)/libdevmapper.a
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $(ELDFLAGS) -static -L. -L$(interfacebuilddir) -o $@ \
|
||||
dmfilemapd.o $(DL_LIBS) $(LVMLIBS) $(LIBS) $(STATIC_LIBS)
|
||||
|
||||
ifneq ("$(CFLOW_CMD)", "")
|
||||
CFLOW_SOURCES = $(addprefix $(srcdir)/, $(SOURCES))
|
||||
-include $(top_builddir)/libdm/libdevmapper.cflow
|
||||
-include $(top_builddir)/lib/liblvm-internal.cflow
|
||||
-include $(top_builddir)/lib/liblvm2cmd.cflow
|
||||
-include $(top_builddir)/daemons/dmfilemapd/$(LIB_NAME).cflow
|
||||
endif
|
||||
|
||||
install_dmfilemapd_dynamic: dmfilemapd
|
||||
$(INSTALL_PROGRAM) -D $< $(sbindir)/$(<F)
|
||||
|
||||
install_dmfilemapd_static: dmfilemapd.static
|
||||
$(INSTALL_PROGRAM) -D $< $(staticdir)/$(<F)
|
||||
|
||||
install_dmfilemapd: $(INSTALL_DMEVENTD_TARGETS)
|
||||
|
||||
install: install_dmfilemapd
|
||||
|
||||
install_device-mapper: install_dmfilemapd
|
||||
|
415
daemons/dmfilemapd/dmfilemapd.c
Normal file
415
daemons/dmfilemapd/dmfilemapd.c
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of the device-mapper userspace tools.
|
||||
*
|
||||
* It includes tree drawing code based on pstree: http://psmisc.sourceforge.net/
|
||||
*
|
||||
* 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 General Public License v.2.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "tool.h"
|
||||
|
||||
#include "dm-logging.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/fanotify.h>
|
||||
|
||||
#ifdef __linux__
|
||||
# include "kdev_t.h"
|
||||
#else
|
||||
# define MAJOR(x) major((x))
|
||||
# define MINOR(x) minor((x))
|
||||
# define MKDEV(x,y) makedev((x),(y))
|
||||
#endif
|
||||
|
||||
struct filemap_monitor {
|
||||
/* group_id to update */
|
||||
uint64_t group_id;
|
||||
char *path;
|
||||
int fanotify_fd;
|
||||
/* file to monitor */
|
||||
int fd;
|
||||
|
||||
/* monitoring heuristics */
|
||||
int64_t blocks; /* allocated blocks, from stat.st_blocks */
|
||||
int64_t nr_regions;
|
||||
};
|
||||
|
||||
static int _debug;
|
||||
static int _verbose;
|
||||
|
||||
const char *const _usage = "dmfilemapd <fd> <group_id> <path> "
|
||||
"[<debug>[<log_level>]]";
|
||||
|
||||
/*
|
||||
* Daemon logging. By default, all messages are thrown away: messages
|
||||
* are only written to the terminal if the daemon is run in the foreground.
|
||||
*/
|
||||
__attribute__((format(printf, 5, 0)))
|
||||
static void _dmfilemapd_log_line(int level,
|
||||
const char *file __attribute__((unused)),
|
||||
int line __attribute__((unused)),
|
||||
int dm_errno_or_class,
|
||||
const char *f, va_list ap)
|
||||
{
|
||||
static int _abort_on_internal_errors = -1;
|
||||
FILE *out = log_stderr(level) ? stderr : stdout;
|
||||
|
||||
level = log_level(level);
|
||||
|
||||
if (level <= _LOG_WARN || _verbose) {
|
||||
if (level < _LOG_WARN)
|
||||
out = stderr;
|
||||
vfprintf(out, f, ap);
|
||||
fputc('\n', out);
|
||||
}
|
||||
|
||||
if (_abort_on_internal_errors < 0)
|
||||
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
|
||||
_abort_on_internal_errors =
|
||||
strcmp(getenv("DM_ABORT_ON_INTERNAL_ERRORS") ? : "0", "0");
|
||||
|
||||
if (_abort_on_internal_errors &&
|
||||
!strncmp(f, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
|
||||
abort();
|
||||
}
|
||||
|
||||
__attribute__((format(printf, 5, 6)))
|
||||
static void _dmfilemapd_log_with_errno(int level,
|
||||
const char *file, int line,
|
||||
int dm_errno_or_class,
|
||||
const char *f, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, f);
|
||||
_dmfilemapd_log_line(level, file, line, dm_errno_or_class, f, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only used for reporting errors before daemonise().
|
||||
*/
|
||||
__attribute__((format(printf, 1, 2)))
|
||||
static void _early_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fputc('\n', stderr);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void _setup_logging(void)
|
||||
{
|
||||
dm_log_init_verbose(_verbose - 1);
|
||||
dm_log_with_errno_init(_dmfilemapd_log_with_errno);
|
||||
}
|
||||
|
||||
static int _bind_stats_from_fd(struct dm_stats *dms, int fd)
|
||||
{
|
||||
int major, minor;
|
||||
struct stat buf;
|
||||
|
||||
if (fstat(fd, &buf)) {
|
||||
log_error("fstat failed for fd %d.", fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
major = (int) MAJOR(buf.st_dev);
|
||||
minor = (int) MINOR(buf.st_dev);
|
||||
|
||||
if (!dm_stats_bind_devno(dms, major, minor))
|
||||
return_0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _parse_args(int argc, char **argv, struct filemap_monitor *fm)
|
||||
{
|
||||
char *endptr;
|
||||
|
||||
/* we don't care what is in argv[0]. */
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (argc < 4) {
|
||||
_early_log("Wrong number of arguments.\n");
|
||||
_early_log("usage: %s\n", _usage);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(fm, 0, sizeof(*fm));
|
||||
|
||||
fm->fd = strtol(argv[0], &endptr, 10);
|
||||
if (*endptr) {
|
||||
_early_log("Could not parse file descriptor: %s", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
fm->group_id = strtoull(argv[0], &endptr, 10);
|
||||
if (*endptr) {
|
||||
_early_log("Could not parse group identifier: %s", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (!argv[0] || !strlen(argv[0])) {
|
||||
_early_log("Path argument is required.");
|
||||
return 0;
|
||||
}
|
||||
fm->path = dm_strdup(argv[0]);
|
||||
if (!fm->path) {
|
||||
_early_log("Could not allocate memory for path argument.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
|
||||
if (argc) {
|
||||
_debug = strtol(argv[0], &endptr, 10);
|
||||
if (*endptr) {
|
||||
_early_log("Could not parse debug argument: %s.",
|
||||
argv[0]);
|
||||
return 0;
|
||||
}
|
||||
argc--;
|
||||
argv++;
|
||||
if (argc) {
|
||||
_verbose = strtol(argv[0], &endptr, 10);
|
||||
if (*endptr) {
|
||||
_early_log("Could not parse verbose "
|
||||
"argument: %s", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (_verbose < 0 || _verbose > 3) {
|
||||
_early_log("Verbose argument out of range: %d.",
|
||||
_verbose);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_verbose)
|
||||
_early_log("dmfilemapd starting: fd=%d", fm->fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _filemap_fd_check_changed(struct filemap_monitor *fm)
|
||||
{
|
||||
int64_t blocks, old_blocks;
|
||||
struct stat buf;
|
||||
|
||||
if (fstat(fm->fd, &buf)) {
|
||||
log_error("Failed to fstat filemap file descriptor.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
blocks = buf.st_blocks;
|
||||
|
||||
/* first check? */
|
||||
if (fm->blocks < 0)
|
||||
old_blocks = buf.st_blocks;
|
||||
else
|
||||
old_blocks = fm->blocks;
|
||||
|
||||
fm->blocks = blocks;
|
||||
|
||||
return (fm->blocks != old_blocks);
|
||||
}
|
||||
|
||||
static int _filemap_monitor_get_events(struct filemap_monitor *fm)
|
||||
{
|
||||
struct fanotify_event_metadata buf[64];
|
||||
ssize_t len;
|
||||
|
||||
len = read(fm->fanotify_fd, (void *) &buf, sizeof(buf));
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _filemap_monitor_set_notify(struct filemap_monitor *fm)
|
||||
{
|
||||
int fan_fd, fan_flags;
|
||||
fan_flags = FAN_CLOEXEC | FAN_CLASS_CONTENT;
|
||||
|
||||
if ((fan_fd = fanotify_init(fan_flags, O_RDONLY | O_LARGEFILE)) < 0) {
|
||||
_early_log("Failed to initialise fanotify.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fanotify_mark(fan_fd, FAN_MARK_ADD, FAN_MODIFY, 0, fm->path)) {
|
||||
_early_log("Failed to add fanotify mark.");
|
||||
perror("fanotify_mark");
|
||||
return 0;
|
||||
}
|
||||
fm->fanotify_fd = fan_fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _filemap_monitor_end_notify(struct filemap_monitor *fm)
|
||||
{
|
||||
if (close(fm->fanotify_fd))
|
||||
log_error("Error closing fanotify fd.");
|
||||
}
|
||||
|
||||
static void _filemap_monitor_destroy(struct filemap_monitor *fm)
|
||||
{
|
||||
_filemap_monitor_end_notify(fm);
|
||||
if (close(fm->fd))
|
||||
log_error("Error closing fd %d.", fm->fd);
|
||||
}
|
||||
|
||||
static int daemonise(struct filemap_monitor *fm)
|
||||
{
|
||||
pid_t pid = 0, sid;
|
||||
int fd;
|
||||
|
||||
if (!(sid = setsid())) {
|
||||
_early_log("setsid failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
_early_log("Failed to fork daemon process.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
if (_verbose)
|
||||
_early_log("Started dmfilemapd with pid=%d", pid);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (chdir("/")) {
|
||||
_early_log("Failed to change directory.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_verbose) {
|
||||
if (close(STDIN_FILENO))
|
||||
_early_log("Error closing stdin");
|
||||
if (close(STDOUT_FILENO))
|
||||
_early_log("Error closing stdout");
|
||||
if (close(STDERR_FILENO))
|
||||
_early_log("Error closing stderr");
|
||||
if ((open("/dev/null", O_RDONLY) < 0) ||
|
||||
(open("/dev/null", O_WRONLY) < 0) ||
|
||||
(open("/dev/null", O_WRONLY) < 0)) {
|
||||
_early_log("Error opening stdio streams.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (fd = sysconf(_SC_OPEN_MAX) - 1; fd > STDERR_FILENO; fd--) {
|
||||
if (fd == fm->fd)
|
||||
continue;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _update_regions(struct dm_stats *dms, struct filemap_monitor *fm)
|
||||
{
|
||||
uint64_t *regions = NULL, *region, nr_regions = 0;
|
||||
|
||||
regions = dm_stats_update_regions_from_fd(dms, fm->fd, fm->group_id);
|
||||
if (!regions) {
|
||||
log_error("Failed to update filemap regions for group_id="
|
||||
FMTu64 ".", fm->group_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
|
||||
nr_regions++;
|
||||
|
||||
fm->nr_regions = nr_regions;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _filemap_monitor_wait(void)
|
||||
{
|
||||
/* limit to two updates/second. */
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
static int _dmfilemapd(struct filemap_monitor *fm)
|
||||
{
|
||||
int running = 1, check = 0;
|
||||
struct dm_stats *dms;
|
||||
|
||||
dms = dm_stats_create("dmstats"); /* FIXME */
|
||||
if (!_bind_stats_from_fd(dms, fm->fd)) {
|
||||
log_error("Could not bind dm_stats handle to file descriptor "
|
||||
"%d", fm->fd);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_filemap_monitor_set_notify(fm))
|
||||
goto bad;
|
||||
|
||||
do {
|
||||
_filemap_monitor_wait();
|
||||
if (!_filemap_monitor_get_events(fm))
|
||||
continue;
|
||||
|
||||
if ((check = _filemap_fd_check_changed(fm)) < 0)
|
||||
goto bad;
|
||||
|
||||
if (check)
|
||||
if (!_update_regions(dms, fm))
|
||||
goto bad;
|
||||
|
||||
running = !!fm->nr_regions;
|
||||
} while (running);
|
||||
|
||||
_filemap_monitor_destroy(fm);
|
||||
dm_stats_destroy(dms);
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
_filemap_monitor_destroy(fm);
|
||||
dm_stats_destroy(dms);
|
||||
log_error("Exiting");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* dmfilemapd <fd> <group_id> <path> [<debug>[<log_level>]]
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct filemap_monitor fm;
|
||||
|
||||
if (!_parse_args(argc, argv, &fm))
|
||||
return 1;
|
||||
|
||||
_setup_logging();
|
||||
|
||||
log_info("Starting dmfilemapd with fd=%d, group_id=" FMTu64 " "
|
||||
"path=%s", fm.fd, fm.group_id, fm.path);
|
||||
|
||||
if (!_debug && !daemonise(&fm))
|
||||
return 1;
|
||||
|
||||
return _dmfilemapd(&fm);
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
dm_bit_get_last
|
||||
dm_bit_get_prev
|
||||
dm_stats_update_regions_from_fd
|
||||
dm_bitset_parse_list
|
||||
dm_stats_start_filemapd
|
||||
|
@@ -1310,8 +1310,9 @@ int dm_stats_get_group_descriptor(const struct dm_stats *dms,
|
||||
* On success the function returns a pointer to an array of uint64_t
|
||||
* containing the IDs of the newly created regions. The region_id
|
||||
* array is terminated by the value DM_STATS_REGION_NOT_PRESENT and
|
||||
* should be freed using dm_free() when no longer required. On error
|
||||
* NULL is returned.
|
||||
* should be freed using dm_free() when no longer required.
|
||||
*
|
||||
* On error NULL is returned.
|
||||
*
|
||||
* Following a call to dm_stats_create_regions_from_fd() the handle
|
||||
* is guaranteed to be in a listed state, and to contain any region
|
||||
@@ -1319,12 +1320,56 @@ int dm_stats_get_group_descriptor(const struct dm_stats *dms,
|
||||
*
|
||||
* The group_id for the new group is equal to the region_id value in
|
||||
* the first array element.
|
||||
*
|
||||
*/
|
||||
uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
int group, int precise,
|
||||
struct dm_histogram *bounds,
|
||||
const char *alias);
|
||||
/*
|
||||
* Update a group of regions that correspond to the extents of a file
|
||||
* in the filesystem, adding and removing regions to account for
|
||||
* allocation changes in the underlying file.
|
||||
*
|
||||
* File descriptor fd must reference a regular file, open for reading,
|
||||
* in a local file system that supports the FIEMAP ioctl, and that
|
||||
* returns data describing the physical location of extents.
|
||||
*
|
||||
* The file descriptor can be closed by the caller following the call
|
||||
* to dm_stats_update_regions_from_fd().
|
||||
*
|
||||
* On success the function returns a pointer to an array of uint64_t
|
||||
* containing the IDs of the updated regions (including any existing
|
||||
* regions that were not modified by the call).
|
||||
*
|
||||
* The region_id array is terminated by the special value
|
||||
* DM_STATS_REGION_NOT_PRESENT and should be freed using dm_free()
|
||||
* when no longer required.
|
||||
*
|
||||
* On error NULL is returned.
|
||||
*
|
||||
* Following a call to dm_stats_update_regions_from_fd() the handle
|
||||
* is guaranteed to be in a listed state, and to contain any region
|
||||
* and group identifiers created by the operation.
|
||||
*
|
||||
* This function cannot be used with file mapped regions that are
|
||||
* not members of a group: either group the regions, or remove them
|
||||
* and re-map them with dm_stats_create_regions_from_fd().
|
||||
*/
|
||||
uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
uint64_t group_id);
|
||||
|
||||
/*
|
||||
* Start the dmfilemapd filemap monitoring daemon for the specified
|
||||
* file descriptor, group, and file system path. The daemon will
|
||||
* monitor the file for allocation changes, and when a change is
|
||||
* detected, call dm_stats_update_regions_from_fd() to update the
|
||||
* mapped regions for the file.
|
||||
*
|
||||
* The daemon can be stopped at any time by sending SIGTERM to the
|
||||
* daemon pid.
|
||||
*/
|
||||
int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
|
||||
unsigned foreground, unsigned verbose);
|
||||
|
||||
/*
|
||||
* Call this to actually run the ioctl.
|
||||
|
@@ -1083,6 +1083,9 @@ int dm_stats_list(struct dm_stats *dms, const char *program_id)
|
||||
if (!_stats_set_name_cache(dms))
|
||||
return_0;
|
||||
|
||||
if (dms->regions)
|
||||
_stats_regions_destroy(dms);
|
||||
|
||||
r = dm_snprintf(msg, sizeof(msg), "@stats_list %s", program_id);
|
||||
|
||||
if (r < 0) {
|
||||
@@ -3843,6 +3846,37 @@ static int _extent_start_compare(const void *p1, const void *p2)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize the group bitmap corresponding to group_id so that it can
|
||||
* contain at least num_regions members.
|
||||
*/
|
||||
static int _stats_resize_group(struct dm_stats_group *group, int num_regions)
|
||||
{
|
||||
int last_bit = dm_bit_get_last(group->regions);
|
||||
dm_bitset_t new, old;
|
||||
|
||||
if (last_bit >= num_regions) {
|
||||
log_error("Cannot resize group bitmap to %d with bit %d set.",
|
||||
num_regions, last_bit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("Resizing group bitmap from %d to %d (last_bit=%d).",
|
||||
group->regions[0], num_regions, last_bit);
|
||||
|
||||
new = dm_bitset_create(NULL, num_regions);
|
||||
if (!new) {
|
||||
log_error("Could not allocate memory for new group bitmap.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
old = group->regions;
|
||||
dm_bit_copy(new, old);
|
||||
group->regions = new;
|
||||
dm_bitset_destroy(old);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _stats_create_group(struct dm_stats *dms, dm_bitset_t regions,
|
||||
const char *alias, uint64_t *group_id)
|
||||
{
|
||||
@@ -4207,16 +4241,17 @@ static int _stats_add_extent(struct dm_pool *mem, struct fiemap_extent *fm_ext,
|
||||
/* convert bytes to dm (512b) sectors */
|
||||
extent.start = fm_ext->fe_physical >> SECTOR_SHIFT;
|
||||
extent.len = fm_ext->fe_length >> SECTOR_SHIFT;
|
||||
|
||||
extent.id = id;
|
||||
|
||||
log_very_verbose("Found file extent (start=" FMTu64 ", len=" FMTu64 ", id="
|
||||
FMTu64 ")", extent.start, extent.len, id);
|
||||
|
||||
if (!dm_pool_grow_object(mem, &extent,
|
||||
sizeof(extent))) {
|
||||
log_error("Cannot map file: failed to grow extent map.");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* test for the boundary of an extent */
|
||||
@@ -4286,7 +4321,7 @@ static uint64_t _stats_map_extents(struct dm_pool *mem,
|
||||
* If the file only has a single extent, no boundary is ever
|
||||
* detected to trigger addition of the first extent.
|
||||
*/
|
||||
if (fm_ext[i - 1].fe_logical == 0) {
|
||||
if (*eof || (fm_ext[i - 1].fe_logical == 0)) {
|
||||
_stats_add_extent(mem, fm_pending, nr_extents);
|
||||
nr_extents++;
|
||||
}
|
||||
@@ -4319,7 +4354,6 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
struct _extent *extents;
|
||||
unsigned long flags = 0;
|
||||
uint64_t *buf;
|
||||
int rc;
|
||||
|
||||
buf = dm_zalloc(STATS_FIE_BUF_LEN);
|
||||
if (!buf) {
|
||||
@@ -4339,7 +4373,7 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
if (!dm_pool_begin_object(mem, sizeof(*extents)))
|
||||
return NULL;
|
||||
|
||||
flags |= FIEMAP_FLAG_SYNC;
|
||||
flags = FIEMAP_FLAG_SYNC;
|
||||
|
||||
do {
|
||||
/* start of ioctl loop - zero size and set count to bufsize */
|
||||
@@ -4348,10 +4382,8 @@ static struct _extent *_stats_get_extents_for_file(struct dm_pool *mem, int fd,
|
||||
fiemap->fm_extent_count = *count;
|
||||
|
||||
/* get count-sized chunk of extents */
|
||||
rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap);
|
||||
if (rc < 0) {
|
||||
rc = -errno;
|
||||
if (rc == -EBADR)
|
||||
if (ioctl(fd, FS_IOC_FIEMAP, (unsigned long) fiemap) < 0) {
|
||||
if (errno == EBADR)
|
||||
log_err_once("FIEMAP failed with unknown "
|
||||
"flags %x.", fiemap->fm_flags);
|
||||
goto bad;
|
||||
@@ -4390,22 +4422,69 @@ bad:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MATCH_EXTENT(e, s, l) \
|
||||
(((e).start == (s)) && ((e).len == (l)))
|
||||
|
||||
static struct _extent *_find_extent(size_t nr_extents, struct _extent *extents,
|
||||
uint64_t start, uint64_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < nr_extents; i++)
|
||||
if (MATCH_EXTENT(extents[i], start, len))
|
||||
return extents + i;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _extent_in_extents(size_t nr_extents, struct _extent *extents,
|
||||
uint64_t start, uint64_t len)
|
||||
{
|
||||
return (_find_extent(nr_extents, extents, start, len) != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a set of regions representing the extents of a file and
|
||||
* return a table of uint64_t region_id values. The number of regions
|
||||
* Clean up a table of region_id values that were created during a
|
||||
* failed dm_stats_create_regions_from_fd, or dm_stats_update_regions_from_fd
|
||||
* operation.
|
||||
*/
|
||||
static void _stats_cleanup_region_ids(struct dm_stats *dms, uint64_t *regions,
|
||||
uint64_t nr_regions)
|
||||
{
|
||||
uint64_t i;
|
||||
|
||||
for (i = 0; i < nr_regions; i++)
|
||||
if (!_stats_delete_region(dms, regions[i]))
|
||||
log_error("Could not delete region " FMTu64 ".", i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create or update a set of regions representing the extents of a file
|
||||
* and return a table of uint64_t region_id values. The number of regions
|
||||
* created is returned in the memory pointed to by count (which must be
|
||||
* non-NULL).
|
||||
*
|
||||
* If group_id is not equal to DM_STATS_GROUP_NOT_PRESENT, it is assumed
|
||||
* that group_id corresponds to a group containing existing regions that
|
||||
* were mapped to this file at an earlier time: regions will be added or
|
||||
* removed to reflect the current status of the file.
|
||||
*/
|
||||
static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
struct dm_histogram *bounds,
|
||||
int precise, uint64_t *count)
|
||||
static uint64_t *_stats_map_file_regions(struct dm_stats *dms, int fd,
|
||||
struct dm_histogram *bounds,
|
||||
int precise, uint64_t group_id,
|
||||
uint64_t *count, int *regroup)
|
||||
{
|
||||
uint64_t *regions = NULL, i, fail_region;
|
||||
struct _extent *extents = NULL, *old_extents;
|
||||
uint64_t *regions = NULL, fail_region;
|
||||
struct dm_stats_region *region = NULL;
|
||||
struct dm_stats_group *group = NULL;
|
||||
struct dm_pool *extent_mem = NULL;
|
||||
struct _extent *extents = NULL;
|
||||
struct _extent ext, *old_ext;
|
||||
int64_t nr_kept, nr_old, i;
|
||||
char *hist_arg = NULL;
|
||||
struct statfs fsbuf;
|
||||
struct stat buf;
|
||||
int num_bits;
|
||||
|
||||
*regroup = (group_id == DM_STATS_GROUP_NOT_PRESENT);
|
||||
|
||||
#ifdef BTRFS_SUPER_MAGIC
|
||||
if (fstatfs(fd, &fsbuf)) {
|
||||
@@ -4435,6 +4514,10 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_very_verbose("%sapping extents from fd %d on (%d:%d)",
|
||||
(group_id != DM_STATS_GROUP_NOT_PRESENT) ? "Rem" : "M",
|
||||
fd, major(buf.st_dev), minor(buf.st_dev));
|
||||
|
||||
/* Use a temporary, private pool for the extent table. This avoids
|
||||
* hijacking the dms->mem (region table) pool which would lead to
|
||||
* interleaving temporary allocations with dm_stats_list() data,
|
||||
@@ -4448,6 +4531,77 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
return_0;
|
||||
}
|
||||
|
||||
/*
|
||||
* First update pass: prune no-longer-allocated extents from the group
|
||||
* and build a table of the remaining extents so that their creation
|
||||
* can be skipped in the second pass.
|
||||
*/
|
||||
if (group_id != DM_STATS_GROUP_NOT_PRESENT) {
|
||||
group = &dms->groups[group_id];
|
||||
|
||||
if (!dms->regions)
|
||||
dm_stats_list(dms, dms->program_id);
|
||||
|
||||
log_very_verbose("Checking filemap for changed extents "
|
||||
"(group_id=" FMTu64 ")", group_id);
|
||||
|
||||
if (!dm_pool_begin_object(extent_mem, sizeof(*old_extents))) {
|
||||
log_error("Could not allocate extent table.");
|
||||
goto out_extents;
|
||||
}
|
||||
|
||||
nr_kept = nr_old = 0; /* counts of old and retained extents */
|
||||
|
||||
/*
|
||||
* First pass: delete de-allocated extents and set regroup=1
|
||||
* if deleting the current group leader.
|
||||
*/
|
||||
for (i = dm_bit_get_last(group->regions);
|
||||
i >= 0; i = dm_bit_get_prev(group->regions, i)) {
|
||||
region = &dms->regions[i];
|
||||
nr_old++;
|
||||
|
||||
log_very_verbose("Checking extent " FMTu64 " (start="
|
||||
FMTu64 " len=" FMTu64 ")", i,
|
||||
region->start, region->len);
|
||||
|
||||
if (!_extent_in_extents(*count, extents,
|
||||
region->start, region->len)) {
|
||||
|
||||
if (i == group_id)
|
||||
*regroup = 1;
|
||||
|
||||
log_very_verbose("Deleting region " FMTu64
|
||||
" (leader=%d, regroup=%d)",
|
||||
i, i == group_id, *regroup);
|
||||
|
||||
_stats_delete_region(dms, i);
|
||||
} else {
|
||||
ext.start = region->start;
|
||||
ext.len = region->len;
|
||||
ext.id = i;
|
||||
nr_kept++;
|
||||
|
||||
log_very_verbose("Keeping region " FMTu64
|
||||
" (leader=%d, regroup=%d)",
|
||||
i, i == group_id, *regroup);
|
||||
|
||||
dm_pool_grow_object(extent_mem, &ext,
|
||||
sizeof(ext));
|
||||
}
|
||||
}
|
||||
|
||||
old_extents = dm_pool_end_object(extent_mem);
|
||||
if (!old_extents) {
|
||||
log_error("Could not finalize region extent table.");
|
||||
goto out;
|
||||
}
|
||||
log_very_verbose("Kept %ld of %ld old extents",
|
||||
nr_kept, nr_old);
|
||||
log_very_verbose("Found " FMTu64 " new extents",
|
||||
*count - nr_kept);
|
||||
}
|
||||
|
||||
if (bounds) {
|
||||
/* _build_histogram_arg enables precise if vals < 1ms. */
|
||||
if (!(hist_arg = _build_histogram_arg(bounds, &precise)))
|
||||
@@ -4460,7 +4614,26 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Second pass (first for non-update case): create regions for
|
||||
* all extents not retained from the prior mapping, and insert
|
||||
* retained regions into the table of region_id values.
|
||||
*
|
||||
* If a regroup is not scheduled, set group bits for newly
|
||||
* created regions in the group leader bitmap.
|
||||
*/
|
||||
for (i = 0; i < *count; i++) {
|
||||
if (group_id != DM_STATS_GROUP_NOT_PRESENT) {
|
||||
if ((old_ext = _find_extent(nr_kept, old_extents,
|
||||
extents[i].start,
|
||||
extents[i].len))) {
|
||||
regions[i] = old_ext->id;
|
||||
log_very_verbose("Reusing existing extent "
|
||||
"region_id=" FMTu64,
|
||||
old_ext->id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!_stats_create_region(dms, regions + i, extents[i].start,
|
||||
extents[i].len, -1, precise, hist_arg,
|
||||
dms->program_id, "")) {
|
||||
@@ -4469,11 +4642,38 @@ static uint64_t *_stats_create_file_regions(struct dm_stats *dms, int fd,
|
||||
extents[i].start);
|
||||
goto out_remove;
|
||||
}
|
||||
|
||||
log_very_verbose("Created new extent region with region_id="
|
||||
FMTu64, regions[i]);
|
||||
|
||||
if (!(*regroup) && (group_id != DM_STATS_GROUP_NOT_PRESENT)) {
|
||||
/* expand group bitmap */
|
||||
if (regions[i] > (group->regions[0] - 1)) {
|
||||
num_bits = regions[i] + *count;
|
||||
if (!_stats_resize_group(group, num_bits)) {
|
||||
log_error("Failed to resize group "
|
||||
"bitmap.");
|
||||
goto out_remove;
|
||||
}
|
||||
}
|
||||
dm_bit_set(group->regions, regions[i]);
|
||||
}
|
||||
|
||||
}
|
||||
regions[*count] = DM_STATS_REGION_NOT_PRESENT;
|
||||
|
||||
/* Update group leader aux_data for new group members. */
|
||||
if (!(*regroup) && (group_id != DM_STATS_GROUP_NOT_PRESENT))
|
||||
if (!_stats_set_aux(dms, group_id,
|
||||
dms->regions[group_id].aux_data))
|
||||
log_error("Failed to update group aux_data.");
|
||||
|
||||
if (bounds)
|
||||
dm_free(hist_arg);
|
||||
|
||||
out_extents:
|
||||
if (group_id != DM_STATS_GROUP_NOT_PRESENT)
|
||||
dm_pool_abandon_object(extent_mem);
|
||||
dm_pool_free(extent_mem, extents);
|
||||
dm_pool_destroy(extent_mem);
|
||||
return regions;
|
||||
@@ -4489,13 +4689,15 @@ out_remove:
|
||||
dm_stats_list(dms, NULL);
|
||||
|
||||
fail_region = i;
|
||||
for (i = 0; i < fail_region; i++)
|
||||
if (!_stats_delete_region(dms, regions[i]))
|
||||
log_error("Could not delete region " FMTu64 ".", i);
|
||||
|
||||
_stats_cleanup_region_ids(dms, regions, fail_region);
|
||||
*count = 0;
|
||||
|
||||
out:
|
||||
/*
|
||||
* The table of file extents in 'extents' is always built, so free
|
||||
* it explicitly: this will also free any 'old_extents' table that
|
||||
* was later allocated from the 'extent_mem' pool by this function.
|
||||
*/
|
||||
dm_pool_free(extent_mem, extents);
|
||||
dm_pool_destroy(extent_mem);
|
||||
dm_free(hist_arg);
|
||||
@@ -4509,15 +4711,16 @@ uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
const char *alias)
|
||||
{
|
||||
uint64_t *regions, count = 0;
|
||||
int regroup = 1;
|
||||
|
||||
if (alias && !group) {
|
||||
log_error("Cannot set alias without grouping regions.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
regions = _stats_create_file_regions(dms, fd, bounds, precise, &count);
|
||||
if (!regions)
|
||||
return_0;
|
||||
if (!(regions = _stats_map_file_regions(dms, fd, bounds, precise,
|
||||
-1, &count, ®roup)))
|
||||
return NULL;
|
||||
|
||||
if (!group)
|
||||
return regions;
|
||||
@@ -4531,11 +4734,175 @@ uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
|
||||
return regions;
|
||||
out:
|
||||
_stats_cleanup_region_ids(dms, regions, count);
|
||||
dm_free(regions);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
uint64_t group_id)
|
||||
{
|
||||
struct dm_histogram *bounds = NULL;
|
||||
int nr_bins, precise, regroup = 0;
|
||||
uint64_t *regions, count = 0;
|
||||
const char *alias;
|
||||
|
||||
if (!dms->regions)
|
||||
dm_stats_list(dms, dms->program_id);
|
||||
|
||||
if (!dm_stats_group_present(dms, group_id)) {
|
||||
log_error("Group ID " FMTu64 " does not exist.", group_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to take a copy of the alias, as it is possible that the
|
||||
* extent corresponding to the current group leader no longer exists;
|
||||
* in that case, _stats_map_file_regions() will remove the leader and
|
||||
* the group will be destroyed.
|
||||
*/
|
||||
if (dms->groups[group_id].alias) {
|
||||
alias = dm_strdup(dms->groups[group_id].alias);
|
||||
if (!alias) {
|
||||
log_error("Failed to allocated group alias string.");
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
alias = NULL;
|
||||
|
||||
if (dms->regions[group_id].bounds) {
|
||||
/*
|
||||
* We need a copy since the group leader may have been
|
||||
* deallocated from the file - _stats_map_file_regions()
|
||||
* will destroy its region in this case.
|
||||
*/
|
||||
nr_bins = dms->regions[group_id].bounds->nr_bins;
|
||||
bounds = _alloc_dm_histogram(nr_bins);
|
||||
if (!bounds) {
|
||||
log_error("Could not allocate memory for group "
|
||||
"histogram bounds.");
|
||||
return NULL;
|
||||
}
|
||||
_stats_copy_histogram_bounds(bounds,
|
||||
dms->regions[group_id].bounds);
|
||||
}
|
||||
|
||||
precise = (dms->regions[group_id].timescale == 1);
|
||||
|
||||
regions = _stats_map_file_regions(dms, fd, bounds, precise,
|
||||
group_id, &count, ®roup);
|
||||
|
||||
if (!regions)
|
||||
goto bad;
|
||||
|
||||
if (!dm_stats_list(dms, NULL))
|
||||
goto bad;
|
||||
|
||||
if (regroup)
|
||||
if (!_stats_group_file_regions(dms, regions, count, alias))
|
||||
goto bad;
|
||||
|
||||
dm_free(bounds);
|
||||
dm_free((char *) alias);
|
||||
return regions;
|
||||
bad:
|
||||
_stats_cleanup_region_ids(dms, regions, count);
|
||||
dm_free(bounds);
|
||||
dm_free((char *) alias);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define NR_FILEMAPD_ARGS 5
|
||||
/*
|
||||
* Start dmfilemapd to monitor the specified file descriptor, and to
|
||||
* update the group given by 'group_id' when the file's allocation
|
||||
* changes.
|
||||
*
|
||||
* usage: dmfilemapd <fd> <group_id> [<debug>[<log_level>]]
|
||||
*/
|
||||
int dm_stats_start_filemapd(int fd, uint64_t group_id, const char *path,
|
||||
unsigned foreground, unsigned verbose)
|
||||
{
|
||||
char *args[NR_FILEMAPD_ARGS + 1];
|
||||
char fd_str[8], group_str[8], fg_str[2], verb_str[2];
|
||||
int argc = 0;
|
||||
pid_t pid;
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("dmfilemapd file descriptor must be "
|
||||
"non-negative: %d", fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (foreground > 1) {
|
||||
log_error("Invalid dmfilemapd foreground argument. "
|
||||
"Must be 0 or 1: %d.", foreground);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (verbose > 3) {
|
||||
log_error("Invalid dmfilemapd verbose argument. "
|
||||
"Must be 0..3: %d.", verbose);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set argv[0] */
|
||||
args[argc++] = (char *) "dmfilemapd";
|
||||
|
||||
/* set <fd> */
|
||||
if ((dm_snprintf(fd_str, sizeof(fd_str), "%d", fd)) < 0) {
|
||||
log_error("Could not format fd argument.");
|
||||
return 0;
|
||||
}
|
||||
args[argc++] = fd_str;
|
||||
|
||||
/* set <group_id> */
|
||||
if ((dm_snprintf(group_str, sizeof(group_str), FMTu64, group_id)) < 0) {
|
||||
log_error("Could not format group_id argument.");
|
||||
return 0;
|
||||
}
|
||||
args[argc++] = group_str;
|
||||
|
||||
/* set <path> */
|
||||
args[argc++] = (char *) path;
|
||||
|
||||
/* set <foreground> */
|
||||
if ((dm_snprintf(fg_str, sizeof(fg_str), "%u", foreground)) < 0) {
|
||||
log_error("Could not format foreground argument.");
|
||||
return 0;
|
||||
}
|
||||
args[argc++] = fg_str;
|
||||
|
||||
/* set <verbose> */
|
||||
if ((dm_snprintf(verb_str, sizeof(verb_str), "%u", verbose)) < 0) {
|
||||
log_error("Could not format verbose argument.");
|
||||
return 0;
|
||||
}
|
||||
args[argc++] = verb_str;
|
||||
|
||||
/* terminate args[argc] */
|
||||
args[argc++] = NULL;
|
||||
|
||||
log_very_verbose("Spawning daemon as '%s %d " FMTu64 "%s %u %u'",
|
||||
*args, fd, group_id, path, foreground, verbose);
|
||||
|
||||
if ((pid = fork()) < 0) {
|
||||
log_error("Failed to fork filemapd process.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
log_very_verbose("Forked filemapd process as pid %d", pid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
execvp(args[0], args);
|
||||
log_error("execv() failed.");
|
||||
exit(127);
|
||||
}
|
||||
|
||||
#else /* HAVE_LINUX_FIEMAP */
|
||||
|
||||
uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
int group, int precise,
|
||||
struct dm_histogram *bounds,
|
||||
@@ -4544,6 +4911,20 @@ uint64_t *dm_stats_create_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
log_error("File mapping requires FIEMAP ioctl support.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t *dm_stats_update_regions_from_fd(struct dm_stats *dms, int fd,
|
||||
uint64_t group_id)
|
||||
{
|
||||
log_error("File mapping requires FIEMAP ioctl support.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_stats_start_filemapd(struct dm_stats *dms, int fd, uint64_t group_id,
|
||||
const char *path)
|
||||
{
|
||||
log_error("File mapping requires FIEMAP ioctl support.");
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_LINUX_FIEMAP */
|
||||
|
||||
/*
|
||||
|
152
tools/dmsetup.c
152
tools/dmsetup.c
@@ -196,6 +196,7 @@ enum {
|
||||
NOTABLE_ARG,
|
||||
NOTIMESUFFIX_ARG,
|
||||
UDEVCOOKIE_ARG,
|
||||
NOMONITOR_ARG,
|
||||
NOUDEVRULES_ARG,
|
||||
NOUDEVSYNC_ARG,
|
||||
OPTIONS_ARG,
|
||||
@@ -4958,16 +4959,8 @@ static char *_get_abspath(const char *path)
|
||||
return _path;
|
||||
}
|
||||
|
||||
static int _stats_create_file(CMD_ARGS)
|
||||
static int _stats_check_filemap_switches(void)
|
||||
{
|
||||
const char *alias, *program_id = DM_STATS_PROGRAM_ID;
|
||||
const char *bounds_str = _string_args[BOUNDS_ARG];
|
||||
uint64_t *regions, *region, count = 0;
|
||||
struct dm_histogram *bounds = NULL;
|
||||
char *path, *abspath = NULL;
|
||||
struct dm_stats *dms = NULL;
|
||||
int group, fd = -1, precise;
|
||||
|
||||
if (_switches[AREAS_ARG] || _switches[AREA_SIZE_ARG]) {
|
||||
log_error("--filemap is incompatible with --areas and --area-size.");
|
||||
return 0;
|
||||
@@ -4998,6 +4991,28 @@ static int _stats_create_file(CMD_ARGS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _stats_create_file(CMD_ARGS)
|
||||
{
|
||||
const char *alias, *program_id = DM_STATS_PROGRAM_ID;
|
||||
const char *bounds_str = _string_args[BOUNDS_ARG];
|
||||
uint64_t *regions, *region, count = 0;
|
||||
int verbose = _switches[VERBOSE_ARG];
|
||||
struct dm_histogram *bounds = NULL;
|
||||
char *path, *abspath = NULL;
|
||||
struct dm_stats *dms = NULL;
|
||||
int group, fd = -1, precise;
|
||||
|
||||
if (names) {
|
||||
err("Device names are not compatibile with --filemap.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_stats_check_filemap_switches())
|
||||
return 0;
|
||||
|
||||
/* _stats_create_file does not use _process_all() */
|
||||
if (!argc) {
|
||||
log_error("--filemap requires a file path argument");
|
||||
@@ -5072,6 +5087,10 @@ static int _stats_create_file(CMD_ARGS)
|
||||
regions = dm_stats_create_regions_from_fd(dms, fd, group, precise,
|
||||
bounds, alias);
|
||||
|
||||
if (!_switches[NOMONITOR_ARG] && group)
|
||||
if (!dm_stats_start_filemapd(fd, regions[0], abspath, 0, verbose))
|
||||
log_warn("Failed to start filemap monitoring daemon.");
|
||||
|
||||
if (close(fd))
|
||||
log_error("Error closing %s", abspath);
|
||||
|
||||
@@ -5599,6 +5618,99 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _stats_update_filemap(CMD_ARGS)
|
||||
{
|
||||
uint64_t group_id, *region, *regions, count = 0;
|
||||
const char *program_id = DM_STATS_PROGRAM_ID;
|
||||
struct dm_stats *dms;
|
||||
char *path, *abspath;
|
||||
int fd = -1;
|
||||
|
||||
if (names) {
|
||||
err("Device names are not compatibile with update_filemap.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_stats_check_filemap_switches())
|
||||
return 0;
|
||||
|
||||
/* _stats_update_filemap does not use _process_all() */
|
||||
if (!argc) {
|
||||
log_error("update_filemap requires a file path argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!_switches[GROUP_ID_ARG]) {
|
||||
err("--groupid is required to update a filemap group.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
path = argv[0];
|
||||
|
||||
if (!(abspath = _get_abspath(path))) {
|
||||
log_error("Could not canonicalize file name: %s", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
group_id = (uint64_t) _int_args[GROUP_ID_ARG];
|
||||
|
||||
if (_switches[PROGRAM_ID_ARG])
|
||||
program_id = _string_args[PROGRAM_ID_ARG];
|
||||
if (!strlen(program_id) && !_switches[FORCE_ARG])
|
||||
program_id = DM_STATS_PROGRAM_ID;
|
||||
|
||||
if (!(dms = dm_stats_create(DM_STATS_PROGRAM_ID)))
|
||||
goto_bad;
|
||||
|
||||
fd = open(abspath, O_RDONLY);
|
||||
|
||||
if (fd < 0) {
|
||||
log_error("Could not open %s for reading", abspath);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!_bind_stats_from_fd(dms, fd))
|
||||
goto_bad;
|
||||
|
||||
if (!strlen(program_id))
|
||||
/* force creation of a region with no id */
|
||||
dm_stats_set_program_id(dms, 1, NULL);
|
||||
|
||||
regions = dm_stats_update_regions_from_fd(dms, fd, group_id);
|
||||
|
||||
if (close(fd))
|
||||
log_error("Error closing %s", abspath);
|
||||
|
||||
fd = -1;
|
||||
|
||||
if (!regions) {
|
||||
log_error("Could not update regions from file %s", abspath);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
for (region = regions; *region != DM_STATS_REGIONS_ALL; region++)
|
||||
count++;
|
||||
|
||||
printf("%s: Updated group ID " FMTu64 " with "FMTu64" region(s).\n",
|
||||
path, group_id, count);
|
||||
|
||||
dm_free(regions);
|
||||
dm_free(abspath);
|
||||
dm_stats_destroy(dms);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
dm_free(abspath);
|
||||
|
||||
if ((fd > -1) && close(fd))
|
||||
log_error("Error closing %s", path);
|
||||
|
||||
if (dms)
|
||||
dm_stats_destroy(dms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command dispatch tables and usage.
|
||||
*/
|
||||
@@ -5609,9 +5721,9 @@ static int _stats_help(CMD_ARGS);
|
||||
* dmstats <cmd> [options] [device_name]
|
||||
*
|
||||
* clear [--regionid id] <device_name>
|
||||
* create [--areas nr_areas] [--areasize size]
|
||||
* create [[--areas nr_areas] [--areasize size]
|
||||
* [ [--start start] [--length len] | [--segments]]
|
||||
* [--userdata data] [--programid id] [<device_name>]
|
||||
* [--userdata data] [--programid id] [<device_name>] | --filemap <path>]
|
||||
* delete [--regionid] <device_name>
|
||||
* delete_all [--programid id]
|
||||
* group [--alias name] [--alldevices] [--regions <regions>] [<device_name>]
|
||||
@@ -5620,10 +5732,18 @@ static int _stats_help(CMD_ARGS);
|
||||
* report [--interval seconds] [--count count] [--units units] [--regionid id]
|
||||
* [--programid id] [<device>]
|
||||
* ungroup [--alldevices] [--groupid id] [<device_name>]
|
||||
* update_filemap --groupid id <file_path>
|
||||
*/
|
||||
|
||||
#define AREA_OPTS "[--areas <nr_areas>] [--areasize <size>] "
|
||||
#define CREATE_OPTS "[--start <start> [--length <len>]]\n\t\t" AREA_OPTS
|
||||
#define USERDATA_OPT "--userdata <data> "
|
||||
#define PROGID_OPT "--programid <id> "
|
||||
#define SEGMENTS_OPT "--segments "
|
||||
#define START_LEN_OPTS "[--start <start>] [--length <len>] "
|
||||
#define AREA_OPTS "[--areas <nr_areas>] [--areasize <size>]"
|
||||
#define CREATE_OPTS USERDATA_OPT PROGID_OPT "\n\t\t" "[[[[ " START_LEN_OPTS "\n\t\t" \
|
||||
AREA_OPTS "] |\n\t\t" SEGMENTS_OPT "] [<device_name]] |\n\t\t[--nomonitor] " \
|
||||
"--filemap <file_path> ]"
|
||||
|
||||
#define ID_OPTS "[--programid <id>] [--userdata <data> ] "
|
||||
#define SELECT_OPTS "[--programid <id>] [--regionid <id>] "
|
||||
#define PRINT_OPTS "[--clear] " SELECT_OPTS
|
||||
@@ -5633,13 +5753,14 @@ static int _stats_help(CMD_ARGS);
|
||||
static struct command _stats_subcommands[] = {
|
||||
{"help", "", 0, 0, 0, 0, _stats_help},
|
||||
{"clear", "--regionid <id> [<device>]", 0, -1, 1, 0, _stats_clear},
|
||||
{"create", CREATE_OPTS "\n\t\t" ID_OPTS "[<device>]", 0, -1, 1, 0, _stats_create},
|
||||
{"create", CREATE_OPTS , 0, -1, 1, 0, _stats_create},
|
||||
{"delete", "--regionid <id> <device>", 1, -1, 1, 0, _stats_delete},
|
||||
{"group", GROUP_OPTS, 1, -1, 1, 0, _stats_group},
|
||||
{"list", "[--programid <id>] [<device>]", 0, -1, 1, 0, _stats_report},
|
||||
{"print", PRINT_OPTS "[<device>]", 0, -1, 1, 0, _stats_print},
|
||||
{"report", REPORT_OPTS "[<device>]", 0, -1, 1, 0, _stats_report},
|
||||
{"ungroup", "--groupid <id> [device]", 1, -1, 1, 0, _stats_ungroup},
|
||||
{"update_filemap", "--groupid <id> <file_path>", 1, 1, 0, 0, _stats_update_filemap},
|
||||
{"version", "", 0, -1, 1, 0, _version},
|
||||
{NULL, NULL, 0, 0, 0, 0, NULL}
|
||||
};
|
||||
@@ -6271,6 +6392,7 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
|
||||
{"notable", 0, &ind, NOTABLE_ARG},
|
||||
{"notimesuffix", 0, &ind, NOTIMESUFFIX_ARG},
|
||||
{"udevcookie", 1, &ind, UDEVCOOKIE_ARG},
|
||||
{"nomonitor", 0, &ind, NOMONITOR_ARG},
|
||||
{"noudevrules", 0, &ind, NOUDEVRULES_ARG},
|
||||
{"noudevsync", 0, &ind, NOUDEVSYNC_ARG},
|
||||
{"options", 1, &ind, OPTIONS_ARG},
|
||||
@@ -6508,6 +6630,8 @@ static int _process_switches(int *argcp, char ***argvp, const char *dev_dir)
|
||||
_switches[UDEVCOOKIE_ARG]++;
|
||||
_udev_cookie = _get_cookie_value(optarg);
|
||||
}
|
||||
if (ind == NOMONITOR_ARG)
|
||||
_switches[NOMONITOR_ARG]++;
|
||||
if (ind == NOUDEVRULES_ARG)
|
||||
_switches[NOUDEVRULES_ARG]++;
|
||||
if (ind == NOUDEVSYNC_ARG)
|
||||
|
Reference in New Issue
Block a user