replay-log: add support for replaying ops in target device sector range
[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: entry to be replayed.
123  *
124  * @return: 0 if we should replay the entry, > 0 if we should skip it.
125  *
126  * Should we skip the entry in our log or replay onto the replay device.
127  */
128 int log_should_skip(struct log *log, struct log_write_entry *entry)
129 {
130         u64 sector = le64_to_cpu(entry->sector);
131         u64 nr_sectors = le64_to_cpu(entry->nr_sectors);
132
133         if (!nr_sectors)
134                 return 0;
135         if (sector + nr_sectors <= log->start_sector ||
136             sector > log->end_sector)
137                 return 1;
138         return 0;
139 }
140
141 /*
142  * @entry: entry to be replayed.
143  *
144  * @return: 1 if the entry is sane, 0 if it is invalid.
145  *
146  * Check if this is a sane log entry.
147  */
148 int log_entry_valid(struct log_write_entry *entry)
149 {
150         u64 flags = le64_to_cpu(entry->flags);
151
152         /* Suspect all zeroes entry */
153         if (!flags && !entry->nr_sectors)
154                 return 0;
155         /* Suspect non zero padded entry */
156         if (flags != LOG_MARK_FLAG && entry->data[0] != 0)
157                 return 0;
158         return 1;
159 }
160
161 /*
162  * @log: the log we are replaying.
163  * @entry: where we put the entry.
164  * @read_data: read the entry data as well, entry must be log->sectorsize sized
165  * if this is set.
166  *
167  * @return: 0 if we replayed, 1 if we are at the end, -1 if there was an error.
168  *
169  * Replay the next entry in our log onto the replay device.
170  */
171 int log_replay_next_entry(struct log *log, struct log_write_entry *entry,
172                           int read_data)
173 {
174         u64 size;
175         u64 flags;
176         size_t read_size = read_data ? log->sectorsize :
177                 sizeof(struct log_write_entry);
178         char *buf;
179         ssize_t ret;
180         off_t offset;
181         int skip = 0;
182
183         if (log->cur_entry >= log->nr_entries)
184                 return 1;
185
186         ret = read(log->logfd, entry, read_size);
187         if (ret != read_size) {
188                 fprintf(stderr, "Error reading entry: %d\n", errno);
189                 return -1;
190         }
191         if (!log_entry_valid(entry)) {
192                 fprintf(stderr, "Malformed entry @%llu\n",
193                                 log->cur_pos / log->sectorsize);
194                 return -1;
195         }
196         log->cur_entry++;
197
198         size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
199         if (read_size < log->sectorsize) {
200                 log->cur_pos = lseek(log->logfd,
201                         log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
202                 if (log->cur_pos == (off_t)-1) {
203                         fprintf(stderr, "Error seeking in log: %d\n", errno);
204                         return -1;
205                 }
206         } else {
207                 log->cur_pos += read_size;
208         }
209
210         skip = log_should_skip(log, entry);
211         if (log_writes_verbose > 1 || (log_writes_verbose && !skip)) {
212                 printf("%s %d@%llu: sector %llu, size %llu, flags %llu\n",
213                        skip ? "skipping" : "replaying",
214                        (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
215                        (unsigned long long)le64_to_cpu(entry->sector),
216                        (unsigned long long)size,
217                        (unsigned long long)le64_to_cpu(entry->flags));
218         }
219         if (!size)
220                 return 0;
221
222         flags = le64_to_cpu(entry->flags);
223         if (flags & LOG_DISCARD_FLAG)
224                 return log_discard(log, entry);
225
226         if (skip) {
227                 log->cur_pos = lseek(log->logfd, size, SEEK_CUR);
228                 if (log->cur_pos == (off_t)-1) {
229                         fprintf(stderr, "Error seeking in log: %d\n", errno);
230                         return -1;
231                 }
232                 return 0;
233         }
234
235         buf = malloc(size);
236         if (!buf) {
237                 fprintf(stderr, "Error allocating buffer %llu entry %llu\n", (unsigned long long)size, (unsigned long long)log->cur_entry - 1);
238                 return -1;
239         }
240
241         ret = read(log->logfd, buf, size);
242         if (ret != size) {
243                 fprintf(stderr, "Error reading data: %d\n", errno);
244                 free(buf);
245                 return -1;
246         }
247         log->cur_pos += size;
248
249         offset = le64_to_cpu(entry->sector) * log->sectorsize;
250         ret = pwrite(log->replayfd, buf, size, offset);
251         free(buf);
252         if (ret != size) {
253                 fprintf(stderr, "Error writing data: %d\n", errno);
254                 return -1;
255         }
256
257         return 0;
258 }
259
260 /*
261  * @log: the log we are manipulating.
262  * @entry_num: the entry we want.
263  *
264  * Seek to the given entry in the log, starting at 0 and ending at
265  * log->nr_entries - 1.
266  */
267 int log_seek_entry(struct log *log, u64 entry_num)
268 {
269         u64 i = 0;
270
271         if (entry_num >= log->nr_entries) {
272                 fprintf(stderr, "Invalid entry number\n");
273                 return -1;
274         }
275
276         /* Skip the first sector containing the log super block */
277         log->cur_pos = lseek(log->logfd, log->sectorsize, SEEK_SET);
278         if (log->cur_pos == (off_t)-1) {
279                 fprintf(stderr, "Error seeking in file: %d\n", errno);
280                 return -1;
281         }
282
283         log->cur_entry = 0;
284         for (i = 0; i < entry_num; i++) {
285                 struct log_write_entry entry;
286                 ssize_t ret;
287                 off_t seek_size;
288                 u64 flags;
289
290                 ret = read(log->logfd, &entry, sizeof(entry));
291                 if (ret != sizeof(entry)) {
292                         fprintf(stderr, "Error reading entry: %d\n", errno);
293                         return -1;
294                 }
295                 if (!log_entry_valid(&entry)) {
296                         fprintf(stderr, "Malformed entry @%llu\n",
297                                         log->cur_pos / log->sectorsize);
298                         return -1;
299                 }
300                 if (log_writes_verbose > 1)
301                         printf("seek entry %d@%llu: %llu, size %llu, flags %llu\n",
302                                (int)i, log->cur_pos / log->sectorsize,
303                                (unsigned long long)le64_to_cpu(entry.sector),
304                                (unsigned long long)le64_to_cpu(entry.nr_sectors),
305                                (unsigned long long)le64_to_cpu(entry.flags));
306                 flags = le64_to_cpu(entry.flags);
307                 seek_size = log->sectorsize - sizeof(entry);
308                 if (!(flags & LOG_DISCARD_FLAG))
309                         seek_size += le64_to_cpu(entry.nr_sectors) *
310                                 log->sectorsize;
311                 log->cur_pos = lseek(log->logfd, seek_size, SEEK_CUR);
312                 if (log->cur_pos == (off_t)-1) {
313                         fprintf(stderr, "Error seeking in file: %d\n", errno);
314                         return -1;
315                 }
316                 log->cur_entry++;
317         }
318
319         return 0;
320 }
321
322 /*
323  * @log: the log we are manipulating.
324  * @entry: the entry we read.
325  * @read_data: read the extra data for the entry, your entry must be
326  * log->sectorsize large.
327  *
328  * @return: 1 if we hit the end of the log, 0 we got the next entry, < 0 if
329  * there was an error.
330  *
331  * Seek to the next entry in the log.
332  */
333 int log_seek_next_entry(struct log *log, struct log_write_entry *entry,
334                         int read_data)
335 {
336         size_t read_size = read_data ? log->sectorsize :
337                 sizeof(struct log_write_entry);
338         u64 flags;
339         ssize_t ret;
340
341         if (log->cur_entry >= log->nr_entries)
342                 return 1;
343
344         ret = read(log->logfd, entry, read_size);
345         if (ret != read_size) {
346                 fprintf(stderr, "Error reading entry: %d\n", errno);
347                 return -1;
348         }
349         if (!log_entry_valid(entry)) {
350                 fprintf(stderr, "Malformed entry @%llu\n",
351                                 log->cur_pos / log->sectorsize);
352                 return -1;
353         }
354         log->cur_entry++;
355
356         if (read_size < log->sectorsize) {
357                 log->cur_pos = lseek(log->logfd,
358                         log->sectorsize - sizeof(struct log_write_entry), SEEK_CUR);
359                 if (log->cur_pos == (off_t)-1) {
360                         fprintf(stderr, "Error seeking in log: %d\n", errno);
361                         return -1;
362                 }
363         } else {
364                 log->cur_pos += read_size;
365         }
366         if (log_writes_verbose > 1)
367                 printf("seek entry %d@%llu: %llu, size %llu, flags %llu\n",
368                        (int)log->cur_entry - 1, log->cur_pos / log->sectorsize,
369                        (unsigned long long)le64_to_cpu(entry->sector),
370                        (unsigned long long)le64_to_cpu(entry->nr_sectors),
371                        (unsigned long long)le64_to_cpu(entry->flags));
372
373         flags = le64_to_cpu(entry->flags);
374         read_size = le64_to_cpu(entry->nr_sectors) * log->sectorsize;
375         if (!read_size || (flags & LOG_DISCARD_FLAG))
376                 return 0;
377
378         log->cur_pos = lseek(log->logfd, read_size, SEEK_CUR);
379         if (log->cur_pos == (off_t)-1) {
380                 fprintf(stderr, "Error seeking in log: %d\n", errno);
381                 return -1;
382         }
383
384         return 0;
385 }
386
387 /*
388  * @logfile: the file that contains the write log.
389  * @replayfile: the file/device to replay onto, can be NULL.
390  *
391  * Opens a logfile and makes sure it is valid and returns a struct log.
392  */
393 struct log *log_open(char *logfile, char *replayfile)
394 {
395         struct log *log;
396         struct log_write_super super;
397         ssize_t ret;
398
399         log = malloc(sizeof(struct log));
400         if (!log) {
401                 fprintf(stderr, "Couldn't alloc log\n");
402                 return NULL;
403         }
404
405         log->replayfd = -1;
406
407         log->logfd = open(logfile, O_RDONLY);
408         if (log->logfd < 0) {
409                 fprintf(stderr, "Couldn't open log %s: %d\n", logfile,
410                         errno);
411                 log_free(log);
412                 return NULL;
413         }
414
415         if (replayfile) {
416                 log->replayfd = open(replayfile, O_WRONLY);
417                 if (log->replayfd < 0) {
418                         fprintf(stderr, "Couldn't open replay file %s: %d\n",
419                                 replayfile, errno);
420                         log_free(log);
421                         return NULL;
422                 }
423         }
424
425         ret = read(log->logfd, &super, sizeof(struct log_write_super));
426         if (ret < sizeof(struct log_write_super)) {
427                 fprintf(stderr, "Error reading super: %d\n", errno);
428                 log_free(log);
429                 return NULL;
430         }
431
432         if (le64_to_cpu(super.magic) != WRITE_LOG_MAGIC) {
433                 fprintf(stderr, "Magic doesn't match\n");
434                 log_free(log);
435                 return NULL;
436         }
437
438         if (le64_to_cpu(super.version) != WRITE_LOG_VERSION) {
439                 fprintf(stderr, "Version mismatch, wanted %d, have %d\n",
440                         WRITE_LOG_VERSION, (int)le64_to_cpu(super.version));
441                 log_free(log);
442                 return NULL;
443         }
444
445         log->sectorsize = le32_to_cpu(super.sectorsize);
446         log->nr_entries = le64_to_cpu(super.nr_entries);
447         log->max_zero_size = 128 * 1024 * 1024;
448
449         log->cur_pos = lseek(log->logfd, log->sectorsize - sizeof(super), SEEK_CUR);
450         if (log->cur_pos == (off_t) -1) {
451                 fprintf(stderr, "Error seeking to first entry: %d\n", errno);
452                 log_free(log);
453                 return NULL;
454         }
455         log->cur_entry = 0;
456
457         return log;
458 }