1 /*
2 * Copyright (C) 2013-2018 Nikos Mavrogiannopoulos
3 * Copyright (C) 2015, 2016 Red Hat, Inc.
4 *
5 * This file is part of ocserv.
6 *
7 * ocserv is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * ocserv is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include <gnutls/gnutls.h>
24 #include <gnutls/dtls.h>
25 #include <math.h>
26 #include <linux/net_tstamp.h>
27 #include <linux/errqueue.h>
28 #include <worker.h>
29 #include <worker-latency.h>
30
31
dtls_pull_latency(gnutls_transport_ptr_t ptr,void * data,size_t size)32 ssize_t dtls_pull_latency(gnutls_transport_ptr_t ptr, void *data, size_t size)
33 {
34 int err;
35 dtls_transport_ptr *p = ptr;
36 p->rx_time.tv_sec = 0;
37 p->rx_time.tv_nsec = 0;
38
39 if (p->msg) {
40 ssize_t need = p->msg->data.len;
41 if (need > size) {
42 need = size;
43 }
44 memcpy(data, p->msg->data.data, need);
45
46 udp_fd_msg__free_unpacked(p->msg, NULL);
47 p->msg = NULL;
48 return need;
49 }
50
51 char controlbuf[1024];
52 struct cmsghdr * cmsg;
53
54 struct iovec io = {
55 .iov_base = data,
56 .iov_len = size,
57 };
58 struct msghdr hdr = {
59 .msg_iov = &io,
60 .msg_iovlen = 1,
61 .msg_control = controlbuf,
62 .msg_controllen = sizeof(controlbuf)
63 };
64 err = recvmsg(p->fd, &hdr, 0);
65 if (err >= 0) {
66 for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL; cmsg = CMSG_NXTHDR(&hdr, cmsg)) {
67 struct scm_timestamping *tss = NULL;
68 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_TIMESTAMPING) {
69 continue;
70 }
71 tss = (struct scm_timestamping *) CMSG_DATA(cmsg);
72 p->rx_time = tss->ts[0];
73 }
74 }
75 return err;
76 }
77
78
send_latency_stats_delta_to_main(worker_st * ws,time_t now)79 void send_latency_stats_delta_to_main(worker_st * ws, time_t now)
80 {
81 LatencyStatsDelta msg = LATENCY_STATS_DELTA__INIT;
82
83 if (ws->latency.sample_set_count == 0) {
84 return;
85 }
86
87 msg.median_delta = ws->latency.median_total;
88 msg.rms_delta = ws->latency.rms_total;
89 msg.sample_count_delta = ws->latency.sample_set_count;
90
91 ws->latency.median_total = 0;
92 ws->latency.rms_total = 0;
93 ws->latency.sample_set_count = 0;
94
95 send_msg_to_main(ws, CMD_LATENCY_STATS_DELTA, &msg,
96 (pack_size_func) latency_stats_delta__get_packed_size,
97 (pack_func) latency_stats_delta__pack);
98
99 ws->latency.last_stats_msg = now;
100 }
101
greater_than(const void * a,const void * b)102 static int greater_than(const void * a, const void * b)
103 {
104 const unsigned long lhs = *(const unsigned long*)a;
105 const unsigned long rhs = *(const unsigned long*)b;
106 return rhs - lhs;
107 }
108
capture_latency_sample(struct worker_st * ws,struct timespec * processing_start_time)109 void capture_latency_sample(struct worker_st* ws, struct timespec *processing_start_time)
110 {
111 struct timespec now;
112 gettime_realtime(&now);
113 unsigned long sample = (unsigned long)timespec_sub_us(&now, processing_start_time);
114 if (ws->latency.next_sample == LATENCY_SAMPLE_SIZE) {
115 unsigned long median;
116 uint64_t total = 0;
117 long double sum_of_squares = 0;
118 uint64_t mean = 0;
119 uint64_t rms = 0;
120 int i;
121
122 ws->latency.next_sample = 0;
123 qsort(ws->latency.samples, LATENCY_SAMPLE_SIZE, sizeof(ws->latency.samples[0]), greater_than);
124 median = ws->latency.samples[LATENCY_SAMPLE_SIZE - 1];
125
126 for (i = 0; i < LATENCY_SAMPLE_SIZE; i ++) {
127 total += ws->latency.samples[i];
128 }
129
130 mean = total / LATENCY_SAMPLE_SIZE;
131 for (i = 0; i < LATENCY_SAMPLE_SIZE; i ++) {
132 long double delta = (long double)ws->latency.samples[i];
133 delta -= mean;
134 sum_of_squares += delta * delta;
135 }
136
137 rms = (uint64_t)sqrt(sum_of_squares / LATENCY_SAMPLE_SIZE);
138
139 (ws->latency.median_total) += median;
140 (ws->latency.rms_total) += rms;
141 (ws->latency.sample_set_count) ++;
142 }
143 ws->latency.samples[(ws->latency.next_sample)++] = sample;
144
145 }
146