log-writes: add replay-log program to replay dm-log-writes target
[xfstests-dev.git] / src / log-writes / log-writes.c
1 #include <linux/fs.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <sys/ioctl.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include "log-writes.h"
12
13 int log_writes_verbose = 0;
14
15 /*
16  * @log: the log to free.
17  *
18  * This will close any open fd's the log has and free up its memory.
19  */
20 void log_free(struct log *log)
21 {
22         if (log->replayfd >= 0)
23                 close(log->replayfd);
24         if (log->logfd >= 0)
25                 close(log->logfd);
26         free(log);
27 }
28
29 static int discard_range(struct log *log, u64 start, u64 len)
30 {
31         u64 range[2] = { start, len };
32
33         if (ioctl(log->replayfd, BLKDISCARD, &range) < 0) {
34                 if (log_writes_verbose)
35                         printf("replay device doesn't support discard, "
36                                "switching to writing zeros\n");
37                 log->flags |= LOG_DISCARD_NOT_SUPP;
38         }
39         return 0;
40 }
41
42 static int zero_range(struct log *log, u64 start, u64 len)
43 {
44         u64 bufsize = len;
45         ssize_t ret;
46         char *buf = NULL;
47
48         if (log->max_zero_size < len) {
49                 if (log_writes_verbose)
50                         printf("discard len %llu larger than max %llu\n",
51                                (unsigned long long)len,
52                                (unsigned long long)log->max_zero_size);
53                 return 0;
54         }
55
56         while (!buf) {
57                 buf = malloc(bufsize);
58                 if (!buf)
59                         bufsize >>= 1;
60                 if (!bufsize) {
61                         fprintf(stderr, "Couldn't allocate zero buffer");
62                         return -1;
63                 }
64         }
65
66         memset(buf, 0, bufsize);
67         while (len) {
68                 ret = pwrite(log->replayfd, buf, bufsize, start);
69                 if (ret != bufsize) {
70                         fprintf(stderr, "Error zeroing file: %d\n", errno);
71                         free(buf);
72                         return -1;
73                 }
74                 len -= ret;
75                 start += ret;
76         }
77         free(buf);
78         return 0;
79 }
80
81 /*
82  * @log: the log we are replaying.
83  * @entry: the discard entry.
84  *
85  * Discard the given length.  If the device supports discard we will call that
86  * ioctl, otherwise we will write 0's to emulate discard.  If the discard size
87  * is larger than log->max_zero_size then we will simply skip the zero'ing if
88  * the drive doesn't support discard.
89  */
90 int log_discard(struct log *log, struct log_write_entry *entry)
91 {
92         u64 start = le64_to_cpu(entry->sector) * log->sectorsize;
93         u64 size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
94         u64 max_chunk = 1 * 1024 * 1024 * 1024;
95
96         if (log->flags & LOG_IGNORE_DISCARD)
97                 return 0;
98
99         while (size) {
100                 u64 len = size > max_chunk ? max_chunk : size;
101                 int ret;
102
103                 /*
104                  * Do this check first in case it is our first discard, that way
105                  * if we return EOPNOTSUPP we will fall back to the 0 method
106                  * automatically.
107                  */
108                 if (!(log->flags & LOG_DISCARD_NOT_SUPP))
109                         ret = discard_range(log, start, len);
110                 if (log->flags & LOG_DISCARD_NOT_SUPP)
111                         ret = zero_range(log, start, len);
112                 if (ret)
113                         return -1;
114                 size -= len;
115                 start += len;
116         }
117         return 0;
118 }
119
120 /*
121  * @log: the log we are replaying.
122  * @entry: where we put the entry.
123  * @read_data: read the entry data as well, entry must be log->sectorsize sized
124  * if this is set.
125  *
126  * @return: 0 if we replayed, 1 if we are at the end, -1 if there was an error.
127  *
128  * Replay the next entry in our log onto the replay device.
129  */
130 int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
131                           int read_data)
132 {
133         u64 size;
134         u64 flags;
135         size_t read_size = read_data ? log->sectorsize :
136                 sizeof(struct log_write_entry);
137         char *buf;
138         ssize_t ret;
139         off_t offset;
140
141         if (log->cur_entry >= log->nr_entries)
142                 return 1;
143
144         ret = read(log->logfd, entry, read_size);
145         if (ret != read_size) {
146                 fprintf(stderr, "Error reading entry: %d\n", errno);
147                 return -1;
148         }
149         log->cur_entry++;
150
151         size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
152         if (read_size < log->sectorsize) {
153                 if (lseek(log->logfd,
154                           log->sectorsize - sizeof(struct log_write_entry),
155                           SEEK_CUR) == (off_t)-1) {
156                         fprintf(stderr, "Error seeking in log: %d\n", errno);
157                         return -1;
158                 }
159         }
160
161         if (log_writes_verbose)
162                 printf("replaying %d: sector %llu, size %llu, flags %llu\n",
163                        (int)log->cur_entry - 1,
164                        (unsigned long long)le64_to_cpu(entry->sector),
165                        (unsigned long long)size,
166                        (unsigned long long)le64_to_cpu(entry->flags));
167         if (!size)
168                 return 0;
169
170         flags = le64_to_cpu(entry->flags);
171         if (flags & LOG_DISCARD_FLAG)
172                 return log_discard(log, entry);
173
174         buf = malloc(size);
175         if (!buf) {
176                 fprintf(stderr, "Error allocating buffer %llu entry %llu\n", (unsigned long long)size, (unsigned long long)log->cur_entry - 1);
177                 return -1;
178         }
179
180         ret = read(log->logfd, buf, size);
181         if (ret != size) {
182                 fprintf(stderr, "Error reading data: %d\n", errno);
183                 free(buf);
184                 return -1;
185         }
186
187         offset = le64_to_cpu(entry->sector) * log->sectorsize;
188         ret = pwrite(log->replayfd, buf, size, offset);
189         free(buf);
190         if (ret != size) {
191                 fprintf(stderr, "Error writing data: %d\n", errno);
192                 return -1;
193         }
194
195         return 0;
196 }
197
198 /*
199  * @log: the log we are manipulating.
200  * @entry_num: the entry we want.
201  *
202  * Seek to the given entry in the log, starting at 0 and ending at
203  * log->nr_entries - 1.
204  */
205 int log_seek_entry(struct log *log, u64 entry_num)
206 {
207         u64 i = 0;
208
209         if (entry_num >= log->nr_entries) {
210                 fprintf(stderr, "Invalid entry number\n");
211                 return -1;
212         }
213
214         /* Skip the first sector containing the log super block */
215         if (lseek(log->logfd, log->sectorsize, SEEK_SET) == (off_t)-1) {
216                 fprintf(stderr, "Error seeking in file: %d\n", errno);
217                 return -1;
218         }
219
220         log->cur_entry = 0;
221         for (i = 0; i < entry_num; i++) {
222                 struct log_write_entry entry;
223                 ssize_t ret;
224                 off_t seek_size;
225                 u64 flags;
226
227                 ret = read(log->logfd, &entry, sizeof(entry));
228                 if (ret != sizeof(entry)) {
229                         fprintf(stderr, "Error reading entry: %d\n", errno);
230                         return -1;
231                 }
232                 if (log_writes_verbose > 1)
233                         printf("seek entry %d: %llu, size %llu, flags %llu\n",
234                                (int)i,
235                                (unsigned long long)le64_to_cpu(entry.sector),
236                                (unsigned long long)le64_to_cpu(entry.nr_sectors),
237                                (unsigned long long)le64_to_cpu(entry.flags));
238                 flags = le64_to_cpu(entry.flags);
239                 seek_size = log->sectorsize - sizeof(entry);
240                 if (!(flags & LOG_DISCARD_FLAG))
241                         seek_size += le64_to_cpu(entry.nr_sectors) *
242                                 log->sectorsize;
243                 if (lseek(log->logfd, seek_size, SEEK_CUR) == (off_t)-1) {
244                         fprintf(stderr, "Error seeking in file: %d\n", errno);
245                         return -1;
246                 }
247                 log->cur_entry++;
248         }
249
250         return 0;
251 }
252
253 /*
254  * @log: the log we are manipulating.
255  * @entry: the entry we read.
256  * @read_data: read the extra data for the entry, your entry must be
257  * log->sectorsize large.
258  *
259  * @return: 1 if we hit the end of the log, 0 we got the next entry, < 0 if
260  * there was an error.
261  *
262  * Seek to the next entry in the log.
263  */
264 int log_seek_next_entry(struct log *log, struct log_write_entry *entry,
265                         int read_data)
266 {
267         size_t read_size = read_data ? log->sectorsize :
268                 sizeof(struct log_write_entry);
269         u64 flags;
270         ssize_t ret;
271
272         if (log->cur_entry >= log->nr_entries)
273                 return 1;
274
275         ret = read(log->logfd, entry, read_size);
276         if (ret != read_size) {
277                 fprintf(stderr, "Error reading entry: %d\n", errno);
278                 return -1;
279         }
280         log->cur_entry++;
281
282         if (read_size < log->sectorsize) {
283                 if (lseek(log->logfd,
284                           log->sectorsize - sizeof(struct log_write_entry),
285                           SEEK_CUR) == (off_t)-1) {
286                         fprintf(stderr, "Error seeking in log: %d\n", errno);
287                         return -1;
288                 }
289         }
290         if (log_writes_verbose > 1)
291                 printf("seek entry %d: %llu, size %llu, flags %llu\n",
292                        (int)log->cur_entry - 1,
293                        (unsigned long long)le64_to_cpu(entry->sector),
294                        (unsigned long long)le64_to_cpu(entry->nr_sectors),
295                        (unsigned long long)le64_to_cpu(entry->flags));
296
297         flags = le32_to_cpu(entry->flags);
298         read_size = le32_to_cpu(entry->nr_sectors) * log->sectorsize;
299         if (!read_size || (flags & LOG_DISCARD_FLAG))
300                 return 0;
301
302         if (lseek(log->logfd, read_size, SEEK_CUR) == (off_t)-1) {
303                 fprintf(stderr, "Error seeking in log: %d\n", errno);
304                 return -1;
305         }
306
307         return 0;
308 }
309
310 /*
311  * @logfile: the file that contains the write log.
312  * @replayfile: the file/device to replay onto, can be NULL.
313  *
314  * Opens a logfile and makes sure it is valid and returns a struct log.
315  */
316 struct log *log_open(char *logfile, char *replayfile)
317 {
318         struct log *log;
319         struct log_write_super super;
320         ssize_t ret;
321
322         log = malloc(sizeof(struct log));
323         if (!log) {
324                 fprintf(stderr, "Couldn't alloc log\n");
325                 return NULL;
326         }
327
328         log->replayfd = -1;
329
330         log->logfd = open(logfile, O_RDONLY);
331         if (log->logfd < 0) {
332                 fprintf(stderr, "Couldn't open log %s: %d\n", logfile,
333                         errno);
334                 log_free(log);
335                 return NULL;
336         }
337
338         if (replayfile) {
339                 log->replayfd = open(replayfile, O_WRONLY);
340                 if (log->replayfd < 0) {
341                         fprintf(stderr, "Couldn't open replay file %s: %d\n",
342                                 replayfile, errno);
343                         log_free(log);
344                         return NULL;
345                 }
346         }
347
348         ret = read(log->logfd, &super, sizeof(struct log_write_super));
349         if (ret < sizeof(struct log_write_super)) {
350                 fprintf(stderr, "Error reading super: %d\n", errno);
351                 log_free(log);
352                 return NULL;
353         }
354
355         if (le64_to_cpu(super.magic) != WRITE_LOG_MAGIC) {
356                 fprintf(stderr, "Magic doesn't match\n");
357                 log_free(log);
358                 return NULL;
359         }
360
361         if (le64_to_cpu(super.version) != WRITE_LOG_VERSION) {
362                 fprintf(stderr, "Version mismatch, wanted %d, have %d\n",
363                         WRITE_LOG_VERSION, (int)le64_to_cpu(super.version));
364                 log_free(log);
365                 return NULL;
366         }
367
368         log->sectorsize = le32_to_cpu(super.sectorsize);
369         log->nr_entries = le64_to_cpu(super.nr_entries);
370         log->max_zero_size = 128 * 1024 * 1024;
371
372         if (lseek(log->logfd, log->sectorsize - sizeof(super), SEEK_CUR) ==
373             (off_t) -1) {
374                 fprintf(stderr, "Error seeking to first entry: %d\n", errno);
375                 log_free(log);
376                 return NULL;
377         }
378         log->cur_entry = 0;
379
380         return log;
381 }