From mboxrd@z Thu Jan 1 00:00:00 1970 From: Andreas Dilger Subject: Re: [PATCH 3/3] e2spacey: create a program to use getfsmap to profile free space Date: Thu, 2 Mar 2017 12:45:24 -0700 Message-ID: <438E3F6A-FF83-44EE-95A4-2184A5A5CD64@dilger.ca> References: <148843032650.21733.13980225191301375698.stgit@birch.djwong.org> <148843034776.21733.3790987196585797215.stgit@birch.djwong.org> Mime-Version: 1.0 (Mac OS X Mail 10.2 \(3259\)) Content-Type: multipart/signed; boundary="Apple-Mail=_77F6CEE0-B9C9-4B4B-815F-A83F363F56AB"; protocol="application/pgp-signature"; micalg=pgp-sha1 Cc: Theodore Ts'o , linux-ext4@vger.kernel.org To: "Darrick J. Wong" Return-path: Received: from mail-it0-f66.google.com ([209.85.214.66]:35168 "EHLO mail-it0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750787AbdCBURa (ORCPT ); Thu, 2 Mar 2017 15:17:30 -0500 Received: by mail-it0-f66.google.com with SMTP id 203so89618ith.2 for ; Thu, 02 Mar 2017 12:16:41 -0800 (PST) In-Reply-To: <148843034776.21733.3790987196585797215.stgit@birch.djwong.org> Sender: linux-ext4-owner@vger.kernel.org List-ID: --Apple-Mail=_77F6CEE0-B9C9-4B4B-815F-A83F363F56AB Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii On Mar 1, 2017, at 9:52 PM, Darrick J. Wong = wrote: >=20 > From: Darrick J. Wong >=20 > e2spacey is a new tool that uses the GETFSMAP ioctl and the FSGEOMETRY > ioctl to find all the free extents in a mounted filesystem and display > either per-flexbg free block counts or a histogram of free extents. > At this point it's mostly useful as a debugging tool. It would be better to wire the GETFSMAP ioctl into e2freefrag.c to use on mounted filesystems, rather than introducing a = new-but-almost-identical tool. e2freefrag has existed for a long time already and is doing this same thing (reporting a histogram of free extents). If there is some = new functionality that GETFSMAP allows, it could be added as a new option to e2freefrag as well. Otherwise, we have two tools to maintain, and we couldn't even deprecate e2freefrag (assuming we wanted to) since e2spacey only works on mounted filesystems. Cheers, Andreas > Signed-off-by: Darrick J. Wong > --- > Makefile.in | 3 > configure | 6 + > configure.ac | 5 - > lib/ext2fs/ext2_fs.h | 8 + > spacey/Makefile.in | 152 +++++++++++++++++ > spacey/e2spacey.8.in | 65 +++++++ > spacey/fsmap.h | 89 ++++++++++ > spacey/main.c | 456 = ++++++++++++++++++++++++++++++++++++++++++++++++++ > 8 files changed, 781 insertions(+), 3 deletions(-) > create mode 100644 spacey/Makefile.in > create mode 100644 spacey/e2spacey.8.in > create mode 100644 spacey/fsmap.h > create mode 100644 spacey/main.c >=20 >=20 > diff --git a/Makefile.in b/Makefile.in > index 7da9ad7..b48d0f9 100644 > --- a/Makefile.in > +++ b/Makefile.in > @@ -13,10 +13,11 @@ INSTALL =3D @INSTALL@ > @DEBUGFS_CMT@DEBUGFS_DIR=3D debugfs > @UUID_CMT@UUID_LIB_SUBDIR=3D lib/uuid > @BLKID_CMT@BLKID_LIB_SUBDIR=3D lib/blkid > +@SPACEY_CMT@SPACEY_DIR=3D spacey > SUPPORT_LIB_SUBDIR=3D lib/support >=20 > LIB_SUBDIRS=3Dlib/et lib/ss lib/e2p $(UUID_LIB_SUBDIR) = $(BLKID_LIB_SUBDIR) $(SUPPORT_LIB_SUBDIR) lib/ext2fs intl > -PROG_SUBDIRS=3De2fsck $(DEBUGFS_DIR) misc $(RESIZE_DIR) tests/progs = po > +PROG_SUBDIRS=3De2fsck $(DEBUGFS_DIR) misc $(RESIZE_DIR) tests/progs = po $(SPACEY_DIR) > SUBDIRS=3Dutil $(LIB_SUBDIRS) $(PROG_SUBDIRS) tests >=20 > SUBS=3D util/subst.conf lib/config.h $(top_builddir)/lib/dirpaths.h \ > diff --git a/configure b/configure > index b553da1..940b11c 100755 > --- a/configure > +++ b/configure > @@ -642,6 +642,7 @@ root_prefix > UNIX_CMT > CYGWIN_CMT > LINUX_CMT > +SPACEY_CMT > E2INFO_CMT > UNI_DIFF_OPTS > SEM_INIT_LIB > @@ -13665,15 +13666,18 @@ fi > $as_echo "$UNI_DIFF_OPTS" >&6; } >=20 > E2INFO_CMT=3D"#" > +SPACEY_CMT=3D"#" > case "$host_os" in > linux*) >=20 > $as_echo "#define HAVE_EXT2_IOCTLS 1" >>confdefs.h >=20 > E2INFO_CMT=3D > + SPACEY_CMT=3D > ;; > esac >=20 > + > LINUX_CMT=3D"#" > CYGWIN_CMT=3D"#" > UNIX_CMT=3D > @@ -13875,7 +13879,7 @@ for i in MCONFIG Makefile e2fsprogs.spec \ > misc/Makefile ext2ed/Makefile e2fsck/Makefile \ > debugfs/Makefile tests/Makefile tests/progs/Makefile \ > resize/Makefile doc/Makefile intl/Makefile \ > - intl/libgnuintl.h po/Makefile.in ; do > + intl/libgnuintl.h po/Makefile.in spacey/Makefile ; do > if test -d `dirname ${srcdir}/$i` ; then > outlist=3D"$outlist $i" > fi > diff --git a/configure.ac b/configure.ac > index bf613fd..5b95f6b 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -1261,13 +1261,16 @@ dnl > dnl We use the EXT2 ioctls only under Linux > dnl > E2INFO_CMT=3D"#" > +SPACEY_CMT=3D"#" > case "$host_os" in > linux*) > AC_DEFINE(HAVE_EXT2_IOCTLS, 1, [Define to 1 if Ext2 ioctls = present]) > E2INFO_CMT=3D > + SPACEY_CMT=3D > ;; > esac > AC_SUBST(E2INFO_CMT) > +AC_SUBST(SPACEY_CMT) > dnl > dnl OS-specific uncomment control > dnl > @@ -1469,7 +1472,7 @@ for i in MCONFIG Makefile e2fsprogs.spec \ > misc/Makefile ext2ed/Makefile e2fsck/Makefile \ > debugfs/Makefile tests/Makefile tests/progs/Makefile \ > resize/Makefile doc/Makefile intl/Makefile \ > - intl/libgnuintl.h po/Makefile.in ; do > + intl/libgnuintl.h po/Makefile.in spacey/Makefile ; do > if test -d `dirname ${srcdir}/$i` ; then > outlist=3D"$outlist $i" > fi > diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h > index bad7648..4beebb6 100644 > --- a/lib/ext2fs/ext2_fs.h > +++ b/lib/ext2fs/ext2_fs.h > @@ -406,6 +406,14 @@ struct ext4_fsop_geom { >=20 > #define EXT4_IOC_FSGEOMETRY _IOR ('f', 19, struct = ext4_fsop_geom) >=20 > +/* Custom FS_IOC_GETFSMAP owner codes */ > +#define EXT4_FMR_OWN_FREE FMR_OWN_FREE /* free space */ > +#define EXT4_FMR_OWN_UNKNOWN FMR_OWN_UNKNOWN /* unknown owner */ > +#define EXT4_FMR_OWN_GDT FMR_OWNER('f', 1) /* group descriptors = */ > +#define EXT4_FMR_OWN_RESV_GDT FMR_OWNER('f', 2) /* reserved = gdt blocks */ > +#define EXT4_FMR_OWN_BLKBM FMR_OWNER('f', 3) /* inode bitmap */ > +#define EXT4_FMR_OWN_INOBM FMR_OWNER('f', 4) /* block bitmap */ > + > /* > * Structure of an inode on the disk > */ > diff --git a/spacey/Makefile.in b/spacey/Makefile.in > new file mode 100644 > index 0000000..3c842f3 > --- /dev/null > +++ b/spacey/Makefile.in > @@ -0,0 +1,152 @@ > +# > +# Makefile for e2spacey > +# > + > +srcdir =3D @srcdir@ > +top_srcdir =3D @top_srcdir@ > +VPATH =3D @srcdir@ > +top_builddir =3D .. > +my_dir =3D scrub > +INSTALL =3D @INSTALL@ > + > +@MCONFIG@ > + > +PROGS=3D e2spacey > +MANPAGES=3D e2spacey.8 > + > +LIBS=3D $(LIBSUPPORT) $(LIBEXT2FS) $(LIBCOM_ERR) $(LIBBLKID) = $(LIBUUID) \ > + $(LIBINTL) $(LIBE2P) $(LIBMAGIC) $(SYSLIBS) > +DEPLIBS=3D $(DEPLIBSUPPORT) $(LIBEXT2FS) $(DEPLIBCOM_ERR) = $(DEPLIBBLKID) \ > + $(DEPLIBUUID) $(DEPLIBE2P) > + > +STATIC_LIBS=3D $(STATIC_LIBSUPPORT) $(STATIC_LIBEXT2FS) = $(STATIC_LIBCOM_ERR) \ > + $(STATIC_LIBBLKID) $(STATIC_LIBUUID) $(LIBINTL) = $(STATIC_LIBE2P) \ > + $(LIBMAGIC) $(SYSLIBS) > +STATIC_DEPLIBS=3D $(DEPSTATIC_LIBSUPPORT) $(STATIC_LIBEXT2FS) \ > + $(DEPSTATIC_LIBCOM_ERR) $(DEPSTATIC_LIBBLKID) \ > + $(DEPSTATIC_LIBUUID) $(DEPSTATIC_LIBE2P) > + > +PROFILED_LIBS=3D $(PROFILED_LIBSUPPORT) $(PROFILED_LIBEXT2FS) \ > + $(PROFILED_LIBCOM_ERR) $(PROFILED_LIBBLKID) = $(PROFILED_LIBUUID) \ > + $(PROFILED_LIBE2P) $(LIBINTL) $(LIBMAGIC) $(SYSLIBS) > +PROFILED_DEPLIBS=3D $(DEPPROFILED_LIBSUPPORT) $(PROFILED_LIBEXT2FS) \ > + $(DEPPROFILED_LIBCOM_ERR) $(DEPPROFILED_LIBBLKID) \ > + $(DEPPROFILED_LIBUUID) $(DEPPROFILED_LIBE2P) > + > +COMPILE_ET=3D _ET_DIR_OVERRIDE=3D$(srcdir)/../lib/et/et = ../lib/et/compile_et > + > +.c.o: > + $(E) " CC $<" > + $(Q) $(CC) -c $(ALL_CFLAGS) $< -o $@ > + $(Q) $(CHECK_CMD) $(ALL_CFLAGS) $< > + $(Q) $(CPPCHECK_CMD) $(CPPFLAGS) $< > +@PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o = -c $< > + > +# > +# Flags for doing mtrace --- uncomment to produce mtracing e2spacey > +# Note: The optimization flags must include -g > +# > +#MTRACE=3D -DMTRACE > +#MTRACE_OBJ=3D mtrace.o > +#MTRACE_SRC=3D $(srcdir)/mtrace.c > +#OPT=3D -g > + > +# > +# Flags for doing mcheck --- uncomment to produce mchecking e2spacey > +# Note: The optimization flags must include -g > +# > +#MCHECK=3D -DMCHECK > + > +OBJS=3D main.o > + > +PROFILED_OBJS=3D profiled/main.o > + > +SRCS=3D $(srcdir)/main.o > + > +all:: profiled $(PROGS) $(MANPAGES) > + > +@PROFILE_CMT@all:: e2spacey.profiled > + > +e2spacey: $(OBJS) $(DEPLIBS) > + $(E) " LD $@" > + $(Q) $(LD) $(ALL_LDFLAGS) $(RDYNAMIC) -o e2spacey $(OBJS) = $(LIBS) > + > +e2spacey.static: $(OBJS) $(STATIC_DEPLIBS) > + $(E) " LD $@" > + $(Q) $(LD) $(LDFLAGS_STATIC) -o e2spacey.static $(OBJS) = $(STATIC_LIBS) -lpthread > + > +e2spacey.profiled: $(OBJS) $(PROFILED_DEPLIBS) > + $(E) " LD $@" > + $(Q) $(LD) $(ALL_LDFLAGS) -g -pg -o e2spacey.profiled = $(PROFILED_OBJS) \ > + $(PROFILED_LIBS) -lpthread > + > +test_profile: $(srcdir)/profile.c profile_helpers.o argv_parse.o \ > + prof_err.o profile.h $(DEPSTATIC_LIBCOM_ERR) > + $(E) " LD $@" > + $(Q) $(CC) -o test_profile -DDEBUG_PROGRAM $(srcdir)/profile.c = prof_err.o \ > + profile_helpers.o argv_parse.o $(STATIC_LIBCOM_ERR) \ > + $(ALL_CFLAGS) > + > +profiled: > +@PROFILE_CMT@ $(E) " MKDIR $@" > +@PROFILE_CMT@ $(Q) mkdir profiled > + > +e2spacey.8: $(DEP_SUBSTITUTE) $(srcdir)/e2spacey.8.in > + $(E) " SUBST $@" > + $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2spacey.8.in e2spacey.8 > + > +installdirs: > + $(E) " MKINSTALLDIRS $(root_sbindir) $(man8dir)" > + $(Q) $(MKINSTALLDIRS) $(DESTDIR)$(root_sbindir) \ > + $(DESTDIR)$(man8dir) $(DESTDIR)$(man5dir) > + > +install: $(PROGS) $(MANPAGES) $(FMANPAGES) installdirs > + $(Q) for i in $(PROGS); do \ > + $(ES) " INSTALL $(root_sbindir)/$$i"; \ > + $(INSTALL_PROGRAM) $$i $(DESTDIR)$(root_sbindir)/$$i; \ > + done > + $(Q) for i in $(MANPAGES); do \ > + for j in $(COMPRESS_EXT); do \ > + $(RM) -f $(DESTDIR)$(man8dir)/$$i.$$j; \ > + done; \ > + $(ES) " INSTALL_DATA $(man8dir)/$$i"; \ > + $(INSTALL_DATA) $$i $(DESTDIR)$(man8dir)/$$i; \ > + done > + > +install-strip: install > + $(Q) for i in $(PROGS); do \ > + $(ES) " STRIP $(root_sbindir)/$$i"; \ > + $(STRIP) $(DESTDIR)$(root_sbindir)/$$i; \ > + done > + > +uninstall: > + for i in $(PROGS); do \ > + $(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \ > + done > + for i in $(MANPAGES); do \ > + $(RM) -f $(DESTDIR)$(man8dir)/$$i; \ > + done > + > +clean:: > + $(RM) -f $(PROGS) > + $(RM) -rf profiled > + > +mostlyclean: clean > +distclean: clean > + $(RM) -f .depend Makefile $(srcdir)/TAGS = $(srcdir)/Makefile.in.old > + > +# +++ Dependency line eater +++ > +# > +# Makefile dependencies follow. This must be the last section in > +# the Makefile.in file > +# > +main.o: $(srcdir)/main.c $(top_builddir)/lib/config.h \ > + $(top_builddir)/lib/dirpaths.h $(srcdir)/fsmap.h \ > + $(top_srcdir)/lib/ext2fs/ext2_fs.h = $(top_builddir)/lib/ext2fs/ext2_types.h \ > + $(top_srcdir)/lib/ext2fs/ext2fs.h = $(top_srcdir)/lib/ext2fs/ext3_extents.h \ > + $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ > + $(top_builddir)/lib/ext2fs/ext2_err.h \ > + $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h = $(top_srcdir)/lib/ext2fs/bitops.h \ > + $(top_srcdir)/lib/support/profile.h = $(top_builddir)/lib/support/prof_err.h \ > + $(top_srcdir)/lib/support/quotaio.h = $(top_srcdir)/lib/support/dqblk_v2.h \ > + $(top_srcdir)/lib/support/quotaio_tree.h > diff --git a/spacey/e2spacey.8.in b/spacey/e2spacey.8.in > new file mode 100644 > index 0000000..f4956fc > --- /dev/null > +++ b/spacey/e2spacey.8.in > @@ -0,0 +1,65 @@ > +.TH E2SPACEY 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs = version @E2FSPROGS_VERSION@" > +.SH NAME > +e2spacey \- show free space information about an ext4 filesystem > +.SH SYNOPSIS > +.B e2spacey > +[ > +.B \-srg > +] > +[ > +.B \-b > +| > +.B \-e > +.I bsize > +| > +.B \-h > +.I h1 > +[ > +.B \-m > +.I bmult > +] > +] > +[ > +.B \-a > +.I group > +] > +.I mountpoint > +.br > +.B e2spacey \-V > +.SH DESCRIPTION > +.B e2spacey > +reports and controls free space usage in an ext4 filesystem. > +When the > +.B flex_bg > +feature is enabled, the free space information is grouped by flex = block groups > +instead of regular block groups. > +This enables reporting of free space extents that span multiple block = groups. > + > +.SH OPTIONS > +.TP 1.0i > +.BI \-a " group" > +Only query the specified block group's free space information. > +Multiple options may be provided. > +.TP > +.B \-b > +Establish histogram bins sized in successive powers of two. > +.TP > +.B \-e > +Fix the histogram bin size to a specific value. > +.TP > +.B \-g > +Instead of printing a histogram, simply report the number of free = extents > +and number of free blocks per block group. > +.TP > +.BI \-h " hist" > +Fix the histogram bin size to a specific initial value. > +Use this in comination with the > +.B \-m > +option to specify a custom histgram bin size growth function. > +.TP > +.BI \-m " mult" > +The size of each histogram bin should be computed by multiplying the > +previous bin's size by this quantity. > +.TP > +.B \-s > +Print a summary of the free space before exiting. > diff --git a/spacey/fsmap.h b/spacey/fsmap.h > new file mode 100644 > index 0000000..3c5084c > --- /dev/null > +++ b/spacey/fsmap.h > @@ -0,0 +1,89 @@ > +/* > + * Copyright (c) 2017 Oracle. > + * All Rights Reserved. > + * > + * Author: Darrick J. Wong > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it would be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write the Free Software = Foundation, > + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + */ > +#ifndef FSMAP_H_ > +#define FSMAP_H_ > + > +/* FS_IOC_GETFSMAP ioctl definitions */ > +#ifndef FS_IOC_GETFSMAP > +struct fsmap { > + __u32 fmr_device; /* device id */ > + __u32 fmr_flags; /* mapping flags */ > + __u64 fmr_physical; /* device offset of segment */ > + __u64 fmr_owner; /* owner id */ > + __u64 fmr_offset; /* file offset of segment */ > + __u64 fmr_length; /* length of segment */ > + __u64 fmr_reserved[3]; /* must be zero */ > +}; > + > +struct fsmap_head { > + __u32 fmh_iflags; /* control flags */ > + __u32 fmh_oflags; /* output flags */ > + __u32 fmh_count; /* # of entries in array incl. = input */ > + __u32 fmh_entries; /* # of entries filled in = (output). */ > + __u64 fmh_reserved[6]; /* must be zero */ > + > + struct fsmap fmh_keys[2]; /* low and high keys for the = mapping search */ > + struct fsmap fmh_recs[]; /* returned records */ > +}; > + > +/* Size of an fsmap_head with room for nr records. */ > +static inline size_t > +fsmap_sizeof( > + unsigned int nr) > +{ > + return sizeof(struct fsmap_head) + nr * sizeof(struct fsmap); > +} > + > +/* Start the next fsmap query at the end of the current query = results. */ > +static inline void > +fsmap_advance( > + struct fsmap_head *head) > +{ > + head->fmh_keys[0] =3D head->fmh_recs[head->fmh_entries - 1]; > +} > + > +/* fmh_iflags values - set by FS_IOC_GETFSMAP caller in the header. = */ > +/* no flags defined yet */ > +#define FMH_IF_VALID 0 > + > +/* fmh_oflags values - returned in the header segment only. */ > +#define FMH_OF_DEV_T 0x1 /* fmr_device values will be = dev_t */ > + > +/* fmr_flags values - returned for each non-header segment */ > +#define FMR_OF_PREALLOC 0x1 /* segment =3D unwritten = pre-allocation */ > +#define FMR_OF_ATTR_FORK 0x2 /* segment =3D attribute fork */ > +#define FMR_OF_EXTENT_MAP 0x4 /* segment =3D extent map */ > +#define FMR_OF_SHARED 0x8 /* segment =3D shared = with another file */ > +#define FMR_OF_SPECIAL_OWNER 0x10 /* owner is a special value */ > +#define FMR_OF_LAST 0x20 /* segment is the last in the FS = */ > + > +/* Each FS gets to define its own special owner codes. */ > +#define FMR_OWNER(type, code) (((__u64)type << 32) | \ > + ((__u64)code & 0xFFFFFFFFULL)) > +#define FMR_OWNER_TYPE(owner) ((__u32)((__u64)owner >> 32)) > +#define FMR_OWNER_CODE(owner) ((__u32)(((__u64)owner & = 0xFFFFFFFFULL))) > +#define FMR_OWN_FREE FMR_OWNER(0, 1) /* free space */ > +#define FMR_OWN_UNKNOWN FMR_OWNER(0, 2) /* unknown owner = */ > +#define FMR_OWN_METADATA FMR_OWNER(0, 3) /* metadata */ > + > +#define FS_IOC_GETFSMAP _IOWR('X', 59, struct = fsmap_head) > +#endif /* FS_IOC_GETFSMAP */ > + > +#endif > diff --git a/spacey/main.c b/spacey/main.c > new file mode 100644 > index 0000000..009a965 > --- /dev/null > +++ b/spacey/main.c > @@ -0,0 +1,456 @@ > +/* > + * Copyright (c) 2017 Oracle. > + * All Rights Reserved. > + * > + * Author: Darrick J. Wong > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it would be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write the Free Software = Foundation, > + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + */ > +#include "config.h" > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include "ext2fs/ext2_types.h" > +#include "ext2fs/ext2fs.h" > +#include "fsmap.h" > +#include "ext2fs/ext2_fs.h" > + > +#include "../version.h" > +#include "support/nls-enable.h" > + > +static const char *progname =3D "e2spaceman"; > + > +struct histent > +{ > + blk64_t low; > + blk64_t high; > + size_t count; > + blk64_t blocks; > +}; > + > +static struct ext4_fsop_geom fsgeo; > +static struct ext4_fsop_geom flexgeo; > + > +static struct histent *hist; > +static size_t histcount; > + > +static dgrp_t *bglist; > +static size_t bgcount; > + > +static blk64_t totblocks; > +static long long totexts; > + > +static int equalsize; > +static int multsize; > + > +static int seen1; > +static int dumpflag; > +static int gflag; > +static const char *filename; > +static dev_t datadev; > + > +static inline dgrp_t f2b(dgrp_t fgroup) > +{ > + return fsgeo.efg_flexbgsize ? fgroup * fsgeo.efg_flexbgsize : = fgroup; > +} > + > +static inline dgrp_t b2f(dgrp_t group) > +{ > + return fsgeo.efg_flexbgsize ? group / fsgeo.efg_flexbgsize : = group; > +} > + > +static void usage(void) > +{ > + fprintf(stderr, _( > +"Usage: %s [options] mountpoint\n\n\ > + -V print version information\n"), > + progname); > + exit(2); > +} > + > +#define PROC_MOUNTS "/proc/mounts" > +int find_datadev(const char *arg, dev_t *datadev, char *mntpoint) > +{ > + struct mntent *mnt; > + FILE *mtp; > + char *mtab_file; > + char rmnt_fsname[PATH_MAX], rmnt_dir[PATH_MAX]; > + struct stat sbuf; > + dev_t fd_dev; > + > + if (stat(arg, &sbuf) < 0) > + return errno; > + /* > + * We want to match st_rdev if the path provided is a device > + * special file. Otherwise we are looking for the the > + * device id for the containing filesystem, in st_dev. > + */ > + if (S_ISBLK(sbuf.st_mode) || S_ISCHR(sbuf.st_mode)) > + fd_dev =3D sbuf.st_rdev; > + else > + fd_dev =3D sbuf.st_dev; > + > + mtab_file =3D PROC_MOUNTS; > + if (access(mtab_file, R_OK) !=3D 0) > + mtab_file =3D MOUNTED; > + > + if ((mtp =3D setmntent(mtab_file, "r")) =3D=3D NULL) > + return ENOENT; > + > + while ((mnt =3D getmntent(mtp)) !=3D NULL) { > + if (!realpath(mnt->mnt_dir, rmnt_dir)) > + continue; > + if (!realpath(mnt->mnt_fsname, rmnt_fsname)) > + continue; > + > + if (stat(rmnt_fsname, &sbuf) < 0) > + continue; > + if (sbuf.st_rdev =3D=3D fd_dev) { > + *datadev =3D fd_dev; > + strncpy(mntpoint, rmnt_dir, PATH_MAX); > + break; > + } > + } > + endmntent(mtp); > + return 0; > +} > + > +static void addhistent(blk64_t h) > +{ > + hist =3D realloc(hist, (histcount + 1) * sizeof(*hist)); > + if (!hist) { > + perror("addhistent"); > + exit(2); > + } > + if (h =3D=3D 0) > + h =3D 1; > + hist[histcount].low =3D h; > + hist[histcount].count =3D hist[histcount].blocks =3D 0; > + histcount++; > + if (h =3D=3D 1) > + seen1 =3D 1; > +} > + > +static void addtohist(dgrp_t group, blk64_t fsb, blk64_t len) > +{ > + size_t i; > + > + if (dumpflag) > + printf("%8u %8llu %8llu\n", group, fsb, len); > + totexts++; > + totblocks +=3D len; > + for (i =3D 0; i < histcount; i++) { > + if (hist[i].high >=3D len) { > + hist[i].count++; > + hist[i].blocks +=3D len; > + break; > + } > + } > +} > + > +static int hcmp(const void *a, const void *b) > +{ > + return ((struct histent *)a)->low - ((struct histent *)b)->low; > +} > + > +static void histinit(blk64_t maxlen) > +{ > + blk64_t i; > + > + if (equalsize) { > + for (i =3D 1; i < maxlen; i +=3D equalsize) > + addhistent(i); > + } else if (multsize) { > + for (i =3D 1; i < maxlen; i *=3D multsize) > + addhistent(i); > + } else { > + if (!seen1) > + addhistent(1); > + qsort(hist, histcount, sizeof(*hist), hcmp); > + } > + for (i =3D 0; i < histcount; i++) { > + if (i < histcount - 1) > + hist[i].high =3D hist[i + 1].low - 1; > + else > + hist[i].high =3D maxlen; > + } > +} > + > +static void printhist(void) > +{ > + size_t i; > + > + printf("%7s %7s %7s %7s %6s\n", > + _("from"), _("to"), _("extents"), _("blocks"), = _("pct")); > + for (i =3D 0; i < histcount; i++) { > + if (hist[i].count) > + printf("%7llu %7llu %7Zu %7llu %6.2f\n", = hist[i].low, > + hist[i].high, hist[i].count, = hist[i].blocks, > + hist[i].blocks * 100.0 / totblocks); > + } > +} > + > +static int inbglist(dgrp_t fgroup) > +{ > + size_t i; > + > + if (bgcount =3D=3D 0) > + return 1; > + for (i =3D 0; i < bgcount; i++) > + if (b2f(bglist[i]) =3D=3D fgroup) > + return 1; > + return 0; > +} > + > +#define NR_EXTENTS 1024 > + > +static void scan_bg(int fd, dgrp_t fgroup) > +{ > + struct fsmap_head *fsmap; > + struct fsmap *extent; > + struct fsmap *l, *h; > + struct fsmap *p; > + off64_t blocksize =3D flexgeo.efg_blocksize; > + off64_t bytes_per_bg; > + off64_t len; > + blk64_t fsb; > + blk64_t freeblks =3D 0; > + blk64_t freeexts =3D 0; > + int ret; > + int i; > + > + bytes_per_bg =3D (off64_t)flexgeo.efg_bgblocks * blocksize; > + > + fsmap =3D malloc(fsmap_sizeof(NR_EXTENTS)); > + if (!fsmap) { > + fprintf(stderr, _("%s: fsmap malloc failed.\n"), = progname); > + exit(1); > + } > + > + memset(fsmap, 0, sizeof(*fsmap)); > + fsmap->fmh_count =3D NR_EXTENTS; > + l =3D fsmap->fmh_keys; > + h =3D fsmap->fmh_keys + 1; > + l->fmr_physical =3D fgroup * bytes_per_bg; > + h->fmr_physical =3D ((fgroup + 1) * bytes_per_bg) - 1; > + l->fmr_device =3D h->fmr_device =3D datadev; > + h->fmr_owner =3D ULLONG_MAX; > + h->fmr_flags =3D UINT_MAX; > + h->fmr_offset =3D ULLONG_MAX; > + > + while (1) { > + ret =3D ioctl(fd, FS_IOC_GETFSMAP, fsmap); > + if (ret < 0) { > + fprintf(stderr, > +_("%s: FS_IOC_GETFSMAP [\"%s\"]: %s\n"), > + progname, filename, strerror(errno)); > + free(fsmap); > + exit(1); > + } > + > + /* No more extents to map, exit */ > + if (!fsmap->fmh_entries) > + break; > + > + for (i =3D 0, extent =3D fsmap->fmh_recs; > + i < fsmap->fmh_entries; > + i++, extent++) { > + if (!(extent->fmr_flags & FMR_OF_SPECIAL_OWNER) = || > + extent->fmr_owner !=3D FMR_OWN_FREE) > + continue; > + > + fsb =3D extent->fmr_physical / blocksize; > + len =3D extent->fmr_length / blocksize; > + freeblks +=3D len; > + freeexts++; > + > + addtohist(f2b(fgroup), fsb, len); > + } > + > + p =3D &fsmap->fmh_recs[fsmap->fmh_entries - 1]; > + if (p->fmr_flags & FMR_OF_LAST) > + break; > + fsmap_advance(fsmap); > + } > + > + if (gflag) > + printf(_("%10u %10llu %10llu\n"), f2b(fgroup), freeexts, > + freeblks); > +} > + > +static void bglistadd(char *a) > +{ > + bglist =3D realloc(bglist, (bgcount + 1) * sizeof(*bglist)); > + bglist[bgcount] =3D (dgrp_t)atoi(a); > + bgcount++; > +} > + > + > +static void setup_fsgeo(int fd) > +{ > + /* Get the current filesystem size & geometry */ > + if (ioctl(fd, EXT4_IOC_FSGEOMETRY, &fsgeo) < 0) { > + fprintf(stderr, _( > + "%s: cannot determine geometry of ext4 = filesystem" > + " mounted at %s.\n"), > + progname, filename); > + exit(1); > + } > + > + /* > + * Refactor the geometry so that a "group" is the size of the = smallest > + * number of BGs required to contain the longest free extent = possible. > + * > + * (IOWs, we handle everything in terms of flexbgs.) > + */ > + flexgeo =3D fsgeo; > + if (fsgeo.efg_flexbgsize) { > + flexgeo.efg_bgblocks *=3D fsgeo.efg_flexbgsize; > + flexgeo.efg_bgcount =3D (fsgeo.efg_bgcount + > + fsgeo.efg_flexbgsize - 1) / > + fsgeo.efg_flexbgsize; > + flexgeo.efg_bg_iblocks *=3D fsgeo.efg_flexbgsize; > + flexgeo.efg_flexbgsize =3D 0; > + } > +} > + > +int main(int argc, char **argv) > +{ > + char *progname; > + int fd; > + int c; > + char mntpoint[PATH_MAX]; > + blk64_t max_extent; > + int speced, summaryflag; > + dgrp_t fgroup; > + > + speced =3D dumpflag =3D summaryflag =3D gflag =3D 0; > + > + progname =3D basename(argv[0]); > +#ifdef ENABLE_NLS > + setlocale(LC_ALL, ""); > + bindtextdomain(PACKAGE, LOCALEDIR); > + textdomain(PACKAGE); > +#endif > + > + while ((c =3D getopt(argc, argv, "a:bde:gh:m:sV")) !=3D EOF) { > + switch (c) { > + case 'a': > + bglistadd(optarg); > + break; > + case 'b': > + if (speced) > + return 0; > + multsize =3D 2; > + speced =3D 1; > + break; > + case 'd': > + dumpflag =3D 1; > + break; > + case 'e': > + if (speced) > + return 0; > + equalsize =3D atoi(optarg); > + speced =3D 1; > + break; > + case 'g': > + histcount =3D 0; > + gflag++; > + break; > + case 'h': > + if (speced && !histcount) > + return 0; > + addhistent(atoi(optarg)); > + speced =3D 1; > + break; > + case 'm': > + if (speced) > + return 0; > + multsize =3D atoi(optarg); > + speced =3D 1; > + break; > + case 's': > + summaryflag =3D 1; > + break; > + case 'V': > + printf(_("%s version %s\n"), progname, VERSION); > + exit(0); > + case '?': > + default: > + usage(); > + } > + } > + if (argc - optind !=3D 1) > + usage(); > + > + filename =3D argv[optind]; > + fd =3D open(argv[optind], O_RDONLY); > + if (fd < 0) { > + perror(argv[optind]); > + return 1; > + } > + > + if (find_datadev(argv[optind], &datadev, mntpoint)) { > + perror("find_datadev"); > + exit(1); > + } > + > + close(fd); > + fd =3D open(mntpoint, O_RDONLY); > + if (fd < 0) { > + perror(mntpoint); > + return 1; > + } > + > + setup_fsgeo(fd); > + > + /* Set up histogram */ > + max_extent =3D flexgeo.efg_bgblocks; > + > + if (!speced) > + multsize =3D 2; > + histinit(max_extent); > + > + /* Collect data and print */ > + if (gflag) > + printf(_(" AG extents blocks\n")); > + for (fgroup =3D 0; fgroup < flexgeo.efg_bgcount; fgroup++) { > + if (inbglist(fgroup)) > + scan_bg(fd, fgroup); > + } > + if (histcount && !gflag) > + printhist(); > + if (summaryflag) { > + printf(_("total free extents %lld\n"), totexts); > + printf(_("total free blocks %lld\n"), totblocks); > + printf(_("average free extent size %g\n"), > + (double)totblocks / (double)totexts); > + } > + if (bglist) > + free(bglist); > + if (hist) > + free(hist); > + close(fd); > + > + return 0; > +} >=20 Cheers, Andreas --Apple-Mail=_77F6CEE0-B9C9-4B4B-815F-A83F363F56AB Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=signature.asc Content-Type: application/pgp-signature; name=signature.asc Content-Description: Message signed with OpenPGP -----BEGIN PGP SIGNATURE----- Comment: GPGTools - http://gpgtools.org iD8DBQFYuHZVpIg59Q01vtYRAqheAJ4m6RQZ/ZvGtlpEUGldI2os5NTlaQCfQXU3 mt+sdCICS/naobjZ7IeTi14= =Adi0 -----END PGP SIGNATURE----- --Apple-Mail=_77F6CEE0-B9C9-4B4B-815F-A83F363F56AB--