Skip to content

Commit

Permalink
linux: move libnl code into separate file
Browse files Browse the repository at this point in the history
Move all the code using libnl functionality into a separate file to ease
modifications.

No functional change.
  • Loading branch information
cgzones committed Apr 5, 2024
1 parent 15b4bc4 commit a782ef3
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 113 deletions.
5 changes: 5 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ linux_platform_sources = \
zfs/ZfsArcMeter.c \
zfs/ZfsCompressedArcMeter.c

if HAVE_DELAYACCT
linux_platform_headers += linux/LibNl.h
linux_platform_sources += linux/LibNl.c
endif

if HTOP_LINUX
AM_LDFLAGS += -rdynamic
myhtopplatheaders = $(linux_platform_headers)
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ case "$enable_delayacct" in
AC_MSG_ERROR([bad value '$enable_delayacct' for --enable-delayacct])
;;
esac
AM_CONDITIONAL([HAVE_DELAYACCT], [test "$enable_delayacct" = yes])


AC_ARG_ENABLE([sensors],
Expand Down
123 changes: 123 additions & 0 deletions linux/LibNl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
htop - linux/LibNl.c
(C) 2024 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "config.h" // IWYU pragma: keep

#ifndef HAVE_DELAYACCT
#error Compiling this file requires HAVE_DELAYACCT
#endif

#include "linux/LibNl.h"

#include <linux/netlink.h>
#include <linux/taskstats.h>

#include <netlink/attr.h>
#include <netlink/handlers.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>


static void initNetlinkSocket(LinuxProcessTable* this) {
this->netlink_socket = nl_socket_alloc();
if (this->netlink_socket == NULL) {
return;
}
if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
return;
}
this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
}

void LibNl_destroyNetlinkSocket(LinuxProcessTable* this) {
if (!this->netlink_socket)
return;

nl_close(this->netlink_socket);
nl_socket_free(this->netlink_socket);
this->netlink_socket = NULL;
}

static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
struct nlmsghdr* nlhdr;
struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
const struct nlattr* nlattr;
struct taskstats stats;
int rem;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;

nlhdr = nlmsg_hdr(nlmsg);

if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
return NL_SKIP;
}

if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats));
assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid);

// The xxx_delay_total values wrap around on overflow.
// (Linux Kernel "Documentation/accounting/taskstats-struct.rst")
unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
#define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0f, 100.0f) : NAN)
lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC

lp->swapin_delay_total = stats.swapin_delay_total;
lp->blkio_delay_total = stats.blkio_delay_total;
lp->cpu_delay_total = stats.cpu_delay_total;
lp->delay_read_time = stats.ac_etime * 1000;
}
return NL_OK;
}

void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) {
struct nl_msg* msg;

if (!this->netlink_socket) {
initNetlinkSocket(this);
if (!this->netlink_socket) {
goto delayacct_failure;
}
}

if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
goto delayacct_failure;
}

if (! (msg = nlmsg_alloc())) {
goto delayacct_failure;
}

if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
nlmsg_free(msg);
}

if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) {
nlmsg_free(msg);
}

if (nl_send_sync(this->netlink_socket, msg) < 0) {
goto delayacct_failure;
}

if (nl_recvmsgs_default(this->netlink_socket) < 0) {
goto delayacct_failure;
}

return;

delayacct_failure:
process->swapin_delay_percent = NAN;
process->blkio_delay_percent = NAN;
process->cpu_delay_percent = NAN;
}
18 changes: 18 additions & 0 deletions linux/LibNl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef HEADER_LibNl
#define HEADER_LibNl
/*
htop - linux/LibNl.h
(C) 2024 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "linux/LinuxProcess.h"
#include "linux/LinuxProcessTable.h"


void LibNl_destroyNetlinkSocket(LinuxProcessTable* this);

void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process);

#endif /* HEADER_LibNl */
119 changes: 6 additions & 113 deletions linux/LinuxProcessTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,6 @@ in the source distribution for its full text.
#include <unistd.h>
#include <sys/stat.h>

#ifdef HAVE_DELAYACCT
#include <linux/netlink.h>
#include <linux/taskstats.h>
#include <netlink/attr.h>
#include <netlink/handlers.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <netlink/socket.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#endif

#include "Compat.h"
#include "Hashtable.h"
#include "Machine.h"
Expand All @@ -55,6 +43,10 @@ in the source distribution for its full text.
#include "linux/LinuxProcess.h"
#include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep

#ifdef HAVE_DELAYACCT
#include "linux/LibNl.h"
#endif

#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
Expand Down Expand Up @@ -187,21 +179,6 @@ static void LinuxProcessTable_initTtyDrivers(LinuxProcessTable* this) {
this->ttyDrivers = ttyDrivers;
}

#ifdef HAVE_DELAYACCT

static void LinuxProcessTable_initNetlinkSocket(LinuxProcessTable* this) {
this->netlink_socket = nl_socket_alloc();
if (this->netlink_socket == NULL) {
return;
}
if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
return;
}
this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
}

#endif

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
LinuxProcessTable* this = xCalloc(1, sizeof(LinuxProcessTable));
Object_setClass(this, Class(ProcessTable));
Expand All @@ -227,10 +204,7 @@ void ProcessTable_delete(Object* cast) {
free(this->ttyDrivers);
}
#ifdef HAVE_DELAYACCT
if (this->netlink_socket) {
nl_close(this->netlink_socket);
nl_socket_free(this->netlink_socket);
}
LibNl_destroyNetlinkSocket(this);
#endif
free(this);
}
Expand Down Expand Up @@ -1010,87 +984,6 @@ static void LinuxProcessTable_readCwd(LinuxProcess* process, openat_arg_t procFd
free_and_xStrdup(&process->super.procCwd, pathBuffer);
}

#ifdef HAVE_DELAYACCT

static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
struct nlmsghdr* nlhdr;
struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
const struct nlattr* nlattr;
struct taskstats stats;
int rem;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;

nlhdr = nlmsg_hdr(nlmsg);

if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
return NL_SKIP;
}

if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats));
assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid);

// The xxx_delay_total values wrap around on overflow.
// (Linux Kernel "Documentation/accounting/taskstats-struct.rst")
unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
#define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0f, 100.0f) : NAN)
lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC

lp->swapin_delay_total = stats.swapin_delay_total;
lp->blkio_delay_total = stats.blkio_delay_total;
lp->cpu_delay_total = stats.cpu_delay_total;
lp->delay_read_time = stats.ac_etime * 1000;
}
return NL_OK;
}

static void LinuxProcessTable_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) {
struct nl_msg* msg;

if (!this->netlink_socket) {
LinuxProcessTable_initNetlinkSocket(this);
if (!this->netlink_socket) {
goto delayacct_failure;
}
}

if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
goto delayacct_failure;
}

if (! (msg = nlmsg_alloc())) {
goto delayacct_failure;
}

if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
nlmsg_free(msg);
}

if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) {
nlmsg_free(msg);
}

if (nl_send_sync(this->netlink_socket, msg) < 0) {
goto delayacct_failure;
}

if (nl_recvmsgs_default(this->netlink_socket) < 0) {
goto delayacct_failure;
}

return;

delayacct_failure:
process->swapin_delay_percent = NAN;
process->blkio_delay_percent = NAN;
process->cpu_delay_percent = NAN;
}

#endif

static bool LinuxProcessTable_readCmdlineFile(Process* process, openat_arg_t procFd) {
char filename[MAX_NAME + 1];
ssize_t amtRead;
Expand Down Expand Up @@ -1593,7 +1486,7 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar

#ifdef HAVE_DELAYACCT
if (ss->flags & PROCESS_FLAG_LINUX_DELAYACCT) {
LinuxProcessTable_readDelayAcctData(this, lp);
LibNl_readDelayAcctData(this, lp);
}
#endif

Expand Down

0 comments on commit a782ef3

Please sign in to comment.