]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
fadvise: new command wrapping posix_fadvise
authorMasatake YAMATO <yamato@redhat.com>
Fri, 23 Sep 2022 22:10:51 +0000 (07:10 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sat, 1 Oct 2022 01:14:45 +0000 (10:14 +0900)
For measuring and monitoring the performance aspect of a command,
people may want to drop the page caches related to specified files.

In some cases, writing 1 to /proc/sys/vm/drop_caches is overkill.  The
fadvise command can be used for dropping page caches related to
specified files; the impact on a system is much smaller than
/proc/sys/vm/drop_caches.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
configure.ac
misc-utils/Makemodule.am
misc-utils/fadvise.1.adoc [new file with mode: 0644]
misc-utils/fadvise.c [new file with mode: 0644]
misc-utils/meson.build
tests/commands.sh
tests/expected/fadvise/drop [new file with mode: 0644]
tests/ts/fadvise/drop [new file with mode: 0644]

index 3920ac6596dab091126108ff615fe302e42e2ce4..e760889ccf682d011f632ee059eba47ebb1eb446 100644 (file)
@@ -1794,6 +1794,10 @@ UL_BUILD_INIT([pipesz])
 UL_REQUIRES_LINUX([pipesz])
 AM_CONDITIONAL([BUILD_PIPESZ], [test "x$build_pipesz" = xyes])
 
+UL_BUILD_INIT([fadvise], [check])
+UL_REQUIRES_LINUX([fadvise])
+AM_CONDITIONAL([BUILD_FADVISE], [test "x$build_fadvise" = xyes])
+
 UL_BUILD_INIT([getopt], [yes])
 AM_CONDITIONAL([BUILD_GETOPT], [test "x$build_getopt" = xyes])
 
index 2c9f7ead5c5afad0c2e6e95a92e7fe53c6ab7b27..74bc2d1db0f72237f326bbdabe9c0507e0bdfd1f 100644 (file)
@@ -279,3 +279,12 @@ pipesz_SOURCES = misc-utils/pipesz.c
 pipesz_LDADD = $(LDADD) libcommon.la
 pipesz_CFLAGS = $(AM_CFLAGS)
 endif
+
+if BUILD_FADVISE
+usrbin_exec_PROGRAMS += fadvise
+MANPAGES += misc-utils/fadvise.1
+dist_noinst_DATA += misc-utils/fadvise.1.adoc
+fadvise_SOURCES = misc-utils/fadvise.c
+fadvise_LDADD = $(LDADD) libcommon.la
+fadvise_CFLAGS = $(AM_CFLAGS)
+endif
diff --git a/misc-utils/fadvise.1.adoc b/misc-utils/fadvise.1.adoc
new file mode 100644 (file)
index 0000000..b4296e5
--- /dev/null
@@ -0,0 +1,57 @@
+//po4a: entry man manual
+= fadvise(1)
+:doctype: manpage
+:man manual: User Commands
+:man source: util-linux {release-version}
+:page-layout: base
+:command: fadvise
+
+== NAME
+
+fadvise - utility to use the posix_fadvise system call
+
+== SYNOPSIS
+
+*fadvise* [*-a* _advice_] [*-o* _offset_] [*-l* _length_] _filename_
+
+*fadvise* [*-a* _advice_] [*-o* _offset_] [*-l* _length_] -d _file-descriptor_
+
+== DESCRIPTION
+
+*fadvise* is a simple command wrapping posix_fadvise system call
+that is for predeclaring an access pattern for file data.
+
+== OPTIONS
+
+*-d*, *--fd* _file-descriptor_::
+Apply the advice to the file specified with the file descriptor instead
+of open a file specified with a file name.
+
+*-a*, *--advice* _advice_::
+See the command output with *--help* option for available values for
+advice. If this option is omitted, "dontneed" is used as default advice.
+
+*-o*, *--offset* _offset_::
+Specifies the beginning offset of the range, in bytes.
+If this option is omitted, 0 is used as default advice.
+
+*-l*, *--length* _length_::
+Specifies the length of the range, in bytes.
+If this option is omitted, 0 is used as default advice.
+
+== AUTHORS
+
+mailto:yamato@redhat.com[Masatake YAMATO]
+
+== SEE ALSO
+
+*posix_fadvise*(2)
+
+include::man-common/bugreports.adoc[]
+
+include::man-common/footer.adoc[]
+
+ifdef::translation[]
+include::man-common/translation.adoc[]
+endif::[]
+
diff --git a/misc-utils/fadvise.c b/misc-utils/fadvise.c
new file mode 100644 (file)
index 0000000..3d8ffc9
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * fadvise - utility to use the posix_fadvise(2)
+ *
+ * Copyright (C) 2022 Red Hat, Inc. All rights reserved.
+ * Written by Masatake YAMATO <yamato@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it 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 to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+
+static const struct advice {
+       const char *name;
+       int num;
+} advices [] = {
+       { "normal",     POSIX_FADV_NORMAL,     },
+       { "sequential", POSIX_FADV_SEQUENTIAL, },
+       { "random",     POSIX_FADV_RANDOM,     },
+       { "noreuse",    POSIX_FADV_NOREUSE,    },
+       { "willneeded", POSIX_FADV_WILLNEED,   },
+       { "dontneed",   POSIX_FADV_DONTNEED,   },
+};
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+       FILE *out = stdout;
+       size_t i;
+
+       fputs(USAGE_HEADER, out);
+       fprintf(out, _(" %s [options] file\n"), program_invocation_short_name);
+       fprintf(out, _(" %s [options] --fd|-d file-descriptor\n"), program_invocation_short_name);
+
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -a, --advice <advice> applying advice to the file (default: \"dontneed\")\n"), out);
+       fputs(_(" -l, --length <num>    length for range operations, in bytes\n"), out);
+       fputs(_(" -o, --offset <num>    offset for range operations, in bytes\n"), out);
+
+       fputs(USAGE_SEPARATOR, out);
+       printf(USAGE_HELP_OPTIONS(23));
+
+       fputs(_("\nAvailable values for advice:\n"), out);
+       for (i = 0; i < ARRAY_SIZE(advices); i++) {
+               fprintf(out, "  %s\n",
+                       advices[i].name);
+       }
+
+       printf(USAGE_MAN_TAIL("fadvise(1)"));
+
+       exit(EXIT_SUCCESS);
+}
+
+struct fadvise_args {
+       int fd;
+       off_t offset;
+       off_t len;
+       int advice;
+};
+
+
+int main(int argc, char ** argv)
+{
+       int c;
+       int rc;
+       bool do_close = false;
+
+       int fd = -1;
+       off_t offset = 0;
+       off_t len = 0;
+       int advice = POSIX_FADV_DONTNEED;
+
+       static const struct option longopts[] = {
+               { "advice",     required_argument, NULL, 'a' },
+               { "fd",         required_argument, NULL, 'd' },
+               { "length",     required_argument, NULL, 'l' },
+               { "offset",     required_argument, NULL, 'o' },
+               { "version",    no_argument,       NULL, 'V' },
+               { "help",       no_argument,       NULL, 'h' },
+               { NULL, 0, NULL, 0 },
+       };
+
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+
+       while ((c = getopt_long (argc, argv, "a:d:hl:o:", longopts, NULL)) != -1) {
+               switch (c) {
+               case 'a':
+                       break;
+               case 'd':
+                       fd = strtos32_or_err(optarg,
+                                            _("invalid fd argument"));
+                       break;
+               case 'l':
+                       len = strtosize_or_err(optarg,
+                                              _("invalid length argument"));
+                       break;
+               case 'o':
+                       offset = strtosize_or_err(optarg,
+                                                 _("invalid offset argument"));
+                       break;
+               case 'V':
+                       print_version(EXIT_SUCCESS);
+               case 'h':
+                       usage();
+               default:
+                       errtryhelp(EXIT_FAILURE);
+               }
+       }
+
+       if (optind == argc && fd == -1) {
+               warnx(_("no file specified"));
+               errtryhelp(EXIT_FAILURE);
+       }
+
+       if (argc - optind > 0 && fd != -1) {
+               warnx(_("specify either file descriptor or file name"));
+               errtryhelp(EXIT_FAILURE);
+       }
+
+       if (argc - optind > 1) {
+               warnx(_("specify one file descriptor or file name"));
+               errtryhelp(EXIT_FAILURE);
+       }
+
+       if (fd == -1) {
+               fd = open(argv[optind], O_RDONLY);
+               if (fd < 0)
+                       err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
+               do_close = true;
+       }
+
+       rc = posix_fadvise(fd,
+                          offset, len,
+                          advice);
+       if (rc != 0)
+               warn(_("failed to advise"));
+
+       if (do_close)
+               close(fd);
+
+       return rc;
+}
index b0dcb801d56395860e463c9dbcd40b14e446759f..d7dfe7372c8aa0522a1017084f463db224409643 100644 (file)
@@ -147,3 +147,7 @@ cal_sources = files(
 pipesz_sources = files(
   'pipesz.c',
 )
+
+fadvise_sources = files(
+  'fadvise.c',
+)
index aff324c1f365ed992728a412b29574d27980b0c8..58223fe95ffbf3778f9324c33621849736e2caa4 100644 (file)
@@ -61,6 +61,7 @@ TS_CMD_FALLOCATE=${TS_CMD_FALLOCATE-"${ts_commandsdir}fallocate"}
 TS_CMD_FDISK=${TS_CMD_FDISK-"${ts_commandsdir}fdisk"}
 TS_CMD_FLOCK=${TS_CMD_FLOCK-"${ts_commandsdir}flock"}
 TS_CMD_SFDISK=${TS_CMD_SFDISK-"${ts_commandsdir}sfdisk"}
+TS_CMD_FADVISE=${TS_CMD_FADVISE-"${ts_commandsdir}fadvise"}
 TS_CMD_FINCORE=${TS_CMD_FINCORE-"${ts_commandsdir}fincore"}
 TS_CMD_FINDMNT=${TS_CMD_FINDMNT-"${ts_commandsdir}findmnt"}
 TS_CMD_FSCKCRAMFS=${TS_CMD_FSCKCRAMFS:-"${ts_commandsdir}fsck.cramfs"}
diff --git a/tests/expected/fadvise/drop b/tests/expected/fadvise/drop
new file mode 100644 (file)
index 0000000..f2360b5
--- /dev/null
@@ -0,0 +1,23 @@
+  RES PAGES SIZE FILE
+  32K     8  32K ddtest
+
+whole file
+status: 0
+RES PAGES SIZE FILE
+ 0B     0  32K ddtest
+
+offset: 8192
+status: 0
+RES PAGES SIZE FILE
+ 8K     2  32K ddtest
+
+length: 16384
+status: 0
+  RES PAGES SIZE FILE
+  16K     4  32K ddtest
+
+offset: 8192, length: 16384 fd: 42
+status: 0
+  RES PAGES SIZE FILE
+  16K     4  32K ddtest
+
diff --git a/tests/ts/fadvise/drop b/tests/ts/fadvise/drop
new file mode 100644 (file)
index 0000000..e8c0071
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="drop page caches related to a file"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_FADVISE"
+ts_check_test_command "$TS_CMD_FINCORE"
+
+ts_check_prog "dd"
+ts_check_prog "sleep"
+
+ts_cd "$TS_OUTDIR"
+
+FILE="ddtest"
+BS=4k
+COUNT=8
+
+{
+    dd if=/dev/zero of="$FILE" bs=$BS count=$COUNT >& /dev/null
+    "$TS_CMD_FINCORE" "$FILE"
+    echo
+
+    dd if=/dev/zero of="$FILE" bs=$BS count=$COUNT >& /dev/null
+    echo "whole file"
+    "$TS_CMD_FADVISE" "$FILE"
+    echo status: $?
+    "$TS_CMD_FINCORE" "$FILE"
+    echo
+
+    dd if=/dev/zero of="$FILE" bs=$BS count=$COUNT >& /dev/null
+    echo "offset: 8192"
+    "$TS_CMD_FADVISE" -o 8192 "$FILE"
+    echo status: $?
+    "$TS_CMD_FINCORE" "$FILE"
+    echo
+
+    dd if=/dev/zero of="$FILE" bs=$BS count=$COUNT >& /dev/null
+    echo "length: 16384"
+    "$TS_CMD_FADVISE" -l 16384 "$FILE"
+    echo status: $?
+    "$TS_CMD_FINCORE" "$FILE"
+    echo
+
+    dd if=/dev/zero of="$FILE" bs=$BS count=$COUNT >& /dev/null
+    echo "offset: 8192, length: 16384 fd: 42"
+    "$TS_CMD_FADVISE" -o 8192 -l 16384 --fd 42 42<"$FILE"
+    echo status: $?
+    "$TS_CMD_FINCORE" "$FILE"
+    echo
+
+    rm "$FILE"
+} >> "$TS_OUTPUT" 2>> "$TS_ERRLOG"
+
+ts_finalize