]>
Commit | Line | Data |
---|---|---|
558d2836 A |
1 | /* |
2 | * For some background: this unit test was created for a bug where | |
3 | * during page-out, we captured the file size without any locks and | |
4 | * then if a change happened to come in before we took the locks, we | |
5 | * would lose the remainder of the page. The following illustrates | |
6 | * the scenario: | |
7 | * | |
8 | * <- 1 PG -> | |
9 | * a b c | |
10 | * +-------------+--------+---- | |
11 | * | |XXXX | | |
12 | * +-------------+--------+---- | |
13 | * ^ | |
14 | * | | |
15 | * EOF | |
16 | * | |
17 | * A page-out is requested for the page starting at @a and ending at | |
18 | * @c. The end of the file is at @b and is captured before any locks | |
19 | * are taken. After that, another thread writes something between @b | |
20 | * and @c. The page-out then continues, but it thinks the end of the | |
21 | * file is at @b, so it will only write up to @b and then it will mark | |
22 | * the entire page as clean. That will cause us to lose the data that | |
23 | * was written between @b and @c. This would only occur if there is | |
24 | * an extent boundary at @b (which is the reason for the F_PREALLOCATE | |
25 | * calls below). The syncer thread below is simulating what we do in | |
26 | * page-out: we write 1 MB chunks. | |
27 | */ | |
28 | ||
29 | #include <fcntl.h> | |
30 | #include <assert.h> | |
31 | #include <unistd.h> | |
32 | #include <string.h> | |
33 | #include <stdlib.h> | |
34 | #include <pthread.h> | |
35 | #include <sys/mman.h> | |
36 | #include <sys/errno.h> | |
37 | #include <sys/stat.h> | |
38 | #include <stdio.h> | |
39 | #include <sched.h> | |
40 | ||
41 | #include "hfs-tests.h" | |
42 | #include "test-utils.h" | |
43 | #include "disk-image.h" | |
44 | ||
45 | TEST(msync_16k) | |
46 | ||
47 | static disk_image_t *di; | |
48 | ||
49 | volatile off_t size = 0; | |
50 | ||
51 | static int fd; | |
52 | ||
53 | void syncer(void) | |
54 | { | |
55 | for (;;) { | |
56 | off_t sz = size; | |
57 | ||
58 | if (sz == -1) | |
59 | break; | |
60 | ||
61 | sz &= ~0xfffff; | |
62 | ||
63 | void *p = mmap(NULL, 0x100000, PROT_READ | PROT_WRITE, MAP_SHARED, | |
64 | fd, sz); | |
65 | ||
66 | assert_with_errno(p != MAP_FAILED); | |
67 | ||
68 | while ((size & ~0xfffff) == sz) { | |
69 | assert_no_err(msync(p, 0x100000, MS_SYNC)); | |
70 | sched_yield(); | |
71 | } | |
72 | ||
73 | assert_no_err(munmap(p, 0x100000)); | |
74 | } | |
75 | } | |
76 | ||
77 | int run_msync_16k(__unused test_ctx_t *ctx) | |
78 | { | |
79 | di = disk_image_get(); | |
80 | ||
81 | char *file; | |
82 | asprintf(&file, "%s/msync-16k.data", di->mount_point); | |
83 | ||
84 | char *buf = malloc(1024 * 1024); | |
85 | memset(buf, 0xaf, 1024 * 1024); | |
86 | ||
87 | unlink(file); | |
88 | ||
89 | fd = open(file, O_CREAT | O_RDWR, 0666); | |
90 | assert_with_errno(fd >= 0); | |
91 | ||
92 | pthread_t thr; | |
93 | pthread_create(&thr, NULL, (void *(*)(void *))syncer, NULL); | |
94 | ||
95 | assert_no_err(fcntl(fd, F_NOCACHE, 1)); | |
96 | ||
97 | check_io(write(fd, buf, 8192), 8192); | |
98 | ||
99 | size = 8192; | |
100 | while (size < 100 * 1024 * 1024ll) { | |
101 | // Force an extent boundary | |
102 | struct log2phys l2p = { | |
103 | .l2p_contigbytes = 4096, | |
104 | .l2p_devoffset = size - 4096, | |
105 | }; | |
106 | assert(!fcntl(fd, F_LOG2PHYS_EXT, &l2p)); | |
107 | ||
108 | struct fstore fst = { | |
109 | .fst_posmode = F_VOLPOSMODE, | |
110 | .fst_offset = l2p.l2p_devoffset + 4096, | |
111 | .fst_length = size + 16384, | |
112 | }; | |
113 | assert(!fcntl(fd, F_PREALLOCATE, &fst)); | |
114 | ||
115 | check_io(pwrite(fd, buf, 16384, size), 16384); | |
116 | size += 16384; | |
117 | } | |
118 | ||
119 | size_t sz = size; | |
120 | size = -1; | |
121 | ||
122 | pthread_join(thr, NULL); | |
123 | assert_no_err(close(fd)); | |
124 | ||
125 | fd = open(file, O_RDWR); | |
126 | ||
127 | assert_with_errno(fd >= 0); | |
128 | size_t done = 0; | |
129 | char *cmp_buf = malloc(1024 * 1024); | |
130 | ||
131 | while (done < sz) { | |
132 | void *p = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, done); | |
133 | assert_with_errno(p != MAP_FAILED); | |
134 | assert_no_err(msync(p, 1024 * 1024, MS_INVALIDATE | MS_SYNC)); | |
135 | assert_no_err(munmap(p, 1024 * 1024)); | |
136 | ||
137 | bzero(cmp_buf, 1024 * 1024); | |
138 | ssize_t amt = read(fd, cmp_buf, 1024 * 1024); | |
139 | assert(amt > 0); | |
140 | assert(!memcmp(cmp_buf, buf, amt)); | |
141 | done += amt; | |
142 | } | |
143 | ||
144 | assert_no_err(close(fd)); | |
145 | ||
146 | assert_no_err(unlink(file)); | |
147 | ||
ec99dd30 A |
148 | free(cmp_buf); |
149 | free(buf); | |
558d2836 A |
150 | free(file); |
151 | ||
152 | return 0; | |
153 | } |