#define _XOPEN_SOURCE 500 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define N_BIG_SNAPS 5 #define VG "vg1" #define LV "test_lv" #define LV_SNAP_SMALL "test_snap_small" #define LV_SNAP_BIG "test_snap_big" #define MEGABYTES "10" #define SNAP_MEGABYTES "12" #define THREADS 10 #define BS 4096 #define ORIG_PATTERN 'p' #define NEW_PATTERN 'n' #define BIG_CHUNKSIZE_N 32768 #define BIG_CHUNKSIZE "32k" #define SMALL_CHUNKSIZE "16k" enum { IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE, }; enum { IOPRIO_WHO_PROCESS = 1, IOPRIO_WHO_PGRP, IOPRIO_WHO_USER, }; #define IOPRIO_CLASS_SHIFT 13 static inline int ioprio_set(int which, int who, int ioprio) { return syscall(__NR_ioprio_set, which, who, ioprio); } static inline int ioprio_get(int which, int who) { return syscall(__NR_ioprio_get, which, who); } #define PRIO_READER ((IOPRIO_CLASS_IDLE << IOPRIO_CLASS_SHIFT) | 0xff) #define PRIO_WRITER (IOPRIO_CLASS_RT << IOPRIO_CLASS_SHIFT) static int do_cmd(char *cmd, int ign_err) { int r; fprintf(stderr, "* %s\n", cmd); r = system(cmd); if (r) { if (r == -1) { perror("system"); } else { if (ign_err) goto ret; fprintf(stderr, "return code %x\n", r); } exit(1); } ret: return r; } static char pattern[BS] __attribute__((aligned(BS))); static char new_pattern[BS] __attribute__((aligned(BS))); static long n; static int h_orig; static void pthread_error(int r) { fprintf(stderr, "pthread_error: %s\n", strerror(r)); exit(1); } pthread_barrier_t bar; static void test_io(unsigned long start, unsigned stride) { int r; while (1) { fprintf(stderr, "testing %llx...\r", (long long)start); r = pthread_barrier_wait(&bar); if (r && r != PTHREAD_BARRIER_SERIAL_THREAD) pthread_error(r); r = pwrite(h_orig, new_pattern, BS, start); if (r != BS) break; start += stride; } if (r < 0 && errno != ENOSPC) perror("pwrite"), exit(1); } static void *writer(void *p) { test_io(BIG_CHUNKSIZE_N / 2, BIG_CHUNKSIZE_N); return NULL; } int main(void) { int i, r; pthread_t th; char str[256]; memset(pattern, ORIG_PATTERN, sizeof pattern); do_cmd("lvremove -f "VG"/"LV_SNAP_SMALL"", 1); for (i = 1; i <= N_BIG_SNAPS; i++) { sprintf(str, "lvremove -f "VG"/"LV_SNAP_BIG"_%d", i); do_cmd(str, 1); } do_cmd("lvremove -f "VG"/"LV"", 1); do_cmd("lvcreate -L "MEGABYTES" -n "LV" "VG"", 0); h_orig = open("/dev/mapper/"VG"-"LV"", O_RDWR | O_DIRECT); if (h_orig < 0) perror("open orig"), exit(1); n = 0; while ((r = write(h_orig, pattern, BS)) == BS) { n++; fprintf(stderr, "creating %llx...\r", (long long)n * BS); } if (r == -1 && errno != ENOSPC) perror("write"), exit(1); if (fsync(h_orig)) perror("fsync"), exit(1); fprintf(stderr,"\n"); close(h_orig); do_cmd("lvcreate -L "SNAP_MEGABYTES" -c "SMALL_CHUNKSIZE" -n "LV_SNAP_SMALL" -s "VG"/"LV"", 0); for (i = 1; i <= N_BIG_SNAPS; i++) { sprintf(str, "lvcreate -L "SNAP_MEGABYTES" -c "BIG_CHUNKSIZE" -n "LV_SNAP_BIG"_%d -s "VG"/"LV"", i); do_cmd(str, 0); } h_orig = open("/dev/mapper/"VG"-"LV"", O_RDWR | O_DIRECT); if (h_orig < 0) perror("open orig"), exit(1); memset(new_pattern, NEW_PATTERN, sizeof new_pattern); if ((r = pthread_barrier_init(&bar, NULL, 2))) pthread_error(r); if ((r = pthread_create(&th, NULL, writer, NULL))) pthread_error(r); test_io(0, BIG_CHUNKSIZE_N); if (!do_cmd("grep -l n /dev/mapper/"VG"-"LV_SNAP_SMALL" /dev/mapper/"VG"-"LV_SNAP_BIG"*[0-9]", 1)) { fprintf(stderr, "ERROR. SNAPSHOT PERMANENTLY CORRUPTED\n"); return 2; } fprintf(stderr, "Ok.\n"); return 0; }