mirror of
https://github.com/upa/mscp.git
synced 2026-02-04 03:24:58 +08:00
add -L limit bitrate option (#14)
This commit is contained in:
@@ -109,7 +109,7 @@ list(APPEND MSCP_BUILD_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/include)
|
|||||||
# libmscp.a
|
# libmscp.a
|
||||||
set(LIBMSCP_SRC
|
set(LIBMSCP_SRC
|
||||||
src/mscp.c src/ssh.c src/fileops.c src/path.c src/checkpoint.c
|
src/mscp.c src/ssh.c src/fileops.c src/path.c src/checkpoint.c
|
||||||
src/platform.c src/print.c src/pool.c src/strerrno.c
|
src/bwlimit.c src/platform.c src/print.c src/pool.c src/strerrno.c
|
||||||
${OPENBSD_COMPAT_SRC})
|
${OPENBSD_COMPAT_SRC})
|
||||||
add_library(mscp-static STATIC ${LIBMSCP_SRC})
|
add_library(mscp-static STATIC ${LIBMSCP_SRC})
|
||||||
target_include_directories(mscp-static
|
target_include_directories(mscp-static
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ mscp \- copy files over multiple SSH connections
|
|||||||
.BI \-b \ BUF_SIZE\c
|
.BI \-b \ BUF_SIZE\c
|
||||||
]
|
]
|
||||||
[\c
|
[\c
|
||||||
|
.BI \-L \ LIMIT_BITRATE\c
|
||||||
|
]
|
||||||
|
[\c
|
||||||
.BI \-l \ LOGIN_NAME\c
|
.BI \-l \ LOGIN_NAME\c
|
||||||
]
|
]
|
||||||
[\c
|
[\c
|
||||||
@@ -198,6 +201,11 @@ Specifies the buffer size for I/O and transfer over SFTP. The default
|
|||||||
value is 16384. Note that the SSH specification restricts buffer size
|
value is 16384. Note that the SSH specification restricts buffer size
|
||||||
delivered over SSH. Changing this value is not recommended at present.
|
delivered over SSH. Changing this value is not recommended at present.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-L \fILIMIT_BITRATE\fR
|
||||||
|
Limits the bitrate, specified with k (K), m (M), and g (G), e.g., 100m
|
||||||
|
indicates 100 Mbps.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B \-4
|
.B \-4
|
||||||
Uses IPv4 addresses only.
|
Uses IPv4 addresses only.
|
||||||
|
|||||||
22
doc/mscp.rst
22
doc/mscp.rst
@@ -2,7 +2,7 @@
|
|||||||
MSCP
|
MSCP
|
||||||
====
|
====
|
||||||
|
|
||||||
:Date: v0.1.4-28-g0d248c5
|
:Date: v0.1.5-4-g9b8ba69
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
====
|
====
|
||||||
@@ -12,14 +12,14 @@ mscp - copy files over multiple SSH connections
|
|||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
========
|
========
|
||||||
|
|
||||||
**mscp** [**-46vqDpHdNh**] [ **-n**\ *NR_CONNECTIONS* ] [
|
**mscp** [**-46vqDpHdNh**] [ **-n** *NR_CONNECTIONS* ] [ **-m**
|
||||||
**-m**\ *COREMASK* ] [ **-u**\ *MAX_STARTUPS* ] [ **-I**\ *INTERVAL* ] [
|
*COREMASK* ] [ **-u** *MAX_STARTUPS* ] [ **-I** *INTERVAL* ] [ **-W**
|
||||||
**-W**\ *CHECKPOINT* ] [ **-R**\ *CHECKPOINT* ] [
|
*CHECKPOINT* ] [ **-R** *CHECKPOINT* ] [ **-s** *MIN_CHUNK_SIZE* ] [
|
||||||
**-s**\ *MIN_CHUNK_SIZE* ] [ **-S**\ *MAX_CHUNK_SIZE* ] [
|
**-S** *MAX_CHUNK_SIZE* ] [ **-a** *NR_AHEAD* ] [ **-b** *BUF_SIZE* ] [
|
||||||
**-a**\ *NR_AHEAD* ] [ **-b**\ *BUF_SIZE* ] [ **-l**\ *LOGIN_NAME* ] [
|
**-L** *LIMIT_BITRATE* ] [ **-l** *LOGIN_NAME* ] [ **-P** *PORT* ] [
|
||||||
**-P**\ *PORT* ] [ **-F**\ *CONFIG* ] [ **-i**\ *IDENTITY* ] [
|
**-F** *CONFIG* ] [ **-i** *IDENTITY* ] [ **-c** *CIPHER* ] [ **-M**
|
||||||
**-c**\ *CIPHER* ] [ **-M**\ *HMAC* ] [ **-C**\ *COMPRESS* ] [
|
*HMAC* ] [ **-C** *COMPRESS* ] [ **-g** *CONGESTION* ] *source ...
|
||||||
**-g**\ *CONGESTION* ] *source ... target*
|
target*
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
===========
|
===========
|
||||||
@@ -111,6 +111,10 @@ OPTIONS
|
|||||||
delivered over SSH. Changing this value is not recommended at
|
delivered over SSH. Changing this value is not recommended at
|
||||||
present.
|
present.
|
||||||
|
|
||||||
|
**-L LIMIT_BITRATE**
|
||||||
|
Limits the bitrate, specified with k (K), m (M), and g (G), e.g.,
|
||||||
|
100m indicates 100 Mbps.
|
||||||
|
|
||||||
**-4**
|
**-4**
|
||||||
Uses IPv4 addresses only.
|
Uses IPv4 addresses only.
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ struct mscp_opts {
|
|||||||
size_t min_chunk_sz; /** minimum chunk size (default 64MB) */
|
size_t min_chunk_sz; /** minimum chunk size (default 64MB) */
|
||||||
size_t max_chunk_sz; /** maximum chunk size (default file size/nr_threads) */
|
size_t max_chunk_sz; /** maximum chunk size (default file size/nr_threads) */
|
||||||
size_t buf_sz; /** buffer size, default 16k. */
|
size_t buf_sz; /** buffer size, default 16k. */
|
||||||
char *coremask; /** hex to specifiy usable cpu cores */
|
size_t bitrate; /** bits-per-seconds to limit bandwidth */
|
||||||
|
char *coremask; /** hex to specifiy usable cpu cores */
|
||||||
int max_startups; /** sshd MaxStartups concurrent connections */
|
int max_startups; /** sshd MaxStartups concurrent connections */
|
||||||
int interval; /** interval between SSH connection attempts */
|
int interval; /** interval between SSH connection attempts */
|
||||||
bool preserve_ts; /** preserve file timestamps */
|
bool preserve_ts; /** preserve file timestamps */
|
||||||
|
|||||||
94
src/bwlimit.c
Normal file
94
src/bwlimit.c
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-3.0-only */
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <bwlimit.h>
|
||||||
|
#include <platform.h>
|
||||||
|
|
||||||
|
#define timespeczerorize(ts) \
|
||||||
|
do { \
|
||||||
|
ts.tv_sec = 0; \
|
||||||
|
ts.tv_nsec = 0; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
int bwlimit_init(struct bwlimit *bw, uint64_t bps, uint64_t win)
|
||||||
|
{
|
||||||
|
if (!(bw->sem = sem_create(1)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bw->bps = bps;
|
||||||
|
bw->win = win; /* msec window */
|
||||||
|
bw->amt = (double)bps / 8 / 1000 * win; /* bytes in a window (msec) */
|
||||||
|
bw->credit = bw->amt;
|
||||||
|
timespeczerorize(bw->wstart);
|
||||||
|
timespeczerorize(bw->wend);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define timespecisset(ts) ((ts).tv_sec || (ts).tv_nsec)
|
||||||
|
|
||||||
|
#define timespecmsadd(a, msec, r) \
|
||||||
|
do { \
|
||||||
|
(r).tv_sec = (a).tv_sec; \
|
||||||
|
(r).tv_nsec = (a).tv_nsec + (msec * 1000000); \
|
||||||
|
if ((r).tv_nsec > 1000000000) { \
|
||||||
|
(r).tv_sec += (r.tv_nsec) / 1000000000L; \
|
||||||
|
(r).tv_nsec = (r.tv_nsec) % 1000000000L; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define timespecsub(a, b, r) \
|
||||||
|
do { \
|
||||||
|
(r).tv_sec = (a).tv_sec - (b).tv_sec; \
|
||||||
|
(r).tv_nsec = (a).tv_nsec - (b).tv_nsec; \
|
||||||
|
if ((r).tv_nsec < 0) { \
|
||||||
|
(r).tv_sec -= 1; \
|
||||||
|
(r).tv_nsec += 1000000000; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define timespeccmp(a, b, expr) \
|
||||||
|
((a.tv_sec * 1000000000 + a.tv_nsec) expr(b.tv_sec * 1000000000 + b.tv_nsec))
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int bwlimit_wait(struct bwlimit *bw, size_t nr_bytes)
|
||||||
|
{
|
||||||
|
struct timespec now, end, rq, rm;
|
||||||
|
|
||||||
|
if (bw->bps == 0)
|
||||||
|
return 0; /* no bandwidth limit */
|
||||||
|
|
||||||
|
if (sem_wait(bw->sem) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
if (!timespecisset(bw->wstart)) {
|
||||||
|
bw->wstart = now;
|
||||||
|
timespecmsadd(bw->wstart, bw->win, bw->wend);
|
||||||
|
}
|
||||||
|
|
||||||
|
bw->credit -= nr_bytes;
|
||||||
|
|
||||||
|
if (bw->credit < 0) {
|
||||||
|
/* no more credit on this window. sleep until the end
|
||||||
|
* of this window + additional time for the remaining
|
||||||
|
* bytes. */
|
||||||
|
uint64_t addition = (double)(bw->credit * -1) / (bw->bps / 8);
|
||||||
|
timespecmsadd(bw->wend, addition * 1000, end);
|
||||||
|
if (timespeccmp(end, now, >)) {
|
||||||
|
timespecsub(end, now, rq);
|
||||||
|
while (nanosleep(&rq, &rm) == -1) {
|
||||||
|
if (errno != EINTR)
|
||||||
|
break;
|
||||||
|
rq = rm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bw->credit = bw->amt;
|
||||||
|
timespeczerorize(bw->wstart);
|
||||||
|
}
|
||||||
|
|
||||||
|
sem_post(bw->sem);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
28
src/bwlimit.h
Normal file
28
src/bwlimit.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-3.0-only */
|
||||||
|
#ifndef _BWLIMIT_H_
|
||||||
|
#define _BWLIMIT_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
struct bwlimit {
|
||||||
|
sem_t *sem; /* semaphore */
|
||||||
|
uint64_t bps; /* limit bit-rate (bps) */
|
||||||
|
uint64_t win; /* window size (msec) */
|
||||||
|
size_t amt; /* amount of bytes can be sent in a window */
|
||||||
|
|
||||||
|
ssize_t credit; /* remaining bytes can be sent in a window */
|
||||||
|
struct timespec wstart, wend; /* window start time and end time */
|
||||||
|
};
|
||||||
|
|
||||||
|
int bwlimit_init(struct bwlimit *bw, uint64_t bps, uint64_t win);
|
||||||
|
/* if bps is 0, it means that bwlimit is not active. If so,
|
||||||
|
* bwlimit_wait() returns immediately. */
|
||||||
|
|
||||||
|
int bwlimit_wait(struct bwlimit *bw, size_t nr_bytes);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _BWLIMIT_H_ */
|
||||||
27
src/main.c
27
src/main.c
@@ -26,7 +26,8 @@ void usage(bool print_help)
|
|||||||
"\n"
|
"\n"
|
||||||
"Usage: mscp [-46vqDpHdNh] [-n nr_conns] [-m coremask]\n"
|
"Usage: mscp [-46vqDpHdNh] [-n nr_conns] [-m coremask]\n"
|
||||||
" [-u max_startups] [-I interval] [-W checkpoint] [-R checkpoint]\n"
|
" [-u max_startups] [-I interval] [-W checkpoint] [-R checkpoint]\n"
|
||||||
" [-s min_chunk_sz] [-S max_chunk_sz] [-a nr_ahead] [-b buf_sz]\n"
|
" [-s min_chunk_sz] [-S max_chunk_sz] [-a nr_ahead]\n"
|
||||||
|
" [-b buf_sz] [-L limit_bitrate]\n"
|
||||||
" [-l login_name] [-P port] [-F ssh_config] [-i identity_file]\n"
|
" [-l login_name] [-P port] [-F ssh_config] [-i identity_file]\n"
|
||||||
" [-c cipher_spec] [-M hmac_spec] [-C compress] [-g congestion]\n"
|
" [-c cipher_spec] [-M hmac_spec] [-C compress] [-g congestion]\n"
|
||||||
" source ... target\n"
|
" source ... target\n"
|
||||||
@@ -48,6 +49,7 @@ void usage(bool print_help)
|
|||||||
" -S MAX_CHUNK_SIZE max chunk size (default: filesize/nr_conn)\n"
|
" -S MAX_CHUNK_SIZE max chunk size (default: filesize/nr_conn)\n"
|
||||||
" -a NR_AHEAD number of inflight SFTP commands (default: 32)\n"
|
" -a NR_AHEAD number of inflight SFTP commands (default: 32)\n"
|
||||||
" -b BUF_SZ buffer size for i/o and transfer\n"
|
" -b BUF_SZ buffer size for i/o and transfer\n"
|
||||||
|
" -L LIMIT_BITRATE Limit the bitrate, n[KMG] (default: 0, no limit)\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -4 use IPv4\n"
|
" -4 use IPv4\n"
|
||||||
" -6 use IPv6\n"
|
" -6 use IPv6\n"
|
||||||
@@ -266,12 +268,14 @@ int main(int argc, char **argv)
|
|||||||
int direction = 0;
|
int direction = 0;
|
||||||
char *remote = NULL, *checkpoint_save = NULL, *checkpoint_load = NULL;
|
char *remote = NULL, *checkpoint_save = NULL, *checkpoint_load = NULL;
|
||||||
bool dryrun = false, resume = false;
|
bool dryrun = false, resume = false;
|
||||||
|
char *u;
|
||||||
|
size_t mag = 0;
|
||||||
|
|
||||||
memset(&s, 0, sizeof(s));
|
memset(&s, 0, sizeof(s));
|
||||||
memset(&o, 0, sizeof(o));
|
memset(&o, 0, sizeof(o));
|
||||||
o.severity = MSCP_SEVERITY_WARN;
|
o.severity = MSCP_SEVERITY_WARN;
|
||||||
|
|
||||||
#define mscpopts "n:m:u:I:W:R:s:S:a:b:46vqDrl:P:i:F:c:M:C:g:pHdNh"
|
#define mscpopts "n:m:u:I:W:R:s:S:a:b:L:46vqDrl:P:i:F:c:M:C:g:pHdNh"
|
||||||
while ((ch = getopt(argc, argv, mscpopts)) != -1) {
|
while ((ch = getopt(argc, argv, mscpopts)) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'n':
|
case 'n':
|
||||||
@@ -309,6 +313,25 @@ int main(int argc, char **argv)
|
|||||||
case 'b':
|
case 'b':
|
||||||
o.buf_sz = atoi(optarg);
|
o.buf_sz = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'L':
|
||||||
|
u = optarg + (strlen(optarg) - 1);
|
||||||
|
if (*u == 'k' || *u == 'K') {
|
||||||
|
mag = 1000;
|
||||||
|
*u = '\0';
|
||||||
|
} else if (*u == 'm' || *u == 'M') {
|
||||||
|
mag = 1000000;
|
||||||
|
*u = '\0';
|
||||||
|
} else if (*u == 'g' || *u == 'G') {
|
||||||
|
mag = 1000000000;
|
||||||
|
*u = '\0';
|
||||||
|
}
|
||||||
|
o.bitrate = atol(optarg);
|
||||||
|
if (o.bitrate == 0) {
|
||||||
|
pr_err("invalid bitrate: %s", optarg);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
o.bitrate *= mag;
|
||||||
|
break;
|
||||||
case '4':
|
case '4':
|
||||||
s.ai_family = AF_INET;
|
s.ai_family = AF_INET;
|
||||||
break;
|
break;
|
||||||
|
|||||||
15
src/mscp.c
15
src/mscp.c
@@ -17,6 +17,7 @@
|
|||||||
#include <print.h>
|
#include <print.h>
|
||||||
#include <strerrno.h>
|
#include <strerrno.h>
|
||||||
#include <mscp.h>
|
#include <mscp.h>
|
||||||
|
#include <bwlimit.h>
|
||||||
|
|
||||||
#include <openbsd-compat/openbsd-compat.h>
|
#include <openbsd-compat/openbsd-compat.h>
|
||||||
|
|
||||||
@@ -56,6 +57,8 @@ struct mscp {
|
|||||||
#define chunk_pool_is_ready(m) ((m)->chunk_pool_ready)
|
#define chunk_pool_is_ready(m) ((m)->chunk_pool_ready)
|
||||||
#define chunk_pool_set_ready(m, b) ((m)->chunk_pool_ready = b)
|
#define chunk_pool_set_ready(m, b) ((m)->chunk_pool_ready = b)
|
||||||
|
|
||||||
|
struct bwlimit bw; /* bandwidth limit mechanism */
|
||||||
|
|
||||||
struct mscp_thread scan; /* mscp_thread for mscp_scan_thread() */
|
struct mscp_thread scan; /* mscp_thread for mscp_scan_thread() */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -281,6 +284,12 @@ struct mscp *mscp_init(struct mscp_opts *o, struct mscp_ssh_opts *s)
|
|||||||
pr_notice("usable cpu cores:%s", b);
|
pr_notice("usable cpu cores:%s", b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bwlimit_init(&m->bw, o->bitrate, 100) < 0) { /* 100ms window (hardcoded) */
|
||||||
|
priv_set_errv("bwlimit_init: %s", strerrno());
|
||||||
|
goto free_out;
|
||||||
|
}
|
||||||
|
pr_notice("bitrate limit: %lu bps", o->bitrate);
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
|
|
||||||
free_out:
|
free_out:
|
||||||
@@ -522,8 +531,8 @@ int mscp_checkpoint_load(struct mscp *m, const char *pathname)
|
|||||||
|
|
||||||
int mscp_checkpoint_save(struct mscp *m, const char *pathname)
|
int mscp_checkpoint_save(struct mscp *m, const char *pathname)
|
||||||
{
|
{
|
||||||
return checkpoint_save(pathname, m->direction, m->ssh_opts->login_name,
|
return checkpoint_save(pathname, m->direction, m->ssh_opts->login_name, m->remote,
|
||||||
m->remote, m->path_pool, m->chunk_pool);
|
m->path_pool, m->chunk_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *mscp_copy_thread(void *arg);
|
static void *mscp_copy_thread(void *arg);
|
||||||
@@ -712,7 +721,7 @@ void *mscp_copy_thread(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((t->ret = copy_chunk(c, src_sftp, dst_sftp, m->opts->nr_ahead,
|
if ((t->ret = copy_chunk(c, src_sftp, dst_sftp, m->opts->nr_ahead,
|
||||||
m->opts->buf_sz, m->opts->preserve_ts,
|
m->opts->buf_sz, m->opts->preserve_ts, &m->bw,
|
||||||
&t->copied_bytes)) < 0)
|
&t->copied_bytes)) < 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/path.c
21
src/path.c
@@ -348,7 +348,7 @@ static ssize_t read_to_buf(void *ptr, size_t len, void *userdata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int copy_chunk_l2r(struct chunk *c, int fd, sftp_file sf, int nr_ahead, int buf_sz,
|
static int copy_chunk_l2r(struct chunk *c, int fd, sftp_file sf, int nr_ahead, int buf_sz,
|
||||||
size_t *counter)
|
struct bwlimit *bw, size_t *counter)
|
||||||
{
|
{
|
||||||
ssize_t read_bytes, remaind, thrown;
|
ssize_t read_bytes, remaind, thrown;
|
||||||
int idx, ret;
|
int idx, ret;
|
||||||
@@ -371,6 +371,7 @@ static int copy_chunk_l2r(struct chunk *c, int fd, sftp_file sf, int nr_ahead, i
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
thrown -= reqs[idx].len;
|
thrown -= reqs[idx].len;
|
||||||
|
bwlimit_wait(bw, reqs[idx].len);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (idx = 0; remaind > 0; idx = (idx + 1) % nr_ahead) {
|
for (idx = 0; remaind > 0; idx = (idx + 1) % nr_ahead) {
|
||||||
@@ -399,6 +400,7 @@ static int copy_chunk_l2r(struct chunk *c, int fd, sftp_file sf, int nr_ahead, i
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
thrown -= reqs[idx].len;
|
thrown -= reqs[idx].len;
|
||||||
|
bwlimit_wait(bw, reqs[idx].len);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remaind < 0) {
|
if (remaind < 0) {
|
||||||
@@ -412,7 +414,7 @@ static int copy_chunk_l2r(struct chunk *c, int fd, sftp_file sf, int nr_ahead, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int copy_chunk_r2l(struct chunk *c, sftp_file sf, int fd, int nr_ahead, int buf_sz,
|
static int copy_chunk_r2l(struct chunk *c, sftp_file sf, int fd, int nr_ahead, int buf_sz,
|
||||||
size_t *counter)
|
struct bwlimit *bw, size_t *counter)
|
||||||
{
|
{
|
||||||
ssize_t read_bytes, write_bytes, remaind, thrown;
|
ssize_t read_bytes, write_bytes, remaind, thrown;
|
||||||
char buf[buf_sz];
|
char buf[buf_sz];
|
||||||
@@ -436,6 +438,7 @@ static int copy_chunk_r2l(struct chunk *c, sftp_file sf, int fd, int nr_ahead, i
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
thrown -= reqs[idx].len;
|
thrown -= reqs[idx].len;
|
||||||
|
bwlimit_wait(bw, reqs[idx].len);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (idx = 0; remaind > 0; idx = (idx + 1) % nr_ahead) {
|
for (idx = 0; remaind > 0; idx = (idx + 1) % nr_ahead) {
|
||||||
@@ -449,6 +452,7 @@ static int copy_chunk_r2l(struct chunk *c, sftp_file sf, int fd, int nr_ahead, i
|
|||||||
reqs[idx].len = min(thrown, sizeof(buf));
|
reqs[idx].len = min(thrown, sizeof(buf));
|
||||||
reqs[idx].id = sftp_async_read_begin(sf, reqs[idx].len);
|
reqs[idx].id = sftp_async_read_begin(sf, reqs[idx].len);
|
||||||
thrown -= reqs[idx].len;
|
thrown -= reqs[idx].len;
|
||||||
|
bwlimit_wait(bw, reqs[idx].len);
|
||||||
}
|
}
|
||||||
|
|
||||||
write_bytes = write(fd, buf, read_bytes);
|
write_bytes = write(fd, buf, read_bytes);
|
||||||
@@ -477,19 +481,22 @@ static int copy_chunk_r2l(struct chunk *c, sftp_file sf, int fd, int nr_ahead, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int _copy_chunk(struct chunk *c, mf *s, mf *d, int nr_ahead, int buf_sz,
|
static int _copy_chunk(struct chunk *c, mf *s, mf *d, int nr_ahead, int buf_sz,
|
||||||
size_t *counter)
|
struct bwlimit *bw, size_t *counter)
|
||||||
{
|
{
|
||||||
if (s->local && d->remote) /* local to remote copy */
|
if (s->local && d->remote) /* local to remote copy */
|
||||||
return copy_chunk_l2r(c, s->local, d->remote, nr_ahead, buf_sz, counter);
|
return copy_chunk_l2r(c, s->local, d->remote, nr_ahead, buf_sz, bw,
|
||||||
|
counter);
|
||||||
else if (s->remote && d->local) /* remote to local copy */
|
else if (s->remote && d->local) /* remote to local copy */
|
||||||
return copy_chunk_r2l(c, s->remote, d->local, nr_ahead, buf_sz, counter);
|
return copy_chunk_r2l(c, s->remote, d->local, nr_ahead, buf_sz, bw,
|
||||||
|
counter);
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
return -1; /* not reached */
|
return -1; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
int copy_chunk(struct chunk *c, sftp_session src_sftp, sftp_session dst_sftp,
|
int copy_chunk(struct chunk *c, sftp_session src_sftp, sftp_session dst_sftp,
|
||||||
int nr_ahead, int buf_sz, bool preserve_ts, size_t *counter)
|
int nr_ahead, int buf_sz, bool preserve_ts, struct bwlimit *bw,
|
||||||
|
size_t *counter)
|
||||||
{
|
{
|
||||||
mode_t mode;
|
mode_t mode;
|
||||||
int flags;
|
int flags;
|
||||||
@@ -529,7 +536,7 @@ int copy_chunk(struct chunk *c, sftp_session src_sftp, sftp_session dst_sftp,
|
|||||||
c->state = CHUNK_STATE_COPING;
|
c->state = CHUNK_STATE_COPING;
|
||||||
pr_debug("copy chunk start: %s 0x%lx-0x%lx", c->p->path, c->off, c->off + c->len);
|
pr_debug("copy chunk start: %s 0x%lx-0x%lx", c->p->path, c->off, c->off + c->len);
|
||||||
|
|
||||||
ret = _copy_chunk(c, s, d, nr_ahead, buf_sz, counter);
|
ret = _copy_chunk(c, s, d, nr_ahead, buf_sz, bw, counter);
|
||||||
|
|
||||||
pr_debug("copy chunk done: %s 0x%lx-0x%lx", c->p->path, c->off, c->off + c->len);
|
pr_debug("copy chunk done: %s 0x%lx-0x%lx", c->p->path, c->off, c->off + c->len);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <pool.h>
|
#include <pool.h>
|
||||||
#include <atomic.h>
|
#include <atomic.h>
|
||||||
#include <ssh.h>
|
#include <ssh.h>
|
||||||
|
#include <bwlimit.h>
|
||||||
|
|
||||||
struct path {
|
struct path {
|
||||||
char *path; /* file path */
|
char *path; /* file path */
|
||||||
@@ -66,6 +67,7 @@ void free_path(struct path *p);
|
|||||||
|
|
||||||
/* copy a chunk. either src_sftp or dst_sftp is not null, and another is null */
|
/* copy a chunk. either src_sftp or dst_sftp is not null, and another is null */
|
||||||
int copy_chunk(struct chunk *c, sftp_session src_sftp, sftp_session dst_sftp,
|
int copy_chunk(struct chunk *c, sftp_session src_sftp, sftp_session dst_sftp,
|
||||||
int nr_ahead, int buf_sz, bool preserve_ts, size_t *counter);
|
int nr_ahead, int buf_sz, bool preserve_ts, struct bwlimit *bw,
|
||||||
|
size_t *counter);
|
||||||
|
|
||||||
#endif /* _PATH_H_ */
|
#endif /* _PATH_H_ */
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ test_e2e.py: End-to-End test for mscp executable.
|
|||||||
import platform
|
import platform
|
||||||
import pytest
|
import pytest
|
||||||
import getpass
|
import getpass
|
||||||
|
import datetime
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@@ -356,6 +357,21 @@ def test_dont_make_conns_more_than_chunks(mscp, src_prefix, dst_prefix):
|
|||||||
assert((end - start) < 10)
|
assert((end - start) < 10)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
|
||||||
|
def test_bwlimit(mscp, src_prefix, dst_prefix):
|
||||||
|
"""Copy 100MB file with 100Mbps bitrate, this requires 8 seconds."""
|
||||||
|
src = File("src", size = 100 * 1024 * 1024).make()
|
||||||
|
dst = File("dst")
|
||||||
|
|
||||||
|
start = datetime.datetime.now().timestamp()
|
||||||
|
run2ok([mscp, "-H", "-vvv", "-L", "100m", src_prefix + "src", dst_prefix + "dst"])
|
||||||
|
end = datetime.datetime.now().timestamp()
|
||||||
|
assert check_same_md5sum(src, dst)
|
||||||
|
src.cleanup()
|
||||||
|
dst.cleanup()
|
||||||
|
assert end - start > 7
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
|
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
|
||||||
@pytest.mark.parametrize("src, dst", param_single_copy)
|
@pytest.mark.parametrize("src, dst", param_single_copy)
|
||||||
def test_set_port_ng(mscp, src_prefix, dst_prefix, src, dst):
|
def test_set_port_ng(mscp, src_prefix, dst_prefix, src, dst):
|
||||||
@@ -553,15 +569,15 @@ def test_checkpoint_dump_and_resume(mscp, src_prefix, dst_prefix):
|
|||||||
dst2.cleanup()
|
dst2.cleanup()
|
||||||
os.remove("checkpoint")
|
os.remove("checkpoint")
|
||||||
|
|
||||||
@pytest.mark.parametrize("timeout", [1,2,3])
|
@pytest.mark.parametrize("timeout", [1, 2, 3, 4, 5, 6])
|
||||||
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
|
@pytest.mark.parametrize("src_prefix, dst_prefix", param_remote_prefix)
|
||||||
def test_checkpoint_interrupt_and_resume(mscp, timeout, src_prefix, dst_prefix):
|
def test_checkpoint_interrupt_and_resume(mscp, timeout, src_prefix, dst_prefix):
|
||||||
src1 = File("src1", size = 1024 * 1024 * 1024).make()
|
"""Copy two 100MB files with 200Mbps -> 4 sec + 4 sec """
|
||||||
src2 = File("src2", size = 1024 * 1024 * 1024).make()
|
src1 = File("src1", size = 100 * 1024 * 1024).make()
|
||||||
|
src2 = File("src2", size = 100 * 1024 * 1024).make()
|
||||||
dst1 = File("dst/src1")
|
dst1 = File("dst/src1")
|
||||||
dst2 = File("dst/src2")
|
dst2 = File("dst/src2")
|
||||||
run2ng([mscp, "-H", "-vv", "-W", "checkpoint",
|
run2ng([mscp, "-H", "-vv", "-W", "checkpoint", "-L", "200m",
|
||||||
"-n", 1, "-s", 8192, "-S", 16384,
|
|
||||||
src_prefix + "src1", src_prefix + "src2", dst_prefix + "dst"],
|
src_prefix + "src1", src_prefix + "src2", dst_prefix + "dst"],
|
||||||
timeout = timeout)
|
timeout = timeout)
|
||||||
assert os.path.exists("checkpoint")
|
assert os.path.exists("checkpoint")
|
||||||
@@ -574,3 +590,4 @@ def test_checkpoint_interrupt_and_resume(mscp, timeout, src_prefix, dst_prefix):
|
|||||||
dst1.cleanup()
|
dst1.cleanup()
|
||||||
dst2.cleanup()
|
dst2.cleanup()
|
||||||
os.remove("checkpoint")
|
os.remove("checkpoint")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user