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 *