[ mmap corruptions with leveldb and btrfs compression ]

I ran this a number of times with compression off and wasn't able to
trigger problems.  With compress=lzo, I see errors on every run.

Compile: gcc -Wall -o mmap-trunc mmap-trunc.c
Run: ./mmap-trunc file_name

The basic idea is to create a 256MB file in steps.  Each step ftruncates
the file larger, and then mmaps a region for writing.  It dirties some
unaligned bytes (a little more than 8K), and then munmaps.

Then a verify stage goes back through the file to make sure the data we
wrote is really there.  I'm using a simple rotating pattern of chars
that compress very well.

I run it in batches of 100 with some memory pressure on the side:

for x in `seq 1 100` ; do (mmap-trunc f$x &) ; done

#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#define FILE_SIZE ((loff_t)256 * 1024 * 1024)
/* make a painfully unaligned chunk size */
#define CHUNK_SIZE (8192 + 932)

#define mmap_align(x) (((x) + 4095) & ~4095)

char *file_name = NULL;

void mmap_one_chunk(int fd, loff_t *cur_size, unsigned char *file_buf)
{
        int ret;
        loff_t new_size = *cur_size + CHUNK_SIZE;
        loff_t pos = *cur_size;
        unsigned long map_size = mmap_align(CHUNK_SIZE) + 4096;
        char val = file_buf[0];
        char *p;
        int extra;

        /* step one, truncate out a hole */
        ret = ftruncate(fd, new_size);
        if (ret) {
                perror("truncate");
                exit(1);
        }

        if (val == 0 || val == 'z')
                val = 'a';
        else
                val++;

        memset(file_buf, val, CHUNK_SIZE);

        extra = pos & 4095;
        p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
                 pos - extra);
        if (p == MAP_FAILED) {
                perror("mmap");
                exit(1);
        }
        memcpy(p + extra, file_buf, CHUNK_SIZE);

        ret = munmap(p, map_size);
        if (ret) {
                perror("munmap");
                exit(1);
        }
        *cur_size = new_size;
}

void check_chunks(int fd)
{
        char *p;
        loff_t checked = 0;
        char val = 'a';
        int i;
        int errors = 0;
        int ret;
        int extra;
        unsigned long map_size = mmap_align(CHUNK_SIZE) + 4096;

        fprintf(stderr, "checking chunks\n");
        while (checked < FILE_SIZE) {
                extra = checked & 4095;
                p = mmap(0, map_size, PROT_READ,
                         MAP_SHARED, fd, checked - extra);
                if (p == MAP_FAILED) {
                        perror("mmap");
                        exit(1);
                }
                for (i = 0; i < CHUNK_SIZE; i++) {
                        if (p[i + extra] != val) {
                                fprintf(stderr, "%s: bad val %x wanted %x 
offset 0x%llx\n",
                                        file_name, p[i + extra], val,
                                        (unsigned long long)checked + i);
                                errors++;
                        }
                }
                if (val == 'z')
                        val = 'a';
                else
                        val++;
                ret = munmap(p, map_size);
                if (ret) {
                        perror("munmap");
                        exit(1);
                }
                checked += CHUNK_SIZE;
        }
        printf("%s found %d errors\n", file_name, errors);
        if (errors)
                exit(1);
}

int main(int ac, char **av)
{
        unsigned char *file_buf;
        loff_t pos = 0;
        int ret;
        int fd;

        if (ac < 2) {
                fprintf(stderr, "usage: mmap-trunc filename\n");
                exit(1);
        }

        ret = posix_memalign((void **)&file_buf, 4096, CHUNK_SIZE);
        if (ret) {
                perror("cannot allocate memory\n");
                exit(1);
        }

        file_buf[0] = 0;

        file_name = av[1];

        fprintf(stderr, "running test on %s\n", file_name);

        unlink(file_name);
        fd = open(file_name, O_RDWR | O_CREAT, 0600);
        if (fd < 0) {
                perror("open");
                exit(1);
        }

        fprintf(stderr, "writing chunks\n");
        while (pos < FILE_SIZE) {
                mmap_one_chunk(fd, &pos, file_buf);
        }
        check_chunks(fd);
        return 0;
}
--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to