fsstress: add mwrite/mread into test operation list
authorZorro Lang <zlang@redhat.com>
Tue, 28 Mar 2017 08:46:34 +0000 (16:46 +0800)
committerEryu Guan <eguan@redhat.com>
Wed, 29 Mar 2017 07:07:25 +0000 (15:07 +0800)
mmap is a popular and basic operation, most of softwares use it to
access files. More and more customers report bugs related with
mmap/munmap and other stress conditions.

So add mmap read/write test into fsstress to increase mmap related
stress to reproduce or find more bugs easily.

Signed-off-by: Zorro Lang <zlang@redhat.com>
Reviewed-by: Eryu Guan <eguan@redhat.com>
Signed-off-by: Eryu Guan <eguan@redhat.com>
configure.ac
ltp/fsstress.c
src/global.h

index fa48d2f806e8ec2c97af74f6f50ab73fb467dc11..246f92eb24f308ab497945e77d58fb30f5209cb5 100644 (file)
@@ -32,6 +32,7 @@ AC_HEADER_STDC
                        xfs/platform_defs.h     \
                        btrfs/ioctl.h           \
                        cifs/ioctl.h            \
+                       sys/mman.h              \
     ])
 
 AC_CHECK_HEADERS([xfs/xfs_log_format.h],,,[
index 7e7cf608f5f389b8167ffdb4b12c29765f505940..6d8f1172e4d416e1aebba894c7665a8821111f4f 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <linux/fs.h>
+#include <setjmp.h>
 #include "global.h"
 
 #ifdef HAVE_ATTR_XATTR_H
@@ -69,6 +70,8 @@ typedef enum {
        OP_LINK,
        OP_MKDIR,
        OP_MKNOD,
+       OP_MREAD,
+       OP_MWRITE,
        OP_PUNCH,
        OP_ZERO,
        OP_COLLAPSE,
@@ -168,6 +171,8 @@ void        getdents_f(int, long);
 void   link_f(int, long);
 void   mkdir_f(int, long);
 void   mknod_f(int, long);
+void   mread_f(int, long);
+void   mwrite_f(int, long);
 void   punch_f(int, long);
 void   zero_f(int, long);
 void   collapse_f(int, long);
@@ -208,6 +213,8 @@ opdesc_t    ops[] = {
        { OP_LINK, "link", link_f, 1, 1 },
        { OP_MKDIR, "mkdir", mkdir_f, 2, 1 },
        { OP_MKNOD, "mknod", mknod_f, 2, 1 },
+       { OP_MREAD, "mread", mread_f, 2, 0 },
+       { OP_MWRITE, "mwrite", mwrite_f, 2, 1 },
        { OP_PUNCH, "punch", punch_f, 1, 1 },
        { OP_ZERO, "zero", zero_f, 1, 1 },
        { OP_COLLAPSE, "collapse", collapse_f, 1, 1 },
@@ -262,6 +269,7 @@ int         cleanup = 0;
 int            verbose = 0;
 int            verifiable_log = 0;
 sig_atomic_t   should_stop = 0;
+sigjmp_buf     *sigbus_jmp = NULL;
 char           *execute_cmd = NULL;
 int            execute_freq = 1;
 struct print_string    flag_str = {0};
@@ -311,7 +319,26 @@ void       zero_freq(void);
 
 void sg_handler(int signum)
 {
-       should_stop = 1;
+       switch (signum) {
+       case SIGTERM:
+               should_stop = 1;
+               break;
+       case SIGBUS:
+               /*
+                * Only handle SIGBUS when mmap write to a hole and no
+                * block can be allocated due to ENOSPC, abort otherwise.
+                */
+               if (sigbus_jmp) {
+                       siglongjmp(*sigbus_jmp, -1);
+               } else {
+                       printf("Unknown SIGBUS is caught, Abort!\n");
+                       abort();
+               }
+               /* should not reach here */
+               break;
+       default:
+               break;
+       }
 }
 
 int main(int argc, char **argv)
@@ -527,10 +554,13 @@ int main(int argc, char **argv)
 
        for (i = 0; i < nproc; i++) {
                if (fork() == 0) {
-                       action.sa_handler = SIG_DFL;
                        sigemptyset(&action.sa_mask);
+                       action.sa_handler = SIG_DFL;
                        if (sigaction(SIGTERM, &action, 0))
                                return 1;
+                       action.sa_handler = sg_handler;
+                       if (sigaction(SIGBUS, &action, 0))
+                               return 1;
 #ifdef HAVE_SYS_PRCTL_H
                        prctl(PR_SET_PDEATHSIG, SIGKILL);
                        if (getppid() == 1) /* parent died already? */
@@ -2655,6 +2685,131 @@ mknod_f(int opno, long r)
        free_pathname(&f);
 }
 
+#ifdef HAVE_SYS_MMAN_H
+struct print_flags mmap_flags[] = {
+       { MAP_SHARED, "SHARED"},
+       { MAP_PRIVATE, "PRIVATE"},
+       { -1, NULL}
+};
+
+#define translate_mmap_flags(flags)      \
+       ({translate_flags(flags, "|", mmap_flags);})
+#endif
+
+void
+do_mmap(int opno, long r, int prot)
+{
+#ifdef HAVE_SYS_MMAN_H
+       char            *addr;
+       int             e;
+       pathname_t      f;
+       int             fd;
+       size_t          len;
+       __int64_t       lr;
+       off64_t         off;
+       int             flags;
+       struct stat64   stb;
+       int             v;
+       char            st[1024];
+       sigjmp_buf      sigbus_jmpbuf;
+
+       init_pathname(&f);
+       if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+               if (v)
+                       printf("%d/%d: do_mmap - no filename\n", procid, opno);
+               free_pathname(&f);
+               return;
+       }
+       fd = open_path(&f, O_RDWR);
+       e = fd < 0 ? errno : 0;
+       check_cwd();
+       if (fd < 0) {
+               if (v)
+                       printf("%d/%d: do_mmap - open %s failed %d\n",
+                              procid, opno, f.path, e);
+               free_pathname(&f);
+               return;
+       }
+       if (fstat64(fd, &stb) < 0) {
+               if (v)
+                       printf("%d/%d: do_mmap - fstat64 %s failed %d\n",
+                              procid, opno, f.path, errno);
+               free_pathname(&f);
+               close(fd);
+               return;
+       }
+       inode_info(st, sizeof(st), &stb, v);
+       if (stb.st_size == 0) {
+               if (v)
+                       printf("%d/%d: do_mmap - %s%s zero size\n", procid, opno,
+                              f.path, st);
+               free_pathname(&f);
+               close(fd);
+               return;
+       }
+
+       lr = ((__int64_t)random() << 32) + random();
+       off = (off64_t)(lr % stb.st_size);
+       off &= (off64_t)(~(sysconf(_SC_PAGE_SIZE) - 1));
+       len = (size_t)(random() % MIN(stb.st_size - off, FILELEN_MAX)) + 1;
+
+       flags = (random() % 2) ? MAP_SHARED : MAP_PRIVATE;
+       addr = mmap(NULL, len, prot, flags, fd, off);
+       e = (addr == MAP_FAILED) ? errno : 0;
+       if (e) {
+               if (v)
+                       printf("%d/%d: do_mmap - mmap failed %s%s [%lld,%d,%s] %d\n",
+                              procid, opno, f.path, st, (long long)off,
+                              (int)len, translate_mmap_flags(flags), e);
+               free_pathname(&f);
+               close(fd);
+               return;
+       }
+
+       if (prot & PROT_WRITE) {
+               if ((e = sigsetjmp(sigbus_jmpbuf, 1)) == 0) {
+                       sigbus_jmp = &sigbus_jmpbuf;
+                       memset(addr, nameseq & 0xff, len);
+               }
+       } else {
+               char *buf;
+               if ((buf = malloc(len)) != NULL) {
+                       memcpy(buf, addr, len);
+                       free(buf);
+               }
+       }
+       munmap(addr, len);
+       /* set NULL to stop other functions from doing siglongjmp */
+       sigbus_jmp = NULL;
+
+       if (v)
+               printf("%d/%d: %s %s%s [%lld,%d,%s] %s\n",
+                      procid, opno, (prot & PROT_WRITE) ? "mwrite" : "mread",
+                      f.path, st, (long long)off, (int)len,
+                      translate_mmap_flags(flags),
+                      (e == 0) ? "0" : "Bus error");
+
+       free_pathname(&f);
+       close(fd);
+#endif
+}
+
+void
+mread_f(int opno, long r)
+{
+#ifdef HAVE_SYS_MMAN_H
+       do_mmap(opno, r, PROT_READ);
+#endif
+}
+
+void
+mwrite_f(int opno, long r)
+{
+#ifdef HAVE_SYS_MMAN_H
+       do_mmap(opno, r, PROT_WRITE);
+#endif
+}
+
 void
 punch_f(int opno, long r)
 {
index f63246bd5e6b8ded1953152f708c65691cef1ea4..3920c0d5e6d5c8d457c3b7122f9452d0e9e25ff1 100644 (file)
 
 #endif /* HAVE_LINUX_FALLOC_H */
 
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
 #endif /* GLOBAL_H */