New option "--direct-io".
This commit is contained in:
@@ -44,6 +44,7 @@ struct opts_s { /* structure describing run-time options */
|
||||
unsigned int skip_errors; /* skip read errors counter */
|
||||
bool stop_at_size; /* set if we stop at "size" bytes */
|
||||
bool sync_after_write; /* set if we sync after every write */
|
||||
bool direct_io; /* set if O_DIRECT is to be used */
|
||||
double interval; /* interval between updates */
|
||||
double delay_start; /* delay before first display */
|
||||
unsigned int watch_pid; /* process to watch fds of */
|
||||
|
||||
@@ -94,6 +94,8 @@ struct pvstate_s {
|
||||
unsigned int skip_errors; /* skip read errors counter */
|
||||
bool stop_at_size; /* set if we stop at "size" bytes */
|
||||
bool sync_after_write; /* set if we sync after every write */
|
||||
bool direct_io; /* set if O_DIRECT is to be used */
|
||||
bool direct_io_changed; /* set when direct_io is changed */
|
||||
bool no_splice; /* never use splice() */
|
||||
unsigned long long rate_limit; /* rate limit, in bytes per second */
|
||||
unsigned long long target_buffer_size; /* buffer size (0=default) */
|
||||
|
||||
@@ -107,6 +107,7 @@ extern void pv_state_no_op_set(pvstate_t, bool);
|
||||
extern void pv_state_skip_errors_set(pvstate_t, unsigned int);
|
||||
extern void pv_state_stop_at_size_set(pvstate_t, bool);
|
||||
extern void pv_state_sync_after_write_set(pvstate_t, bool);
|
||||
extern void pv_state_direct_io_set(pvstate_t, bool);
|
||||
extern void pv_state_rate_limit_set(pvstate_t, unsigned long long);
|
||||
extern void pv_state_target_buffer_size_set(pvstate_t, unsigned long long);
|
||||
extern void pv_state_no_splice_set(pvstate_t, bool);
|
||||
|
||||
@@ -93,6 +93,8 @@ void display_help(void)
|
||||
N_("stop after --size bytes have been transferred")},
|
||||
{"-Y", "--sync", 0,
|
||||
N_("flush cache to disk after every write")},
|
||||
{"-K", "--direct-io", 0,
|
||||
N_("use direct I/O to bypass cache")},
|
||||
#ifdef HAVE_IPC
|
||||
{"-R", "--remote", N_("PID"),
|
||||
N_("update settings of process PID")},
|
||||
|
||||
@@ -202,6 +202,7 @@ int main(int argc, char **argv)
|
||||
pv_state_skip_errors_set(state, opts->skip_errors);
|
||||
pv_state_stop_at_size_set(state, opts->stop_at_size);
|
||||
pv_state_sync_after_write_set(state, opts->sync_after_write);
|
||||
pv_state_direct_io_set(state, opts->direct_io);
|
||||
pv_state_rate_limit_set(state, opts->rate_limit);
|
||||
pv_state_target_buffer_size_set(state, opts->buffer_size);
|
||||
pv_state_no_splice_set(state, opts->no_splice);
|
||||
|
||||
+5
-1
@@ -83,6 +83,7 @@ opts_t opts_parse(int argc, char **argv)
|
||||
{"skip-errors", 0, NULL, (int) 'E'},
|
||||
{"stop-at-size", 0, NULL, (int) 'S'},
|
||||
{"sync", 0, NULL, (int) 'Y'},
|
||||
{"direct-io", 0, NULL, (int) 'K'},
|
||||
{"remote", 1, NULL, (int) 'R'},
|
||||
{"pidfile", 1, NULL, (int) 'P'},
|
||||
{"watchfd", 1, NULL, (int) 'd'},
|
||||
@@ -92,7 +93,7 @@ opts_t opts_parse(int argc, char **argv)
|
||||
int option_index = 0;
|
||||
#endif
|
||||
char *short_options =
|
||||
"hVpteIrab8TA:fnqcWD:s:l0i:w:H:N:F:L:B:CESYR:P:d:m:";
|
||||
"hVpteIrab8TA:fnqcWD:s:l0i:w:H:N:F:L:B:CESYKR:P:d:m:";
|
||||
int c, numopts;
|
||||
unsigned int check_pid;
|
||||
int check_fd;
|
||||
@@ -337,6 +338,9 @@ opts_t opts_parse(int argc, char **argv)
|
||||
case 'Y':
|
||||
opts->sync_after_write = true;
|
||||
break;
|
||||
case 'K':
|
||||
opts->direct_io = true;
|
||||
break;
|
||||
case 'R':
|
||||
opts->remote = pv_getnum_ui(optarg);
|
||||
break;
|
||||
|
||||
+37
-33
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-07-26 22:09+0100\n"
|
||||
"POT-Creation-Date: 2023-07-27 20:49+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -44,7 +44,7 @@ msgstr ""
|
||||
msgid "failed to read file"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/file.c:253 src/main/options.c:292
|
||||
#: src/pv/file.c:253 src/main/options.c:293
|
||||
msgid "failed to stat file"
|
||||
msgstr ""
|
||||
|
||||
@@ -84,11 +84,11 @@ msgstr ""
|
||||
msgid "write failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:726 src/pv/display.c:646
|
||||
#: src/pv/transfer.c:808 src/pv/display.c:646
|
||||
msgid "buffer allocation failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pv/transfer.c:817
|
||||
#: src/pv/transfer.c:913
|
||||
msgid "select call failed"
|
||||
msgstr ""
|
||||
|
||||
@@ -96,7 +96,7 @@ msgstr ""
|
||||
#: src/pv/watchpid.c:110 src/pv/watchpid.c:151 src/pv/watchpid.c:168
|
||||
#: src/pv/watchpid.c:178 src/pv/watchpid.c:192 src/pv/watchpid.c:280
|
||||
#: src/pv/watchpid.c:289 src/pv/watchpid.c:322 src/pv/watchpid.c:343
|
||||
#: src/pv/loop.c:533 src/pv/loop.c:595 src/pv/loop.c:647
|
||||
#: src/pv/loop.c:542 src/pv/loop.c:604 src/pv/loop.c:656
|
||||
msgid "pid"
|
||||
msgstr ""
|
||||
|
||||
@@ -331,55 +331,59 @@ msgid "flush cache to disk after every write"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:97
|
||||
msgid "use direct I/O to bypass cache"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:99
|
||||
msgid "PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:98
|
||||
#: src/main/help.c:100
|
||||
msgid "update settings of process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:101
|
||||
#: src/main/help.c:103
|
||||
msgid "FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:102
|
||||
#: src/main/help.c:104
|
||||
msgid "save process ID in FILE"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:104
|
||||
#: src/main/help.c:106
|
||||
msgid "PID[:FD]"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:105
|
||||
#: src/main/help.c:107
|
||||
msgid "watch file FD opened by process PID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:108
|
||||
#: src/main/help.c:110
|
||||
msgid "show this help and exit"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:110
|
||||
#: src/main/help.c:112
|
||||
msgid "show version information and exit"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:117
|
||||
#: src/main/help.c:119
|
||||
#, c-format
|
||||
msgid "Usage: %s [OPTION] [FILE]..."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:120
|
||||
#: src/main/help.c:122
|
||||
msgid ""
|
||||
"Concatenate FILE(s), or standard input, to standard output,\n"
|
||||
"with monitoring."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:213
|
||||
#: src/main/help.c:215
|
||||
msgid ""
|
||||
"Debugging is enabled; export the DEBUG environment variable to define the\n"
|
||||
"output filename.\n"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/help.c:217
|
||||
#: src/main/help.c:219
|
||||
#, c-format
|
||||
msgid "Please report any bugs to %s."
|
||||
msgstr ""
|
||||
@@ -388,62 +392,62 @@ msgstr ""
|
||||
msgid "state allocation failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/main.c:251
|
||||
#: src/main/main.c:252
|
||||
msgid "failed to read terminal attributes"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:105
|
||||
#: src/main/options.c:106
|
||||
msgid "option structure allocation failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:119
|
||||
#: src/main/options.c:120
|
||||
msgid "option structure argv allocation failed"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:164
|
||||
#: src/main/options.c:165
|
||||
msgid "integer argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:175
|
||||
#: src/main/options.c:176
|
||||
msgid "numeric argument expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:186
|
||||
#: src/main/options.c:187
|
||||
msgid "process ID or pid:fd pair expected"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:193
|
||||
#: src/main/options.c:194
|
||||
msgid "invalid process ID"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:362
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:366
|
||||
#, c-format
|
||||
msgid "Try `%s --help' for more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:370
|
||||
#, c-format
|
||||
msgid "Try `%s -h' for more information."
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:382
|
||||
#: src/main/options.c:386
|
||||
msgid ""
|
||||
"cannot use line mode or transfer modifier options when watching file "
|
||||
"descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:390
|
||||
#: src/main/options.c:394
|
||||
msgid "cannot use cursor positioning when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:398
|
||||
#: src/main/options.c:402
|
||||
msgid "cannot use remote control when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:406
|
||||
#: src/main/options.c:410
|
||||
msgid "cannot transfer files when watching file descriptors"
|
||||
msgstr ""
|
||||
|
||||
#: src/main/options.c:414
|
||||
#: src/main/options.c:418
|
||||
msgid "not available on systems without /proc/self/fdinfo"
|
||||
msgstr ""
|
||||
|
||||
@@ -293,6 +293,18 @@ int pv_next_file(pvstate_t state, int filenum, int oldfd)
|
||||
if (0 == strcmp(state->input_files[filenum], "-")) {
|
||||
state->current_file = "(stdin)";
|
||||
}
|
||||
#ifdef O_DIRECT
|
||||
/*
|
||||
* Set or clear O_DIRECT on the file descriptor.
|
||||
*/
|
||||
fcntl(fd, F_SETFL,
|
||||
(state->direct_io ? O_DIRECT : 0) | fcntl(fd, F_GETFL));
|
||||
/*
|
||||
* We don't clear direct_io_changed here, to avoid race conditions
|
||||
* that could cause the input and output settings to differ.
|
||||
*/
|
||||
#endif /* O_DIRECT */
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,15 @@ int pv_main_loop(pvstate_t state)
|
||||
pv_crs_fini(state);
|
||||
return state->exit_status;
|
||||
}
|
||||
#ifdef O_DIRECT
|
||||
/*
|
||||
* Set or clear O_DIRECT on the output.
|
||||
*/
|
||||
fcntl(STDOUT_FILENO, F_SETFL,
|
||||
(state->direct_io ? O_DIRECT : 0) | fcntl(STDOUT_FILENO,
|
||||
F_GETFL));
|
||||
state->direct_io_changed = false;
|
||||
#endif /* O_DIRECT */
|
||||
|
||||
/*
|
||||
* Set target buffer size if the initial file's block size can be
|
||||
|
||||
@@ -206,6 +206,12 @@ void pv_state_sync_after_write_set(pvstate_t state, bool val)
|
||||
state->sync_after_write = val;
|
||||
};
|
||||
|
||||
void pv_state_direct_io_set(pvstate_t state, bool val)
|
||||
{
|
||||
state->direct_io = val;
|
||||
state->direct_io_changed = true;
|
||||
};
|
||||
|
||||
void pv_state_rate_limit_set(pvstate_t state, unsigned long long val)
|
||||
{
|
||||
state->rate_limit = val;
|
||||
|
||||
+101
-5
@@ -684,6 +684,63 @@ static int pv__transfer_write(pvstate_t state,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a pointer to a newly allocated buffer of the given size, aligned
|
||||
* appropriately for the current input and output file descriptors
|
||||
* (important if using O_DIRECT).
|
||||
*
|
||||
* Falls back to unaligned allocation if it was not possible to get an
|
||||
* aligned buffer, or if the relevant operating system features were not
|
||||
* available. With O_DIRECT, this means that transfers could fail with an
|
||||
* "Invalid argument" error (EINVAL).
|
||||
*
|
||||
* Returns NULL on complete allocation failure.
|
||||
*/
|
||||
static unsigned char *pv__allocate_aligned_buffer(int fd,
|
||||
size_t target_size)
|
||||
{
|
||||
unsigned char *newptr;
|
||||
|
||||
#if defined(HAVE_FPATHCONF) && defined(HAVE_POSIX_MEMALIGN) && defined(_PC_REC_XFER_ALIGN)
|
||||
long input_alignment, output_alignment, min_alignment;
|
||||
size_t required_alignment;
|
||||
|
||||
input_alignment = fd >= 0 ? fpathconf(fd, _PC_REC_XFER_ALIGN) : -1;
|
||||
output_alignment = fpathconf(STDOUT_FILENO, _PC_REC_XFER_ALIGN);
|
||||
# if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
|
||||
min_alignment = sysconf(_SC_PAGESIZE);
|
||||
# else /* ! defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) */
|
||||
min_alignment = 8192;
|
||||
# endif /* defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) */
|
||||
|
||||
if (input_alignment > output_alignment) {
|
||||
required_alignment = input_alignment;
|
||||
} else if (output_alignment > input_alignment) {
|
||||
required_alignment = output_alignment;
|
||||
} else if (input_alignment < min_alignment) {
|
||||
required_alignment = min_alignment;
|
||||
} else {
|
||||
required_alignment = input_alignment;
|
||||
}
|
||||
|
||||
/* Ensure the alignment is at least the page size. */
|
||||
if (required_alignment < min_alignment) {
|
||||
required_alignment = min_alignment;
|
||||
}
|
||||
|
||||
if (0 !=
|
||||
posix_memalign((void **) (&newptr), required_alignment,
|
||||
target_size)) {
|
||||
newptr = (unsigned char *) malloc(target_size);
|
||||
}
|
||||
#else /* ! defined(HAVE_FPATHCONF) && defined(HAVE_POSIX_MEMALIGN) && defined(_PC_REC_XFER_ALIGN) */
|
||||
newptr = (unsigned char *) malloc(target_size);
|
||||
#endif /* defined(HAVE_FPATHCONF) && defined(HAVE_POSIX_MEMALIGN) && defined(_PC_REC_XFER_ALIGN) */
|
||||
|
||||
return newptr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Transfer some data from "fd" to standard output, timing out after 9/100
|
||||
* of a second. If state->rate_limit is >0, and/or "allowed" is >0, only up
|
||||
@@ -707,6 +764,26 @@ long pv_transfer(pvstate_t state, int fd, int *eof_in, int *eof_out,
|
||||
if (NULL == state)
|
||||
return 0;
|
||||
|
||||
#ifdef O_DIRECT
|
||||
/*
|
||||
* Set or clear O_DIRECT on the input and output file descriptors,
|
||||
* if the setting has changed.
|
||||
*/
|
||||
if (state->direct_io_changed) {
|
||||
if (!(*eof_in)) {
|
||||
fcntl(fd, F_SETFL,
|
||||
(state->direct_io ? O_DIRECT : 0) | fcntl(fd,
|
||||
F_GETFL));
|
||||
}
|
||||
if (!(*eof_out)) {
|
||||
fcntl(STDOUT_FILENO, F_SETFL,
|
||||
(state->direct_io ? O_DIRECT : 0) |
|
||||
fcntl(STDOUT_FILENO, F_GETFL));
|
||||
}
|
||||
state->direct_io_changed = false;
|
||||
}
|
||||
#endif /* O_DIRECT */
|
||||
|
||||
/*
|
||||
* Reinitialise the error skipping variables if the file descriptor
|
||||
* has changed since the last time we were called.
|
||||
@@ -717,10 +794,15 @@ long pv_transfer(pvstate_t state, int fd, int *eof_in, int *eof_out,
|
||||
state->read_error_warning_shown = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new buffer, aligned appropriately for the input file
|
||||
* (important if using O_DIRECT).
|
||||
*/
|
||||
if (NULL == state->transfer_buffer) {
|
||||
state->buffer_size = state->target_buffer_size;
|
||||
state->transfer_buffer =
|
||||
(unsigned char *) malloc(state->buffer_size + 32);
|
||||
pv__allocate_aligned_buffer(fd,
|
||||
state->target_buffer_size +
|
||||
32);
|
||||
if (NULL == state->transfer_buffer) {
|
||||
pv_error(state, "%s: %s",
|
||||
_("buffer allocation failed"),
|
||||
@@ -728,16 +810,22 @@ long pv_transfer(pvstate_t state, int fd, int *eof_in, int *eof_out,
|
||||
state->exit_status |= 64;
|
||||
return -1;
|
||||
}
|
||||
state->buffer_size = state->target_buffer_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reallocate the buffer if the buffer size has changed mid-transfer.
|
||||
* Reallocate the buffer if the buffer size has changed
|
||||
* mid-transfer. We have to do this by allocating a new buffer,
|
||||
* copying to it, and freeing the old one (potentially leaking
|
||||
* memory) because the buffer may need to be aligned for O_DIRECT,
|
||||
* and we can't realloc() an aligned buffer.
|
||||
*/
|
||||
if (state->buffer_size < state->target_buffer_size) {
|
||||
unsigned char *newptr;
|
||||
newptr =
|
||||
realloc(state->transfer_buffer,
|
||||
state->target_buffer_size + 32);
|
||||
pv__allocate_aligned_buffer(fd,
|
||||
state->target_buffer_size +
|
||||
32);
|
||||
if (NULL == newptr) {
|
||||
/*
|
||||
* Reset target if realloc failed so we don't keep
|
||||
@@ -748,6 +836,14 @@ long pv_transfer(pvstate_t state, int fd, int *eof_in, int *eof_out,
|
||||
} else {
|
||||
debug("%s: %ld", "buffer resized",
|
||||
state->buffer_size);
|
||||
/*
|
||||
* Copy the old buffer contents into the new buffer,
|
||||
* and free the old one.
|
||||
*/
|
||||
if (state->buffer_size > 0)
|
||||
memcpy(newptr, state->transfer_buffer,
|
||||
state->buffer_size);
|
||||
free(state->transfer_buffer);
|
||||
state->transfer_buffer = newptr;
|
||||
state->buffer_size = state->target_buffer_size;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user