]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_io: allow mmap command to reserve some free space
authorZorro Lang <zlang@redhat.com>
Tue, 10 May 2016 07:16:05 +0000 (17:16 +1000)
committerDave Chinner <david@fromorbit.com>
Tue, 10 May 2016 07:16:05 +0000 (17:16 +1000)
When using xfs_io to rewrite LTP's mmap16 testcase for xfstests,
we always hit mremap ENOMEM error. But according to linux commit:

  90a8020 vfs: fix data corruption when blocksize < pagesize for mmaped data

The reproducer shouldn't use MREMAP_MAYMOVE flag for mremap(). So we
need a stable method to make mremap can extend space from the original
mapped starting address.

Generally if we try to mremap from the original mapped starting point
in a C program, at first we always do:

  addr = mmap(NULL, res_size, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  munmap(addr, res_size);

Then do:

  addr = mmap(addr, real_len, ...);

The "res_size" is bigger than "real_len". This will help us get a
region between real_len and res_size, which maybe free space. The
truth is we can't guarantee that this free memory will stay free.
But this method is still very helpful for reserve some free space
in short time.

This patch bring in the "res_size" by use a new -s option. If don't use
this option, xfs_io -c "mmap 0 1024" -c "mremap 8192" will hit ENOMEM
error nearly 100%, but if use this option, it nearly always remap
successfully.

Signed-off-by: Zorro Lang <zlang@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
io/mmap.c
man/man8/xfs_io.8

index 568102ead4ba90dbfd18be704ca51d18f02dffa8..dc188d0557cf1b78acdc9bc85a2badc2e788e368 100644 (file)
--- a/io/mmap.c
+++ b/io/mmap.c
@@ -146,6 +146,7 @@ mmap_help(void)
 " -r -- map with PROT_READ protection\n"
 " -w -- map with PROT_WRITE protection\n"
 " -x -- map with PROT_EXEC protection\n"
+" -s <size> -- first do mmap(size)/munmap(size), try to reserve some free space\n"
 " If no protection mode is specified, all are used by default.\n"
 "\n"));
 }
@@ -156,8 +157,8 @@ mmap_f(
        char            **argv)
 {
        off64_t         offset;
-       ssize_t         length;
-       void            *address;
+       ssize_t         length = 0, length2 = 0;
+       void            *address = NULL;
        char            *filename;
        size_t          blocksize, sectsize;
        int             c, prot = 0;
@@ -181,7 +182,9 @@ mmap_f(
                return 0;
        }
 
-       while ((c = getopt(argc, argv, "rwx")) != EOF) {
+       init_cvtnum(&blocksize, &sectsize);
+
+       while ((c = getopt(argc, argv, "rwxs:")) != EOF) {
                switch (c) {
                case 'r':
                        prot |= PROT_READ;
@@ -192,6 +195,9 @@ mmap_f(
                case 'x':
                        prot |= PROT_EXEC;
                        break;
+               case 's':
+                       length2 = cvtnum(blocksize, sectsize, optarg);
+                       break;
                default:
                        return command_usage(&mmap_cmd);
                }
@@ -202,7 +208,6 @@ mmap_f(
        if (optind != argc - 2)
                return command_usage(&mmap_cmd);
 
-       init_cvtnum(&blocksize, &sectsize);
        offset = cvtnum(blocksize, sectsize, argv[optind]);
        if (offset < 0) {
                printf(_("non-numeric offset argument -- %s\n"), argv[optind]);
@@ -221,7 +226,19 @@ mmap_f(
                return 0;
        }
 
-       address = mmap(NULL, length, prot, MAP_SHARED, file->fd, offset);
+       /*
+        * mmap and munmap memory area of length2 region is helpful to
+        * make a region of extendible free memory. It's generally used
+        * for later mremap operation(no MREMAP_MAYMOVE flag). But there
+        * isn't guarantee that the memory after length (up to length2)
+        * will stay free.
+        */
+       if (length2 > length) {
+               address = mmap(NULL, length2, prot,
+                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+               munmap(address, length2);
+       }
+       address = mmap(address, length, prot, MAP_SHARED, file->fd, offset);
        if (address == MAP_FAILED) {
                perror("mmap");
                free(filename);
@@ -658,7 +675,7 @@ mmap_init(void)
        mmap_cmd.argmin = 0;
        mmap_cmd.argmax = -1;
        mmap_cmd.flags = CMD_NOMAP_OK | CMD_NOFILE_OK | CMD_FOREIGN_OK;
-       mmap_cmd.args = _("[N] | [-rwx] [off len]");
+       mmap_cmd.args = _("[N] | [-rwx] [-s size] [off len]");
        mmap_cmd.oneline =
                _("mmap a range in the current file, show mappings");
        mmap_cmd.help = mmap_help;
index 984b55121abf6d97c027e59b80c924ec9482ab57..b0300bb5c1e17532faaf94b2687219bd9bc49c57 100644 (file)
@@ -559,7 +559,7 @@ Do not print timing statistics at all.
 
 .SH MEMORY MAPPED I/O COMMANDS
 .TP
-.BI "mmap [ " N " | [[ \-rwx ] " "offset length " ]]
+.BI "mmap [ " N " | [[ \-rwx ] [\-s " size " ] " "offset length " ]]
 With no arguments,
 .B mmap
 shows the current mappings. Specifying a single numeric argument
@@ -575,6 +575,21 @@ PROT_WRITE
 .RB ( \-w ),
 and PROT_EXEC
 .RB ( \-x ).
+.BI \-s " size"
+is used to do a mmap(size) && munmap(size) operation at first, try to reserve some
+extendible free memory space, if
+.I size
+is bigger than
+.I length
+parameter. But there's not guarantee that the memory after
+.I length
+( up to
+.I size
+) will stay free.
+.B e.g.
+"mmap -rw -s 8192 1024" will mmap 0 ~ 1024 bytes memory, but try to reserve 1024 ~ 8192
+free space(no guarantee). This free space will helpful for "mremap 8192" without
+MREMAP_MAYMOVE flag.
 .TP
 .B mm
 See the