1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-09-26 01:44:19 +03:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Bryn M. Reeves
9e3ef29562 dmstats: launch dmfilemapd when creating file mapped groups 2016-12-16 13:44:42 +00:00
Bryn M. Reeves
146edfcdaa dmstats: add --nomonitor switch
Add a --nomonitor switch to disable spawning of dmfilemapd when
new file mapped groups are created.
2016-12-16 13:44:42 +00:00
Bryn M. Reeves
42f2193cd2 daemons: add dmfilemapd to daemons/dmfilemapd/.gitignore 2016-12-16 13:44:42 +00:00
Bryn M. Reeves
50d124652c libdm: add dm_stats_start_filemapd()
Add a function to the stats API to launch the dmfilemapd filemap
monitoring daemon.

This carries out the first fork and execs dmfilemapd with the
appropriate arguments.
2016-12-16 13:44:42 +00:00
Bryn M. Reeves
fe0997d1ed daemons: add dmfilemapd to update dmstats filemap groups
Add a daemon that can be launched to monitor a group of regions
corresponding to the extents of a file, and to update the regions
as the file's allocation changes.

The daemon is intended to be started from a library interfface,
but can also be run from the command line:

  dmfilemapd <fd> <group_id> <path> [<debug>[<log_level>]]

Where fd is a file descriptor open on the mapped file, group_id
is the group identifier of the mapped group and mode is either
"inode" or "path". E.g.:

  # dmfilemapd 3 0 vm.img 1 3 3<vm.img
  ...

If debug is non-zero, the daemon will not fork into the
background. If verbose is non-zero, libdm and daemon log messages
will be printed.
2016-12-16 08:52:28 +00:00
Bryn M. Reeves
2f949bec23 make: add dmfilemapd to daemons makefiles 2016-12-15 20:12:57 +00:00
Bryn M. Reeves
2573f714d0 configure: add --enable-dmfilemapd 2016-12-15 20:10:27 +00:00
Bryn M. Reeves
811355194a libdm: always list handle after updating regions from fd
The interface for dm_stats_update_regions_from_fd() states that
the handle will be listed on return:

 * 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.

Currently this only happens in the case that the regions need to
be re-grouped after the operation (i.e. the group leader was
deleted). Fix this to avoid a confusing state for cliend programs
where newly-created regions and group members are not reflected in
the handle.
2016-12-15 19:06:06 +00:00
Bryn M. Reeves
61552a6c96 libdm: clear region table in dm_stats_list()
Call _stats_regions_destroy() from dm_stats_list() if dms->regions
is non-NULL. This avoids leaking any pool allocations and ensures
the handle is in a known state: if an error occurs during the list,
dms->regions will be NULL and the handle will appear empty.
2016-12-15 19:03:42 +00:00
Bryn M. Reeves
65db92d396 dmstats: improve usage message 2016-12-14 19:42:24 +00:00
Bryn M. Reeves
7c89429db8 dmstats: allow --filemap groups to be updated
Add a new update_filemap command to dmstats that allows a filemap
group to be updated:

  # dmstats update_filemap --groupid 0 vm.img
  /var/lib/libvirt/images/vm.img: Updated group ID 0 with 137 region(s).

This will update the set of regions mapped to the file to reflect
the current file system allocation.

Currently this needs to be run manually - a future update will add
support for monitoring file maps via a daemon, allowing them to be
automatically updated when the underlying file is modified.
2016-12-14 19:42:24 +00:00
Bryn M. Reeves
d94c4e7678 libdm: add dm_stats_update_regions_from_fd()
Add a call to update the regions corresponding to a file mapped
group of regions. The regions to be updated must be grouped, to
allow us to correctly identify extents that have been deallocated
since the map was created.

Tables are built of the file extents, and the extents currently
mapped to dmstats regions: if a region no longer has a matching
file extent, it is deleted, and new regions are created for any
file extents without a matching region.

The FIEMAP call returns extents that are currently in-memory (or
journaled) and awaiting allocation in the file system. These have
the FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_DELALLOC flag bits set
in the fe_flags field - these extents are skipped until they
have a known disk location.

Since it is possile for the 0th extent of the file to have been
deallocated this must also handle the possible deletion and
re-creation of the group leader: if no other region allocation
is taking place the group identifier will not change.
2016-12-14 19:42:23 +00:00
Bryn M. Reeves
aa5c026d61 libdm: add extent table helpers
Add a pair of helper functions for searching tables of _extent
objects:

  /* test whether an extent matching start/len exists */
  _extent_in_extents(nr_extents, extents, start, len)

  /* return the extent matching star/len or NULL */
  _find_extent(nr_extents, extents, start, len)

The filemap remapping support will use these when filtering the
set of extents that need to be updated in order to reflect the
current state of a mapped file.
2016-12-14 11:45:28 +00:00
10 changed files with 1113 additions and 42 deletions

20
configure vendored
View File

@@ -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" ;;

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1 @@
dmfilemapd

View 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

View 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);
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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, &regroup)))
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, &regroup);
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 */
/*

View File

@@ -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)