Add speed limit to dd(1). This is useful for testing RCTL disk io limits

(when they actually get committed, that is), and might also come in handy
in other situations.

Reviewed by:	wblock@ (man page)
MFC after:	1 month
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Edward Tomasz Napierala 2016-02-28 10:27:12 +00:00
parent 8d88b09320
commit 75e5511214
5 changed files with 60 additions and 7 deletions

View File

@ -66,6 +66,7 @@ static void f_obs(char *);
static void f_of(char *);
static void f_seek(char *);
static void f_skip(char *);
static void f_speed(char *);
static void f_status(char *);
static uintmax_t get_num(const char *);
static off_t get_off_t(const char *);
@ -89,6 +90,7 @@ static const struct arg {
{ "oseek", f_seek, C_SEEK, C_SEEK },
{ "seek", f_seek, C_SEEK, C_SEEK },
{ "skip", f_skip, C_SKIP, C_SKIP },
{ "speed", f_speed, 0, 0 },
{ "status", f_status, C_STATUS,C_STATUS },
};
@ -294,6 +296,13 @@ f_skip(char *arg)
in.offset = get_off_t(arg);
}
static void
f_speed(char *arg)
{
speed = get_num(arg);
}
static void
f_status(char *arg)
{

View File

@ -32,7 +32,7 @@
.\" @(#)dd.1 8.2 (Berkeley) 1/13/94
.\" $FreeBSD$
.\"
.Dd February 4, 2016
.Dd February 28, 2016
.Dt DD 1
.Os
.Sh NAME
@ -156,6 +156,10 @@ Otherwise, input data is read and discarded.
For pipes, the correct number of bytes is read.
For all other devices, the correct number of blocks is read without
distinguishing between a partial or complete block being read.
.It Cm speed Ns = Ns Ar n
Limit the copying speed to
.Ar n
bytes per second.
.It Cm status Ns = Ns Ar value
Where
.Cm value
@ -325,7 +329,7 @@ appended.
.El
.El
.Pp
Where sizes are specified, a decimal, octal, or hexadecimal number of
Where sizes or speed are specified, a decimal, octal, or hexadecimal number of
bytes is expected.
If the number ends with a
.Dq Li b ,

View File

@ -82,6 +82,7 @@ size_t cbsz; /* conversion block size */
uintmax_t files_cnt = 1; /* # of files to copy */
const u_char *ctab; /* conversion table */
char fill_char; /* Character to fill with if defined */
size_t speed = 0; /* maximum speed, in bytes per second */
volatile sig_atomic_t need_summary;
int
@ -276,6 +277,29 @@ getfdtype(IO *io)
io->flags |= ISSEEK;
}
/*
* Limit the speed by adding a delay before every block read.
* The delay (t_usleep) is equal to the time computed from block
* size and the specified speed limit (t_target) minus the time
* spent on actual read and write operations (t_io).
*/
static void
speed_limit(void)
{
static double t_prev, t_usleep;
double t_now, t_io, t_target;
t_now = secs_elapsed();
t_io = t_now - t_prev - t_usleep;
t_target = (double)in.dbsz / (double)speed;
t_usleep = t_target - t_io;
if (t_usleep > 0)
usleep(t_usleep * 1000000);
else
t_usleep = 0;
t_prev = t_now;
}
static void
dd_in(void)
{
@ -293,6 +317,9 @@ dd_in(void)
break;
}
if (speed > 0)
speed_limit();
/*
* Zero the buffer first if sync; if doing block operations,
* use spaces.

View File

@ -42,6 +42,7 @@ void def_close(void);
void jcl(char **);
void pos_in(void);
void pos_out(void);
double secs_elapsed(void);
void summary(void);
void siginfo_handler(int);
void terminate(int);
@ -54,6 +55,7 @@ extern void (*cfunc)(void);
extern uintmax_t cpy_cnt;
extern size_t cbsz;
extern u_int ddflags;
extern size_t speed;
extern uintmax_t files_cnt;
extern const u_char *ctab;
extern const u_char a2e_32V[], a2e_POSIX[];

View File

@ -54,15 +54,12 @@ __FBSDID("$FreeBSD$");
#include "dd.h"
#include "extern.h"
void
summary(void)
double
secs_elapsed(void)
{
struct timespec end, ts_res;
double secs, res;
if (ddflags & C_NOINFO)
return;
if (clock_gettime(CLOCK_MONOTONIC, &end))
err(1, "clock_gettime");
if (clock_getres(CLOCK_MONOTONIC, &ts_res))
@ -72,6 +69,20 @@ summary(void)
res = ts_res.tv_sec + ts_res.tv_nsec * 1e-9;
if (secs < res)
secs = res;
return (secs);
}
void
summary(void)
{
double secs;
if (ddflags & C_NOINFO)
return;
secs = secs_elapsed();
(void)fprintf(stderr,
"%ju+%ju records in\n%ju+%ju records out\n",
st.in_full, st.in_part, st.out_full, st.out_part);