256 lines
7.9 KiB
C++
256 lines
7.9 KiB
C++
/*
|
|
* Copyright (c) 2016 MariaDB Corporation Ab
|
|
*
|
|
* Use of this software is governed by the Business Source License included
|
|
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
|
|
*
|
|
* Change Date: 2022-01-01
|
|
*
|
|
* On the date above, in accordance with the Business Source License, use
|
|
* of this software will be governed by version 2 or later of the General
|
|
* Public License.
|
|
*/
|
|
|
|
/**
|
|
* @file poll.c - Abstraction of the epoll functionality
|
|
*/
|
|
|
|
#include <maxscale/poll.hh>
|
|
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <mysql.h>
|
|
#include <sys/epoll.h>
|
|
|
|
#include <maxscale/alloc.h>
|
|
#include <maxbase/atomic.h>
|
|
#include <maxscale/config.hh>
|
|
#include <maxscale/clock.h>
|
|
#include <maxscale/server.hh>
|
|
#include <maxscale/statistics.hh>
|
|
#include <maxscale/routingworker.hh>
|
|
|
|
#include "internal/poll.hh"
|
|
|
|
using maxbase::Worker;
|
|
using maxscale::RoutingWorker;
|
|
|
|
static int n_threads; /*< Number of threads */
|
|
|
|
/**
|
|
* Initialise the polling system we are using for the gateway.
|
|
*
|
|
* In this case we are using the Linux epoll mechanism
|
|
*/
|
|
void poll_init()
|
|
{
|
|
n_threads = config_threadcount();
|
|
}
|
|
|
|
/**
|
|
* Set the number of non-blocking poll cycles that will be done before
|
|
* a blocking poll will take place. Whenever an event arrives on a thread
|
|
* or the thread sees a pending event to execute it will reset it's
|
|
* poll_spin coutn to zero and will then poll with a 0 timeout until the
|
|
* poll_spin value is greater than the value set here.
|
|
*
|
|
* @param nbpolls Number of non-block polls to perform before blocking
|
|
*/
|
|
void poll_set_nonblocking_polls(unsigned int nbpolls)
|
|
{
|
|
RoutingWorker::set_nonblocking_polls(nbpolls);
|
|
}
|
|
|
|
/**
|
|
* Set the maximum amount of time, in milliseconds, the polling thread
|
|
* will block before it will wake and check the event queue for work
|
|
* that may have been added by another thread.
|
|
*
|
|
* @param maxwait Maximum wait time in milliseconds
|
|
*/
|
|
void poll_set_maxwait(unsigned int maxwait)
|
|
{
|
|
RoutingWorker::set_maxwait(maxwait);
|
|
}
|
|
|
|
/**
|
|
* Display an entry from the spinlock statistics data
|
|
*
|
|
* @param dcb The DCB to print to
|
|
* @param desc Description of the statistic
|
|
* @param value The statistic value
|
|
*/
|
|
static void spin_reporter(void* dcb, char* desc, int value)
|
|
{
|
|
dcb_printf((DCB*)dcb, "\t%-40s %d\n", desc, value);
|
|
}
|
|
|
|
/**
|
|
* Debug routine to print the polling statistics
|
|
*
|
|
* @param dcb DCB to print to
|
|
*/
|
|
void dprintPollStats(DCB* dcb)
|
|
{
|
|
int i;
|
|
|
|
Worker::STATISTICS s = RoutingWorker::get_statistics();
|
|
|
|
dcb_printf(dcb, "\nPoll Statistics.\n\n");
|
|
dcb_printf(dcb, "No. of epoll cycles: %" PRId64 "\n", s.n_polls);
|
|
dcb_printf(dcb, "No. of epoll calls returning events: %" PRId64 "\n", s.n_pollev);
|
|
dcb_printf(dcb, "No. of read events: %" PRId64 "\n", s.n_read);
|
|
dcb_printf(dcb, "No. of write events: %" PRId64 "\n", s.n_write);
|
|
dcb_printf(dcb, "No. of error events: %" PRId64 "\n", s.n_error);
|
|
dcb_printf(dcb, "No. of hangup events: %" PRId64 "\n", s.n_hup);
|
|
dcb_printf(dcb, "No. of accept events: %" PRId64 "\n", s.n_accept);
|
|
dcb_printf(dcb, "Average event queue length: %" PRId64 "\n", s.evq_avg);
|
|
dcb_printf(dcb, "Maximum event queue length: %" PRId64 "\n", s.evq_max);
|
|
|
|
dcb_printf(dcb, "No of poll completions with descriptors\n");
|
|
dcb_printf(dcb, "\tNo. of descriptors\tNo. of poll completions.\n");
|
|
for (i = 0; i < Worker::STATISTICS::MAXNFDS - 1; i++)
|
|
{
|
|
dcb_printf(dcb, "\t%2d\t\t\t%" PRId64 "\n", i + 1, s.n_fds[i]);
|
|
}
|
|
|
|
dcb_printf(dcb,
|
|
"\t>= %d\t\t\t%" PRId64 "\n",
|
|
Worker::STATISTICS::MAXNFDS,
|
|
s.n_fds[Worker::STATISTICS::MAXNFDS - 1]);
|
|
}
|
|
|
|
/**
|
|
* Print the thread status for all the polling threads
|
|
*
|
|
* @param dcb The DCB to send the thread status data
|
|
*/
|
|
void dShowThreads(DCB* dcb)
|
|
{
|
|
dcb_printf(dcb, "Polling Threads.\n\n");
|
|
|
|
dcb_printf(dcb,
|
|
" ID | State | #descriptors (curr) | #descriptors (tot) | Load (1s) | Load (1m) | Load (1h) |\n");
|
|
dcb_printf(dcb,
|
|
"----+------------+---------------------+---------------------+-----------+-----------+-----------+\n");
|
|
for (int i = 0; i < n_threads; i++)
|
|
{
|
|
Worker* worker = RoutingWorker::get(i);
|
|
mxb_assert(worker);
|
|
|
|
const char* state = "Unknown";
|
|
|
|
switch (worker->state())
|
|
{
|
|
case Worker::STOPPED:
|
|
state = "Stopped";
|
|
break;
|
|
|
|
case Worker::POLLING:
|
|
state = "Polling";
|
|
break;
|
|
|
|
case Worker::PROCESSING:
|
|
state = "Processing";
|
|
break;
|
|
|
|
default:
|
|
mxb_assert(!true);
|
|
}
|
|
|
|
uint32_t nCurrent;
|
|
uint64_t nTotal;
|
|
|
|
worker->get_descriptor_counts(&nCurrent, &nTotal);
|
|
|
|
dcb_printf(dcb,
|
|
" %2d | %10s | %19" PRIu32 " | %19" PRIu64 " | %9d | %9d | %9d |\n",
|
|
i,
|
|
state,
|
|
nCurrent,
|
|
nTotal,
|
|
worker->load(Worker::Load::ONE_SECOND),
|
|
worker->load(Worker::Load::ONE_MINUTE),
|
|
worker->load(Worker::Load::ONE_HOUR));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print the event queue statistics
|
|
*
|
|
* @param pdcb The DCB to print the event queue to
|
|
*/
|
|
void dShowEventStats(DCB* pdcb)
|
|
{
|
|
int i;
|
|
|
|
Worker::STATISTICS s = RoutingWorker::get_statistics();
|
|
|
|
dcb_printf(pdcb, "\nEvent statistics.\n");
|
|
dcb_printf(pdcb, "Maximum queue time: %3" PRId64 "00ms\n", s.maxqtime);
|
|
dcb_printf(pdcb, "Maximum execution time: %3" PRId64 "00ms\n", s.maxexectime);
|
|
dcb_printf(pdcb, "Maximum event queue length: %3" PRId64 "\n", s.evq_max);
|
|
dcb_printf(pdcb, "Average event queue length: %3" PRId64 "\n", s.evq_avg);
|
|
dcb_printf(pdcb, "\n");
|
|
dcb_printf(pdcb, " | Number of events\n");
|
|
dcb_printf(pdcb, "Duration | Queued | Executed\n");
|
|
dcb_printf(pdcb, "---------------+------------+-----------\n");
|
|
|
|
dcb_printf(pdcb, " < 100ms | %-10d | %-10d\n", s.qtimes[0], s.exectimes[0]);
|
|
|
|
for (i = 1; i < Worker::STATISTICS::N_QUEUE_TIMES; i++)
|
|
{
|
|
dcb_printf(pdcb, " %2d00 - %2d00ms | %-10d | %-10d\n", i, i + 1, s.qtimes[i], s.exectimes[i]);
|
|
}
|
|
|
|
dcb_printf(pdcb,
|
|
" > %2d00ms | %-10d | %-10d\n",
|
|
Worker::STATISTICS::N_QUEUE_TIMES,
|
|
s.qtimes[Worker::STATISTICS::N_QUEUE_TIMES],
|
|
s.exectimes[Worker::STATISTICS::N_QUEUE_TIMES]);
|
|
}
|
|
|
|
/**
|
|
* Return a poll statistic from the polling subsystem
|
|
*
|
|
* @param what The required statistic
|
|
* @return The value of that statistic
|
|
*/
|
|
int64_t poll_get_stat(POLL_STAT what)
|
|
{
|
|
return RoutingWorker::get_one_statistic(what);
|
|
}
|
|
|
|
/**
|
|
* Return a result set that has the current set of services in it
|
|
*
|
|
* @return A Result set
|
|
*/
|
|
std::unique_ptr<ResultSet> eventTimesGetList()
|
|
{
|
|
std::unique_ptr<ResultSet> set = ResultSet::create({"Duration", "No. Events Queued",
|
|
"No. Events Executed"});
|
|
char buf[40];
|
|
Worker::STATISTICS stats = RoutingWorker::get_statistics();
|
|
|
|
set->add_row({"< 100ms", std::to_string(stats.qtimes[0]), std::to_string(stats.exectimes[0])});
|
|
|
|
for (int i = 1; i < Worker::STATISTICS::N_QUEUE_TIMES - 1; i++)
|
|
{
|
|
snprintf(buf, sizeof(buf), "%2d00 - %2d00ms", i, i + 1);
|
|
set->add_row({buf, std::to_string(stats.qtimes[i]), std::to_string(stats.exectimes[i])});
|
|
}
|
|
|
|
int idx = Worker::STATISTICS::N_QUEUE_TIMES - 1;
|
|
snprintf(buf, sizeof(buf), "> %2d00ms", Worker::STATISTICS::N_QUEUE_TIMES);
|
|
set->add_row({buf, std::to_string(stats.qtimes[idx]), std::to_string(stats.exectimes[idx])});
|
|
|
|
return set;
|
|
}
|