Skip to content
Snippets Groups Projects
  • kalpak's avatar
    ca44fe28
    · ca44fe28
    kalpak authored
    b=16098
    
    Add URL for GPLv2 license in copyright headers
    ca44fe28
    History
    kalpak authored
    b=16098
    
    Add URL for GPLv2 license in copyright headers
mmap_sanity.c 18.27 KiB
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
 * vim:expandtab:shiftwidth=8:tabstop=8:
 *
 * GPL HEADER START
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will 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 version 2 for more details (a copy is included
 * in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU General Public License
 * version 2 along with this program; If not, see
 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 * GPL HEADER END
 */
/*
 * Copyright  2008 Sun Microsystems, Inc. All rights reserved
 * Use is subject to license terms.
 */
/*
 * This file is part of Lustre, http://www.lustre.org/
 * Lustre is a trademark of Sun Microsystems, Inc.
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <sys/wait.h>

char *dir = NULL, *dir2 = NULL;
long page_size;
char mmap_sanity[256];


static void usage(void)
{
        printf("Usage: mmap_sanity -d dir [-m dir2]\n");
        printf("       dir      lustre mount point\n");
        printf("       dir2     another mount point\n");
        exit(127);
}

static int remote_tst(int tc, char *mnt);
static int mmap_run(int tc)
{
        pid_t child;
        int rc = 0;

        child = fork();
        if (child < 0)
                return errno;
        else if (child)
                return 0;

        if (dir2 != NULL) {
                rc = remote_tst(tc, dir2);
        } else {
                rc = EINVAL;
                fprintf(stderr, "invalid argument!\n");
        }
        _exit(rc);
}

static int mmap_initialize(char *myself)
{
        char buf[1024], *file;
        int fdr, fdw, count, rc = 0;
        
        page_size = sysconf(_SC_PAGESIZE);
        if (page_size == -1) {
                perror("sysconf(_SC_PAGESIZE)");
                return errno;
        }

        /* copy myself to lustre for another client */
        fdr = open(myself, O_RDONLY);
        if (fdr < 0) {
                perror(myself);
                return EINVAL;
        }
        file = strrchr(myself, '/');
        if (file == NULL) {
                fprintf(stderr, "can't get test filename\n");
                close(fdr);
                return EINVAL;
        }
        file++;
        sprintf(mmap_sanity, "%s/%s", dir, file);

        fdw = open(mmap_sanity, O_CREAT|O_WRONLY, 0777);
        if (fdw < 0) {
                perror(mmap_sanity);
                close(fdr);
                return EINVAL;
        }
        while ((count = read(fdr, buf, sizeof(buf))) != 0) {
                int writes;

                if (count < 0) {
                        perror("read()");
                        rc = errno;
                        break;
                }
                writes = write(fdw, buf, count);
                if (writes != count) {
                        perror("write()");
                        rc = errno;
                        break;
                }
        }
        close(fdr);
        close(fdw);
        return rc;
}

static void mmap_finalize()
{
        unlink(mmap_sanity);
}

/* basic mmap operation on single node */
static int mmap_tst1(char *mnt)
{
        char *ptr, mmap_file[256];
        int region, fd, rc = 0;

        region = page_size * 10;
        sprintf(mmap_file, "%s/%s", mnt, "mmap_file1");
        
        if (unlink(mmap_file) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }

        fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }
        ftruncate(fd, region);

        ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }
        memset(ptr, 'a', region);

        munmap(ptr, region);
out_close:
        close(fd);
        unlink(mmap_file);
        return rc;
}

/* MAP_PRIVATE create a copy-on-write mmap */
static int mmap_tst2(char *mnt)
{
        char *ptr, mmap_file[256], buf[256];
        int fd, rc = 0;

        sprintf(mmap_file, "%s/%s", mnt, "mmap_file2");

        if (unlink(mmap_file) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }

        fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }
        ftruncate(fd, page_size);

        ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }
        memcpy(ptr, "blah", strlen("blah"));

        munmap(ptr, page_size);
out_close:
        close(fd);
        if (rc)
                return rc;

        fd = open(mmap_file, O_RDONLY);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }
        rc = read(fd, buf, sizeof(buf));
        if (rc < 0) {
                perror("read()");
                rc = errno;
                goto out_close;
        }
        rc = 0;
        
        if (strncmp("blah", buf, strlen("blah")) == 0) {
                fprintf(stderr, "mmap write back with MAP_PRIVATE!\n");
                rc = EFAULT;
        }
        close(fd);
        unlink(mmap_file);
        return rc;
}

/* concurrent mmap operations on two nodes */
static int mmap_tst3(char *mnt)
{
        char *ptr, mmap_file[256];
        int region, fd, rc = 0;

        region = page_size * 100;
        sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");
        
        if (unlink(mmap_file) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }

        fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }
        ftruncate(fd, region);

        ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }

        rc = mmap_run(3);
        if (rc)
                goto out_unmap;
        
        memset(ptr, 'a', region);
        sleep(2);       /* wait for remote test finish */
out_unmap:
        munmap(ptr, region);
out_close:
        close(fd);
        unlink(mmap_file);
        return rc;
}       

static int remote_tst3(char *mnt)
{
        char *ptr, mmap_file[256];
        int region, fd, rc = 0;
        region = page_size * 100;
        sprintf(mmap_file, "%s/%s", mnt, "mmap_file3");

        fd = open(mmap_file, O_RDWR, 0600);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }

        ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }
        memset(ptr, 'b', region);
        memset(ptr, 'c', region);
        
        munmap(ptr, region);
out_close:
        close(fd);
        return rc;
}

/* client1 write to file_4a from mmap()ed file_4b;
 * client2 write to file_4b from mmap()ed file_4a. */
static int mmap_tst4(char *mnt)
{
        char *ptr, filea[256], fileb[256];
        int region, fdr, fdw, rc = 0;

        region = page_size * 100;
        sprintf(filea, "%s/%s", mnt, "mmap_file_4a");
        sprintf(fileb, "%s/%s", mnt, "mmap_file_4b");

        if (unlink(filea) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }
        if (unlink(fileb) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }

        fdr = fdw = -1;
        fdr = open(fileb, O_CREAT|O_RDWR, 0600);
        if (fdr < 0) {
                perror(fileb);
                return errno;
        }
        ftruncate(fdr, region);
        fdw = open(filea, O_CREAT|O_RDWR, 0600);
        if (fdw < 0) {
                perror(filea);
                rc = errno;
                goto out_close;
        }
        ftruncate(fdw, region);
        
        ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }

        rc = mmap_run(4);
        if (rc)
                goto out_unmap;
        
        memset(ptr, '1', region);
        
        rc = write(fdw, ptr, region);
        if (rc <= 0) {
                perror("write()");
                rc = errno;
        } else
                rc = 0;

        sleep(2);       /* wait for remote test finish */
out_unmap:
        munmap(ptr, region);
out_close:
        if (fdr >= 0)
                close(fdr);
        if (fdw >= 0)
                close(fdw);
        unlink(filea);
        unlink(fileb);
        return rc;
}

static int remote_tst4(char *mnt)
{
        char *ptr, filea[256], fileb[256];
        int region, fdr, fdw, rc = 0;

        region = page_size * 100;
        sprintf(filea, "%s/%s", mnt, "mmap_file_4a");
        sprintf(fileb, "%s/%s", mnt, "mmap_file_4b");

        fdr = fdw = -1;
        fdr = open(filea, O_RDWR, 0600);
        if (fdr < 0) {
                perror(filea);
                return errno;
        }
        fdw = open(fileb, O_RDWR, 0600);
        if (fdw < 0) {
                perror(fileb);
                rc = errno;
                goto out_close;
        }

        ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fdr, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }

        memset(ptr, '2', region);

        rc = write(fdw, ptr, region);
        if (rc <= 0) {
                perror("write()");
                rc = errno;
        } else
                rc = 0;
     
        munmap(ptr, region);
out_close:
        if (fdr >= 0)
                close(fdr);
        if (fdw >= 0)
                close(fdw);
        return rc;
}

static int cancel_lru_locks(char *prefix)
{
        char cmd[256], line[1024];
        FILE *file;
        pid_t child;
        int len = 1024, rc = 0;

        child = fork();
        if (child < 0)
                return errno;
        else if (child) {
                int status;
                rc = waitpid(child, &status, WNOHANG);
                if (rc == child)
                        rc = 0;
                return rc;
        }

        if (prefix)
                sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size | grep -i %s", prefix);
        else
                sprintf(cmd, "ls /proc/fs/lustre/ldlm/namespaces/*/lru_size");

        file = popen(cmd, "r");
        if (file == NULL) {
                perror("popen()");
                return errno;
        }

        while (fgets(line, len, file)) {
                FILE *f;

                if (!strlen(line))
                        continue;
                /* trim newline character */
                *(line + strlen(line) - 1) = '\0';
                f = fopen(line, "w");
                if (f == NULL) {
                        perror("fopen()");
                        rc = errno;
                        break;
                }
                rc = fwrite("clear", strlen("clear") + 1, 1, f);
                if (rc < 1) {
                        perror("fwrite()");
                        rc = errno;
                        fclose(f);
                        break;
                }
                fclose(f);
        }

        pclose(file);
        _exit(rc);
}

/* don't dead lock while read/write file to/from the buffer which
 * mmaped to just this file */
static int mmap_tst5(char *mnt)
{
        char *ptr, mmap_file[256];
        int region, fd, off, rc = 0;

        region = page_size * 40;
        off = page_size * 10;
        sprintf(mmap_file, "%s/%s", mnt, "mmap_file5");

        if (unlink(mmap_file) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }

        fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }
        ftruncate(fd, region);

        ptr = mmap(NULL, region, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out_close;
        }
        memset(ptr, 'a', region);

        /* cancel unused locks */
        rc = cancel_lru_locks("osc");
        if (rc)
                goto out_unmap;

        /* read/write region of file and buffer should be overlap */
        rc = read(fd, ptr + off, off * 2);
        if (rc != off * 2) {
                perror("read()");
                rc = errno;
                goto out_unmap;
        }
        rc = write(fd, ptr + off, off * 2);
        if (rc != off * 2) {
                perror("write()");
                rc = errno;
        }
        rc = 0;
out_unmap:
        munmap(ptr, region);
out_close:
        close(fd);
        unlink(mmap_file);
        return rc;
}

/* mmap write to a file form client1 then mmap read from client2 */
static int mmap_tst6(char *mnt)
{
        char mmap_file[256], mmap_file2[256];
        char *ptr = NULL, *ptr2 = NULL;
        int fd = 0, fd2 = 0, rc = 0;

        sprintf(mmap_file, "%s/%s", mnt, "mmap_file6");
        sprintf(mmap_file2, "%s/%s", dir2, "mmap_file6");
        if (unlink(mmap_file) && errno != ENOENT) {
                perror("unlink()");
                return errno;
        }

        fd = open(mmap_file, O_CREAT|O_RDWR, 0600);
        if (fd < 0) {
                perror(mmap_file);
                return errno;
        }
        ftruncate(fd, page_size);

        fd2 = open(mmap_file2, O_RDWR, 0600);
        if (fd2 < 0) {
                perror(mmap_file2);
                goto out;
        }

        ptr = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
        if (ptr == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out;
        }
        
        ptr2 = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
        if (ptr2 == MAP_FAILED) {
                perror("mmap()");
                rc = errno;
                goto out;
        }

        rc = cancel_lru_locks("osc");
        if (rc)
                goto out;

        memcpy(ptr, "blah", strlen("blah"));
        if (strncmp(ptr, ptr2, strlen("blah"))) {
                fprintf(stderr, "client2 mmap mismatch!\n");
                rc = EFAULT;
                goto out;
        }
        memcpy(ptr2, "foo", strlen("foo"));
        if (strncmp(ptr, ptr2, strlen("foo"))) {
                fprintf(stderr, "client1 mmap mismatch!\n");
                rc = EFAULT;
        }
out:
        if (ptr2)
                munmap(ptr2, page_size);
        if (ptr)
                munmap(ptr, page_size);
        if (fd2 > 0)
                close(fd2);
        if (fd > 0)
                close(fd);
        unlink(mmap_file);
        return rc;
}

static int remote_tst(int tc, char *mnt)
{
        int rc = 0;
        switch(tc) {
        case 3:
                rc = remote_tst3(mnt);
                break;
        case 4:
                rc = remote_tst4(mnt);
                break;
        default:
                fprintf(stderr, "wrong test case number %d\n", tc);
                rc = EINVAL;
                break;
        }
        return rc;
}
        
struct test_case {
        int     tc;                     /* test case number */
        char    *desc;                  /* test description */
        int     (* test_fn)(char *mnt); /* test function */
        int     node_cnt;               /* node count */
};

struct test_case tests[] = {
        { 1, "mmap test1: basic mmap operation", mmap_tst1, 1 },
        { 2, "mmap test2: MAP_PRIVATE not write back", mmap_tst2, 1 },
        { 3, "mmap test3: concurrent mmap ops on two nodes", mmap_tst3, 2 },
        { 4, "mmap test4: c1 write to f1 from mmapped f2, " 
             "c2 write to f1 from mmapped f1", mmap_tst4, 2 },
        { 5, "mmap test5: read/write file to/from the buffer "
             "which mmapped to just this file", mmap_tst5, 1 },
        { 6, "mmap test6: check mmap write/read content on two nodes", 
                mmap_tst6, 2 },
        { 0, NULL, 0, 0 }
};

int main(int argc, char **argv)
{
        extern char *optarg;
        struct test_case *test;
        int c, rc = 0;

        for(;;) {
                c = getopt(argc, argv, "d:m:");
                if ( c == -1 )
                        break;

                switch(c) {
                        case 'd':
                                dir = optarg;
                                break;
                        case 'm':
                                dir2 = optarg;
                                break;
                        default:
                        case '?':
                                usage();
                                break;
                }
        }

        if (dir == NULL)
                usage();

        if (mmap_initialize(argv[0]) != 0) {
                fprintf(stderr, "mmap_initialize failed!\n");
                return EINVAL;
        }

        for (test = tests; test->tc; test++) {
                char *rs = "skip";
                rc = 0;
                if (test->node_cnt == 1 || dir2 != NULL) {
                        rc = test->test_fn(dir);
                        rs = rc ? "fail" : "pass";
                }
                fprintf(stderr, "%s (%s)\n", test->desc, rs);
                if (rc)
                        break;
        }

        mmap_finalize();
        return rc;
}