mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
smbget: Adds a rate limiting option --limit-rate in KB/s
This patch implements a very simple rate limiter. It works by pausing the main download loop whenever the bytes transferred are more than what we would get with if it were transferred at the rate set by the user. Please note that this may reduce the blocksize if the limit is too small. Signed-off-by: Vasileios Porpodas <v.porpodas@gmail.com> Reviewed-by: Jeremy Allison <jra@samba.org> Reviewed-by: Andreas Schneider <asn@samba.org> Autobuild-User(master): Jeremy Allison <jra@samba.org> Autobuild-Date(master): Wed Nov 2 22:47:10 UTC 2022 on sn-devel-184
This commit is contained in:
parent
bf446bcf61
commit
b3292b541e
@ -35,6 +35,7 @@
|
||||
<arg choice="opt">-O, --stdout</arg>
|
||||
<arg choice="opt">-u, --update</arg>
|
||||
<arg choice="opt">-e, --encrypt</arg>
|
||||
<arg choice="opt">--limit-rate=INT</arg>
|
||||
<arg choice="opt">-?, --help</arg>
|
||||
<arg choice="opt">--usage</arg>
|
||||
<arg choice="req">smb://host/share/path/to/file</arg>
|
||||
@ -151,6 +152,11 @@
|
||||
<listitem><para>Enable SMB encryption.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>--limit-rate=INT</term>
|
||||
<listitem><para>Limit download rate by this many KB/s.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -269,6 +269,32 @@ test_msdfs_link()
|
||||
return 0
|
||||
}
|
||||
|
||||
# Tests --limit-rate. Getting the testfile (128K in size) with --limit-rate 100
|
||||
# (that is 100KB/s) should take at least 1 sec to complete.
|
||||
test_limit_rate()
|
||||
{
|
||||
clear_download_area
|
||||
echo "$SMBGET -v -a --limit-rate 100 smb://$SERVER_IP/smbget/testfile"
|
||||
time_begin=$(date +%s)
|
||||
$SMBGET -v -a --limit-rate 100 smb://$SERVER_IP/smbget/testfile
|
||||
if [ $? -ne 0 ]; then
|
||||
echo 'ERROR: RC does not match, expected: 0'
|
||||
return 1
|
||||
fi
|
||||
time_end=$(date +%s)
|
||||
cmp --silent $WORKDIR/testfile ./testfile
|
||||
if [ $? -ne 0 ]; then
|
||||
echo 'ERROR: file content does not match'
|
||||
return 1
|
||||
fi
|
||||
if [ $((time_end - time_begin)) -lt 1 ]; then
|
||||
echo 'ERROR: It should take at least 1s to transfer 128KB with rate 100KB/s'
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
create_test_data
|
||||
|
||||
pushd $TMPDIR
|
||||
@ -306,6 +332,10 @@ testit "update" test_update ||
|
||||
|
||||
testit "msdfs" test_msdfs_link ||
|
||||
failed=$((failed + 1))
|
||||
|
||||
testit "limit rate" test_limit_rate ||
|
||||
failed=$((failed + 1))
|
||||
|
||||
clear_download_area
|
||||
|
||||
popd # TMPDIR
|
||||
|
@ -56,6 +56,7 @@ struct opt {
|
||||
bool send_stdout;
|
||||
bool update;
|
||||
int debuglevel;
|
||||
unsigned limit_rate;
|
||||
};
|
||||
static struct opt opt = { .blocksize = SMB_DEFAULT_BLOCKSIZE };
|
||||
|
||||
@ -355,6 +356,10 @@ static bool smb_download_file(const char *base, const char *name,
|
||||
off_t offset_download = 0, offset_check = 0, curpos = 0,
|
||||
start_offset = 0;
|
||||
struct stat localstat, remotestat;
|
||||
clock_t start_of_bucket_ticks = 0;
|
||||
size_t bytes_in_bucket = 0;
|
||||
size_t bucket_size = 0;
|
||||
clock_t ticks_to_fill_bucket = 0;
|
||||
|
||||
snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
|
||||
(*base && *name && name[0] != '/' &&
|
||||
@ -576,6 +581,44 @@ static bool smb_download_file(const char *base, const char *name,
|
||||
offset_check = 0;
|
||||
}
|
||||
|
||||
/* We implement rate limiting by filling up a bucket with bytes and
|
||||
* checking, once the bucket is filled, if it was filled too fast.
|
||||
* If so, we sleep for some time to get an average transfer rate that
|
||||
* equals to the one set by the user.
|
||||
*
|
||||
* The bucket size directly affects the traffic characteristics.
|
||||
* The smaller the bucket the more frequent the pause/resume cycle.
|
||||
* A large bucket can result in burst of high speed traffic and large
|
||||
* pauses. A cycle of 100ms looks like a good value. This value (in
|
||||
* ticks) is held in `ticks_to_fill_bucket`. The `bucket_size` is
|
||||
* calculated as:
|
||||
* `limit_rate * 1024 * / (CLOCKS_PER_SEC / ticks_to_fill_bucket)`
|
||||
*
|
||||
* After selecting the bucket size we also need to check the blocksize
|
||||
* of the transfer, since this is the minimum unit of traffic that we
|
||||
* can observe. Achieving a ~10% precision requires a blocksize with a
|
||||
* maximum size of `bucket_size / 10`.
|
||||
*/
|
||||
if (opt.limit_rate > 0) {
|
||||
unsigned max_block_size;
|
||||
/* This is the time that the bucket should take to fill. */
|
||||
ticks_to_fill_bucket = 100 /*ms*/ * CLOCKS_PER_SEC / 1000;
|
||||
/* This is the size of the bucket in bytes.
|
||||
* If we fill the bucket too quickly we should pause */
|
||||
bucket_size = opt.limit_rate * 1024 / (CLOCKS_PER_SEC / ticks_to_fill_bucket);
|
||||
max_block_size = bucket_size / 10;
|
||||
max_block_size = max_block_size > 0 ? max_block_size : 1;
|
||||
if (opt.blocksize > max_block_size) {
|
||||
if (opt.blocksize != SMB_DEFAULT_BLOCKSIZE) {
|
||||
fprintf(stderr,
|
||||
"Warning: Overriding block size to %d \
|
||||
due to limit-rate", max_block_size);
|
||||
}
|
||||
opt.blocksize = max_block_size;
|
||||
}
|
||||
start_of_bucket_ticks = clock();
|
||||
}
|
||||
|
||||
readbuf = (char *)SMB_MALLOC(opt.blocksize);
|
||||
if (!readbuf) {
|
||||
fprintf(stderr, "Failed to allocate %zu bytes for read "
|
||||
@ -592,7 +635,30 @@ static bool smb_download_file(const char *base, const char *name,
|
||||
ssize_t bytesread;
|
||||
ssize_t byteswritten;
|
||||
|
||||
/* Rate limiting. This pauses the transfer to limit traffic. */
|
||||
if (opt.limit_rate > 0) {
|
||||
if (bytes_in_bucket > bucket_size) {
|
||||
clock_t now_ticks = clock();
|
||||
clock_t diff_ticks = now_ticks
|
||||
- start_of_bucket_ticks;
|
||||
/* Check if the bucket filled up too fast. */
|
||||
if (diff_ticks < ticks_to_fill_bucket) {
|
||||
/* Pause until `ticks_to_fill_bucket` */
|
||||
double sleep_us
|
||||
= (ticks_to_fill_bucket - diff_ticks)
|
||||
* 1000000 / CLOCKS_PER_SEC;
|
||||
usleep(sleep_us);
|
||||
}
|
||||
/* Reset the byte counter and the ticks. */
|
||||
bytes_in_bucket = 0;
|
||||
start_of_bucket_ticks = clock();
|
||||
}
|
||||
}
|
||||
|
||||
bytesread = smbc_read(remotehandle, readbuf, opt.blocksize);
|
||||
if (opt.limit_rate > 0) {
|
||||
bytes_in_bucket += bytesread;
|
||||
}
|
||||
if(bytesread < 0) {
|
||||
fprintf(stderr,
|
||||
"Can't read %zu bytes at offset %jd, file %s\n",
|
||||
@ -902,6 +968,13 @@ int main(int argc, char **argv)
|
||||
.val = 'f',
|
||||
.descrip = "Use specified rc file"
|
||||
},
|
||||
{
|
||||
.longName = "limit-rate",
|
||||
.argInfo = POPT_ARG_INT,
|
||||
.arg = &opt.limit_rate,
|
||||
.val = 'l',
|
||||
.descrip = "Limit download speed to this many KB/s"
|
||||
},
|
||||
|
||||
POPT_TABLEEND
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user