2 * fsync-err.c: test whether writeback errors are reported to all open fds
3 * and properly cleared as expected after being seen once on each
5 * Copyright (c) 2017: Jeff Layton <jlayton@redhat.com>
19 * btrfs has a fixed stripewidth of 64k, so we need to write enough data to
20 * ensure that we hit both stripes by default.
22 #define DEFAULT_BUFSIZE (65 * 1024)
24 /* default number of fds to open */
25 #define DEFAULT_NUM_FDS 10
29 printf("Usage: fsync-err [ -b bufsize ] [ -n num_fds ] [ -s ] -d dmerror path <filename>\n");
32 int main(int argc, char **argv)
34 int *fd, ret, i, numfds = DEFAULT_NUM_FDS;
36 char *dmerror_path = NULL;
38 size_t cmdsize, bufsize = DEFAULT_BUFSIZE;
39 bool simple_mode = false;
41 while ((i = getopt(argc, argv, "b:d:n:s")) != -1) {
44 bufsize = strtol(optarg, &buf, 0);
46 printf("bad string conversion: %s\n", optarg);
51 dmerror_path = optarg;
54 numfds = strtol(optarg, &buf, 0);
56 printf("bad string conversion: %s\n", optarg);
62 * Many filesystems will continue to throw errors after
63 * fsync has already advanced to the current error,
64 * due to metadata writeback failures or other
65 * issues. Allow those fs' to opt out of more thorough
78 printf("Must specify dmerror path with -d option!\n");
82 /* Remaining argument is filename */
85 fd = calloc(numfds, sizeof(*fd));
87 printf("malloc failed: %m\n");
91 for (i = 0; i < numfds; ++i) {
92 fd[i] = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
94 printf("open of fd[%d] failed: %m\n", i);
99 buf = malloc(bufsize);
101 printf("malloc failed: %m\n");
105 /* fill it with some junk */
106 memset(buf, 0x7c, bufsize);
108 for (i = 0; i < numfds; ++i) {
109 ret = pwrite(fd[i], buf, bufsize, i * bufsize);
111 printf("First write on fd[%d] failed: %m\n", i);
116 for (i = 0; i < numfds; ++i) {
119 printf("First fsync on fd[%d] failed: %m\n", i);
124 /* enough for path + dmerror command string (and then some) */
125 cmdsize = strlen(dmerror_path) + 64;
127 cmdbuf = malloc(cmdsize);
129 printf("malloc failed: %m\n");
133 ret = snprintf(cmdbuf, cmdsize, "%s load_error_table", dmerror_path);
134 if (ret < 0 || ret >= cmdsize) {
135 printf("sprintf failure: %d\n", ret);
139 /* flip the device to non-working mode */
140 ret = system(cmdbuf);
143 printf("system: program exited: %d\n",
146 printf("system: 0x%x\n", (int)ret);
151 for (i = 0; i < numfds; ++i) {
152 ret = pwrite(fd[i], buf, bufsize, i * bufsize);
154 printf("Second write on fd[%d] failed: %m\n", i);
159 for (i = 0; i < numfds; ++i) {
161 /* Now, we EXPECT the error! */
163 printf("Success on second fsync on fd[%d]!\n", i);
169 for (i = 0; i < numfds; ++i) {
173 * We did a failed write and fsync on each fd
174 * before. Now the error should be clear since
175 * we've not done any writes since then.
177 printf("Third fsync on fd[%d] failed: %m\n", i);
183 /* flip the device to working mode */
184 ret = snprintf(cmdbuf, cmdsize, "%s load_working_table", dmerror_path);
185 if (ret < 0 || ret >= cmdsize) {
186 printf("sprintf failure: %d\n", ret);
190 ret = system(cmdbuf);
193 printf("system: program exited: %d\n",
196 printf("system: 0x%x\n", (int)ret);
202 for (i = 0; i < numfds; ++i) {
205 /* The error should still be clear */
206 printf("fsync after healing device on fd[%d] failed: %m\n", i);
213 * reopen each file one at a time to ensure the same inode stays
214 * in core. fsync each one to make sure we see no errors on a fresh
217 for (i = 0; i < numfds; ++i) {
220 printf("Close of fd[%d] returned unexpected error: %m\n", i);
223 fd[i] = open(fname, O_WRONLY, 0644);
225 printf("Second open of fd[%d] failed: %m\n", i);
230 /* New opens should not return an error */
231 printf("First fsync after reopen of fd[%d] failed: %m\n", i);
236 printf("Test passed!\n");