diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
index ac15b7c934..ae1820de0d 100644
--- a/bin/named/bind9.xsl
+++ b/bin/named/bind9.xsl
@@ -931,6 +931,7 @@
Messages Received |
Records Received |
Bytes Received |
+ Transfer Rate (B/s) |
@@ -959,6 +960,7 @@
|
|
|
+ |
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c
index 887f21a1ed..8b1a1cd91c 100644
--- a/bin/named/statschannel.c
+++ b/bin/named/statschannel.c
@@ -1488,6 +1488,7 @@ xfrin_xmlrender(dns_zone_t *zone, void *arg) {
unsigned int nmsg = 0;
unsigned int nrecs = 0;
uint64_t nbytes = 0;
+ uint64_t rate = 0;
statlevel = dns_zone_getstatlevel(zone);
if (statlevel == dns_zonestat_none) {
@@ -1701,7 +1702,7 @@ xfrin_xmlrender(dns_zone_t *zone, void *arg) {
TRY0(xmlTextWriterEndElement(writer));
if (is_running) {
- dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes);
+ dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes, &rate);
}
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nmsg"));
TRY0(xmlTextWriterWriteFormatString(writer, "%u", nmsg));
@@ -1712,6 +1713,9 @@ xfrin_xmlrender(dns_zone_t *zone, void *arg) {
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "nbytes"));
TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, nbytes));
TRY0(xmlTextWriterEndElement(writer));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rate"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64, rate));
+ TRY0(xmlTextWriterEndElement(writer));
TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ixfr"));
if (is_running && is_first_data_received) {
@@ -2559,6 +2563,7 @@ xfrin_jsonrender(dns_zone_t *zone, void *arg) {
unsigned int nmsg = 0;
unsigned int nrecs = 0;
uint64_t nbytes = 0;
+ uint64_t rate = 0;
statlevel = dns_zone_getstatlevel(zone);
if (statlevel == dns_zonestat_none) {
@@ -2756,7 +2761,7 @@ xfrin_jsonrender(dns_zone_t *zone, void *arg) {
}
if (is_running) {
- dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes);
+ dns_xfrin_getstats(xfr, &nmsg, &nrecs, &nbytes, &rate);
}
json_object_object_add(xfrinobj, "nmsg",
json_object_new_int64((int64_t)nmsg));
@@ -2766,6 +2771,10 @@ xfrin_jsonrender(dns_zone_t *zone, void *arg) {
xfrinobj, "nbytes",
json_object_new_int64(nbytes > INT64_MAX ? INT64_MAX
: (int64_t)nbytes));
+ json_object_object_add(xfrinobj, "rate",
+ json_object_new_int64(rate > INT64_MAX
+ ? INT64_MAX
+ : (int64_t)rate));
if (is_running && is_first_data_received) {
json_object_object_add(
diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst
index d541d705d6..1af2777594 100644
--- a/doc/arm/reference.rst
+++ b/doc/arm/reference.rst
@@ -7835,6 +7835,12 @@ Incoming Zone Transfers
64-bit unsigned Integer. This is the number of usable bytes
of DNS data. It does not include transport overhead.
+ ``Transfer Rate (B/s)`` (``rate``)
+ 64 bit unsigned Integer. This is the average zone transfer rate in
+ bytes-per-second during the latest full interval that is configured by the
+ :any:`min-transfer-rate-in` configuration option. If no such interval
+ has passed yet, then the overall average rate is reported instead.
+
.. note::
Depending on the current state of the transfer, some of the
values may be empty or set to ``-`` (meaning "not available").
diff --git a/lib/dns/include/dns/xfrin.h b/lib/dns/include/dns/xfrin.h
index 52c74ec91a..161737abfa 100644
--- a/lib/dns/include/dns/xfrin.h
+++ b/lib/dns/include/dns/xfrin.h
@@ -140,10 +140,13 @@ dns_xfrin_getendserial(dns_xfrin_t *xfr);
void
dns_xfrin_getstats(dns_xfrin_t *xfr, unsigned int *nmsgp, unsigned int *nrecsp,
- uint64_t *nbytesp);
+ uint64_t *nbytesp, uint64_t *ratep);
/*%<
* Get various statistics values of the xfrin object: number of the received
- * messages, number of the received records, number of the received bytes.
+ * messages, number of the received records, number of the received bytes,
+ * and the average transfer rate (in bytes-per-second) during the last full
+ * 'min-transfer-rate-in ' minutes interval. If no such
+ * interval has passed yet, then the overall average rate is reported instead.
*
* Requires:
*\li 'xfr' is a valid dns_xfrin_t.
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
index c6167900c4..a352d93353 100644
--- a/lib/dns/xfrin.c
+++ b/lib/dns/xfrin.c
@@ -154,6 +154,7 @@ struct dns_xfrin {
atomic_uint nrecs; /*%< Number of records recvd */
atomic_uint_fast64_t nbytes; /*%< Number of bytes received */
_Atomic(isc_time_t) start; /*%< Start time of the transfer */
+ atomic_uint_fast64_t rate_bytes_per_second;
_Atomic(dns_transport_type_t) soa_transport_type;
atomic_uint_fast32_t end_serial;
@@ -975,13 +976,21 @@ xfrin_minratecheck(void *arg) {
const uint64_t nbytes = atomic_load_relaxed(&xfr->nbytes);
const uint64_t min = dns_zone_getminxfrratebytesin(xfr->zone);
+ uint64_t rate = nbytes - xfr->nbytes_saved;
- if (nbytes - xfr->nbytes_saved < min) {
+ if (rate < min) {
isc_timer_stop(xfr->min_rate_timer);
xfrin_fail(xfr, ISC_R_TIMEDOUT,
"minimum transfer rate reached");
} else {
xfr->nbytes_saved = nbytes;
+
+ /*
+ * Calculate and store for the statistics channel the transfer
+ * rate in bytes-per-second for the latest interval.
+ */
+ rate /= dns_zone_getminxfrratesecondsin(xfr->zone);
+ atomic_store_relaxed(&xfr->rate_bytes_per_second, rate);
}
}
@@ -1046,13 +1055,29 @@ dns_xfrin_getendserial(dns_xfrin_t *xfr) {
void
dns_xfrin_getstats(dns_xfrin_t *xfr, unsigned int *nmsgp, unsigned int *nrecsp,
- uint64_t *nbytesp) {
+ uint64_t *nbytesp, uint64_t *ratep) {
REQUIRE(VALID_XFRIN(xfr));
REQUIRE(nmsgp != NULL && nrecsp != NULL && nbytesp != NULL);
+ uint64_t rate = atomic_load_relaxed(&xfr->rate_bytes_per_second);
+ if (rate == 0) {
+ /*
+ * Likely the first 'min-transfer-rate-in '
+ * minutes interval hasn't passed yet. Calculate the overall
+ * average transfer rate instead.
+ */
+ isc_time_t now = isc_time_now();
+ isc_time_t start = atomic_load_relaxed(&xfr->start);
+ uint64_t sec = isc_time_microdiff(&now, &start) / US_PER_SEC;
+ if (sec > 0) {
+ rate = atomic_load_relaxed(&xfr->nbytes) / sec;
+ }
+ }
+
SET_IF_NOT_NULL(nmsgp, atomic_load_relaxed(&xfr->nmsg));
SET_IF_NOT_NULL(nrecsp, atomic_load_relaxed(&xfr->nrecs));
SET_IF_NOT_NULL(nbytesp, atomic_load_relaxed(&xfr->nbytes));
+ SET_IF_NOT_NULL(ratep, rate);
}
const isc_sockaddr_t *