dd: support for O_DIRECT i/o
Based on patch by Akash Hadke <hadkeakash4@gmail.com>
function old new delta
dd_read - 66 +66
clear_O_DIRECT - 55 +55
write_and_stats 102 135 +33
dd_main 1578 1601 +23
static.oflag_words 19 26 +7
static.iflag_words 22 29 +7
packed_usage 33665 33668 +3
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 5/0 up/down: 194/0) Total: 194 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/coreutils/dd.c b/coreutils/dd.c
index b5f3cbe..24d7f0b 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -59,7 +59,7 @@
//usage: "[if=FILE] [of=FILE] [" IF_FEATURE_DD_IBS_OBS("ibs=N obs=N/") "bs=N] [count=N] [skip=N] [seek=N]\n"
//usage: IF_FEATURE_DD_IBS_OBS(
//usage: " [conv=notrunc|noerror|sync|fsync]\n"
-//usage: " [iflag=skip_bytes|fullblock] [oflag=seek_bytes|append]"
+//usage: " [iflag=skip_bytes|fullblock|direct] [oflag=seek_bytes|append|direct]"
//usage: )
//usage:#define dd_full_usage "\n\n"
//usage: "Copy a file with converting and formatting\n"
@@ -82,9 +82,11 @@
//usage: "\n conv=fsync Physically write data out before finishing"
//usage: "\n conv=swab Swap every pair of bytes"
//usage: "\n iflag=skip_bytes skip=N is in bytes"
-//usage: "\n iflag=fullblock Read full blocks"
//usage: "\n oflag=seek_bytes seek=N is in bytes"
-//usage: "\n oflag=append Open output file in append mode"
+//usage: "\n iflag=direct O_DIRECT input"
+//usage: "\n oflag=direct O_DIRECT output"
+//usage: "\n iflag=fullblock Read full blocks"
+//usage: "\n oflag=append Open output in append mode"
//usage: )
//usage: IF_FEATURE_DD_STATUS(
//usage: "\n status=noxfer Suppress rate output"
@@ -137,16 +139,18 @@
FLAG_IFLAG_SHIFT = 5,
FLAG_SKIP_BYTES = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
FLAG_FULLBLOCK = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_IDIRECT = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS,
/* end of input flags */
/* start of output flags */
- FLAG_OFLAG_SHIFT = 7,
- FLAG_SEEK_BYTES = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_APPEND = (1 << 8) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_OFLAG_SHIFT = 8,
+ FLAG_SEEK_BYTES = (1 << 8) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_APPEND = (1 << 9) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_ODIRECT = (1 << 10) * ENABLE_FEATURE_DD_IBS_OBS,
/* end of output flags */
- FLAG_TWOBUFS = (1 << 9) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_COUNT = 1 << 10,
- FLAG_STATUS_NONE = 1 << 11,
- FLAG_STATUS_NOXFER = 1 << 12,
+ FLAG_TWOBUFS = (1 << 11) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_COUNT = 1 << 12,
+ FLAG_STATUS_NONE = 1 << 13,
+ FLAG_STATUS_NOXFER = 1 << 14,
};
static void dd_output_status(int UNUSED_PARAM cur_signal)
@@ -192,12 +196,50 @@
#endif
}
+#if ENABLE_FEATURE_DD_IBS_OBS
+static int clear_O_DIRECT(int fd)
+{
+ if (errno == EINVAL) {
+ int fl = fcntl(fd, F_GETFL);
+ if (fl & O_DIRECT) {
+ fcntl(fd, F_SETFL, fl & ~O_DIRECT);
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+static ssize_t dd_read(void *ibuf, size_t ibs)
+{
+ ssize_t n;
+
+#if ENABLE_FEATURE_DD_IBS_OBS
+ read_again:
+ if (G.flags & FLAG_FULLBLOCK)
+ n = full_read(ifd, ibuf, ibs);
+ else
+#endif
+ n = safe_read(ifd, ibuf, ibs);
+#if ENABLE_FEATURE_DD_IBS_OBS
+ if (n < 0 && (G.flags & FLAG_IDIRECT) && clear_O_DIRECT(ifd))
+ goto read_again;
+#endif
+ return n;
+}
+
static bool write_and_stats(const void *buf, size_t len, size_t obs,
const char *filename)
{
ssize_t n;
+ IF_FEATURE_DD_IBS_OBS(write_again:)
n = full_write(ofd, buf, len);
+#if ENABLE_FEATURE_DD_IBS_OBS
+ if (n < 0 && (G.flags & FLAG_ODIRECT) && clear_O_DIRECT(ofd))
+ goto write_again;
+#endif
+
#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
if (n > 0)
G.total_bytes += n;
@@ -254,6 +296,14 @@
}
#endif
+static void *alloc_buf(size_t size)
+{
+ /* Important for "{i,o}flag=direct" - buffers must be page aligned */
+ if (size >= bb_getpagesize())
+ return xmmap_anon(size);
+ return xmalloc(size);
+}
+
int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int dd_main(int argc UNUSED_PARAM, char **argv)
{
@@ -267,9 +317,9 @@
static const char conv_words[] ALIGN1 =
"notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
static const char iflag_words[] ALIGN1 =
- "skip_bytes\0""fullblock\0";
+ "skip_bytes\0""fullblock\0""direct\0";
static const char oflag_words[] ALIGN1 =
- "seek_bytes\0append\0";
+ "seek_bytes\0append\0""direct\0";
#endif
#if ENABLE_FEATURE_DD_STATUS
static const char status_words[] ALIGN1 =
@@ -310,7 +360,9 @@
//swab swap every pair of input bytes: will abort on non-even reads
OP_iflag_skip_bytes,
OP_iflag_fullblock,
+ OP_iflag_direct,
OP_oflag_seek_bytes,
+ OP_oflag_direct,
#endif
};
smallint exitcode = EXIT_FAILURE;
@@ -426,13 +478,12 @@
#endif
} /* end of "for (argv[i])" */
-//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
- ibuf = xmalloc(ibs);
+ ibuf = alloc_buf(ibs);
obuf = ibuf;
#if ENABLE_FEATURE_DD_IBS_OBS
if (ibs != obs) {
G.flags |= FLAG_TWOBUFS;
- obuf = xmalloc(obs);
+ obuf = alloc_buf(obs);
}
#endif
@@ -444,7 +495,12 @@
#endif
if (infile) {
- xmove_fd(xopen(infile, O_RDONLY), ifd);
+ int iflag = O_RDONLY;
+#if ENABLE_FEATURE_DD_IBS_OBS
+ if (G.flags & FLAG_IDIRECT)
+ iflag |= O_DIRECT;
+#endif
+ xmove_fd(xopen(infile, iflag), ifd);
} else {
infile = bb_msg_standard_input;
}
@@ -455,7 +511,10 @@
oflag |= O_TRUNC;
if (G.flags & FLAG_APPEND)
oflag |= O_APPEND;
-
+#if ENABLE_FEATURE_DD_IBS_OBS
+ if (G.flags & FLAG_ODIRECT)
+ oflag |= O_DIRECT;
+#endif
xmove_fd(xopen(outfile, oflag), ofd);
if (seek && !(G.flags & FLAG_NOTRUNC)) {
@@ -478,13 +537,7 @@
size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs;
if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) {
do {
- ssize_t n;
-#if ENABLE_FEATURE_DD_IBS_OBS
- if (G.flags & FLAG_FULLBLOCK)
- n = full_read(ifd, ibuf, blocksz);
- else
-#endif
- n = safe_read(ifd, ibuf, blocksz);
+ ssize_t n = dd_read(ibuf, blocksz);
if (n < 0)
goto die_infile;
if (n == 0)
@@ -499,13 +552,7 @@
}
while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
- ssize_t n;
-#if ENABLE_FEATURE_DD_IBS_OBS
- if (G.flags & FLAG_FULLBLOCK)
- n = full_read(ifd, ibuf, ibs);
- else
-#endif
- n = safe_read(ifd, ibuf, ibs);
+ ssize_t n = dd_read(ibuf, ibs);
if (n == 0)
break;
if (n < 0) {
@@ -600,11 +647,13 @@
if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE))
dd_output_status(0);
+#if 0 /* can't just free(), they can be mmap()ed */
if (ENABLE_FEATURE_CLEAN_UP) {
free(obuf);
if (G.flags & FLAG_TWOBUFS)
free(ibuf);
}
+#endif
return exitcode;
}