mirror of https://github.com/schoebel/mars
163 lines
3.7 KiB
C
163 lines
3.7 KiB
C
// (c) 2013 Thomas Schoebel-Theuer / 1&1 Internet AG
|
|
|
|
/* This is a trivial hacker's tool for testing the reboot saftey of MARS
|
|
* (or other system software).
|
|
*
|
|
* The trick is to reboot _immediately_ after the last IO operation.
|
|
* Will the block layer contain _all_ the data, at least if it had been
|
|
* sync()ed before?
|
|
*
|
|
* Note: the transaction logfiles of MARS may contain an incomplete
|
|
* log record at the end if there was (additional) IO running _in parallel_
|
|
* to the reboot operation.
|
|
* Even then, the transaction logfile must contain all the data which
|
|
* has been reported as "completed" by O_DIRECT or O_SYNC or by one
|
|
* of the fsync() / fdatasync() operations.
|
|
* In addition, MARS must tolerate such incomplete records because they
|
|
* are unavoidable as such (races between IO and reboot / reset).
|
|
*
|
|
* Exception: when /proc/sys/mars/logger_completion_semantics is deliberately
|
|
* set to 0, some synced IO may get lost. However, even then the _order_ must
|
|
* be correct.
|
|
*
|
|
* Hint: you may also try physical reset via IPMI
|
|
*/
|
|
|
|
#define _FILE_OFFSET_BITS 64
|
|
#define _LARGEFILE64_SOURCE
|
|
#define _GNU_SOURCE
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <linux/reboot.h>
|
|
#include <sys/reboot.h>
|
|
|
|
#include <time.h>
|
|
|
|
#define BLK_SIZE 1024
|
|
|
|
static
|
|
time_t now = 0;
|
|
|
|
static
|
|
int blk_size = BLK_SIZE;
|
|
|
|
void write_block(int fd, int nr)
|
|
{
|
|
char *buf = aligned_alloc(blk_size, blk_size);
|
|
int status;
|
|
|
|
if (!buf) {
|
|
fprintf(stderr, "no mem\n");
|
|
exit (-1);
|
|
}
|
|
|
|
/* Hint: you may simply use the tool "strings" to dig for
|
|
* the markers produced by this.
|
|
* When running multiple times, check the correctness of the
|
|
* timestamps.
|
|
*/
|
|
memset(buf, 0, blk_size);
|
|
snprintf(buf, blk_size, "timestamp %lld block %05d\n", (long long)now, nr);
|
|
|
|
status = write(fd, buf, blk_size);
|
|
if (status != blk_size) {
|
|
fprintf(stderr, "write() #%d failed, status = %d, errno = %d %s\n", nr, status, errno, strerror(errno));
|
|
exit (-1);
|
|
}
|
|
|
|
free(buf);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
/* smode:
|
|
* 0 = only write blocks (see fmode) [DEFAULT]
|
|
* 1 = immedialtely reboot afterwards (no sync, no pause)
|
|
* 2 = use fdatasync() before reboot
|
|
* 3 = use fsync() before reboot
|
|
*/
|
|
int smode = 0;
|
|
|
|
/* fmode:
|
|
* 0 = buffered write (IO may get lost because of buffering)
|
|
* 1 = use O_DIRECT (IO should not get lost) [DEFAULT]
|
|
* 2 = use O_SYNC (IO should not get lost)
|
|
* [combine the bits like a bitmask]
|
|
*/
|
|
int fmode = 1;
|
|
int flags = O_WRONLY | O_CREAT;
|
|
|
|
int count = 1000;
|
|
int fd;
|
|
int i;
|
|
|
|
if (argc < 2) {
|
|
fprintf(stderr, "usage: write-reboot <filename> [<smode>] [<fmode>] [<count>] [<blk_size>]\n");
|
|
exit (-1);
|
|
}
|
|
if (argc > 2) {
|
|
smode = atoi(argv[2]);
|
|
}
|
|
if (argc > 3) {
|
|
fmode = atoi(argv[3]);
|
|
}
|
|
if (fmode & 1)
|
|
flags |= O_DIRECT;
|
|
if (fmode & 2)
|
|
flags |= O_SYNC;
|
|
if (argc > 4) {
|
|
count = atoi(argv[4]);
|
|
}
|
|
if (argc > 5) {
|
|
blk_size = atoi(argv[5]);
|
|
}
|
|
|
|
time(&now);
|
|
|
|
printf("now = %lld file = '%s' fmode = %d smode = %d count = %d blk_size = %d\n",
|
|
(long long)now,
|
|
argv[1],
|
|
fmode,
|
|
smode,
|
|
count,
|
|
blk_size);
|
|
|
|
fd = open(argv[1], flags, 0600);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "open() with flags %d failed, errno = %d %s\n", flags, errno, strerror(errno));
|
|
exit (-1);
|
|
}
|
|
printf("fd = %d\n", fd);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
write_block(fd, i);
|
|
}
|
|
|
|
printf("done.\n");
|
|
|
|
if (smode > 2) {
|
|
printf("fsync()\n");
|
|
fsync(fd);
|
|
} else
|
|
if (smode > 1) {
|
|
printf("fdatasync()\n");
|
|
fdatasync(fd);
|
|
}
|
|
|
|
if (smode > 0) {
|
|
printf("reboot...\n");
|
|
reboot(LINUX_REBOOT_CMD_RESTART);
|
|
}
|
|
|
|
return 0;
|
|
}
|