[linux-yocto] [PATCH 1/2] netatop: Initial integration
Bruce Ashfield
bruce.ashfield at windriver.com
Sun Apr 15 17:38:57 PDT 2018
On 2018-04-14 11:27 AM, He Zhe wrote:
>
>
> On 2018年04月10日 03:19, Bruce Ashfield wrote:
>> On 2018-04-05 3:08 PM, Bruce Ashfield wrote:
>>> On 2018-03-28 11:25 PM, He Zhe wrote:
>>>> This is a net statistics collector for atop from:
>>>> http://atoptool.nl/netatop.php
>>>> https://atoptool.nl/download/netatop-2.0.tar.gz
>>>>
>>>> Two changes:
>>>> Adjust pathes for netatop.h and netatopversion.h
>>>> Add module_init and module_exit
>>>
>>> Which kernel versions have you tested this on ? Is there a way we could get
>>> a README that describes how to build and test it ?
>>
>> Also, is there a recipe that builds this that I can see/test ?
>
> Sorry for slow reply, I'm on vacation and will handle this asap.
> Is this expected to be built as a module via a recipe? Or as a patch/feature in yocto-kernel-cache?
>
I'd expect something like the perf recipe to build this directly
from the source tree.
That recipe can live wherever, i.e. a github based meta-layer, but
it should be available so that others can see how to build the code.
Bruce
> Zhe
>
>>
>> Bruce
>>
>>>
>>> Bruce
>>>
>>>>
>>>> Signed-off-by: He Zhe <zhe.he at windriver.com>
>>>> ---
>>>> drivers/staging/netatop/netatop.c | 1843 ++++++++++++++++++++++++++++++
>>>> drivers/staging/netatop/netatop.h | 47 +
>>>> drivers/staging/netatop/netatopversion.h | 2 +
>>>> 3 files changed, 1892 insertions(+)
>>>> create mode 100644 drivers/staging/netatop/netatop.c
>>>> create mode 100644 drivers/staging/netatop/netatop.h
>>>> create mode 100644 drivers/staging/netatop/netatopversion.h
>>>>
>>>> diff --git a/drivers/staging/netatop/netatop.c b/drivers/staging/netatop/netatop.c
>>>> new file mode 100644
>>>> index 000000000000..3baa6520416a
>>>> --- /dev/null
>>>> +++ b/drivers/staging/netatop/netatop.c
>>>> @@ -0,0 +1,1843 @@
>>>> +/*
>>>> +** This module uses the netfilter interface to maintain statistics
>>>> +** about the network traffic per task, on level of thread group
>>>> +** and individual thread.
>>>> +**
>>>> +** General setup
>>>> +** -------------
>>>> +** Once the module is active, it is called for every packet that is
>>>> +** transmitted by a local process and every packet that is received
>>>> +** from an interface. Not only the packets that contain the user data
>>>> +** are passed but also the TCP related protocol packets (SYN, ACK, ...).
>>>> +**
>>>> +** When the module discovers a packet for a connection (TCP) or local
>>>> +** port (UDP) that is new, it creates a sockinfo structure. As soon as
>>>> +** possible the sockinfo struct will be connected to a taskinfo struct
>>>> +** that represents the proces or thread that is related to the socket.
>>>> +** However, the task can only be determined when a packet is transmitted,
>>>> +** i.e. the module is called during system call handling in the context
>>>> +** of the transmitting process. At that moment the tgid (process) and
>>>> +** pid (thread) can be obtained from the process administration to
>>>> +** be stored in the module's own taskinfo structs (one for the process,
>>>> +** one for the thread).
>>>> +** For the time that the sockinfo struct can not be related to a taskinfo
>>>> +** struct (e.g. when only packets are received), counters are maintained
>>>> +** temporarily in the sockinfo struct. As soon as a related taskinfo struct
>>>> +** is discovered when the task transmits, counters will be maintained in
>>>> +** the taskinfo struct itself.
>>>> +** When packets are only received for a socket (e.g. another machine is
>>>> +** sending UDP packets to the local machine) while the local task
>>>> +** never responds, no match to a process can be made and the packets
>>>> +** remain unidentified by the netatop module. At least one packet should
>>>> +** have been sent by a local process to be able to match packets for such
>>>> +** socket.
>>>> +** In the file /proc/netatop counters can be found that show the total
>>>> +** number of packets sent/received and how many of these packets were
>>>> +** unidentified (i.e. not accounted to a process/thread).
>>>> +**
>>>> +** Garbage collection
>>>> +** ------------------
>>>> +** The module uses a garbage collector to cleanup the unused sockinfo
>>>> +** structs if connections do not exist any more (TCP) or have not been
>>>> +** used for some time (TCP/UDP).
>>>> +** Furthermore, the garbage collector checks if the taskinfo structs
>>>> +** still represent existing processes or threads. If not, the taskinfo struct
>>>> +** is destroyed (in case of a thread) or it is moved to a separate list of
>>>> +** finished processes (in case of a process). Analysis programs can read
>>>> +** the taskinfo of such finished process. When the taskinfo of a finished
>>>> +** process is not read within 15 seconds, the taskinfo will be destroyed.
>>>> +**
>>>> +** A garbage collector cycle can be triggered by issueing a getsockopt
>>>> +** call from an analysis program (e.g. atop). Apart from that, a time-based
>>>> +** garbage collector cycle is issued anyhow every 15 seconds by the
>>>> +** knetatop kernel thread.
>>>> +**
>>>> +** Interface with user mode
>>>> +** ------------------------
>>>> +** Programs can open an IP socket and use the getsockopt() system call
>>>> +** to issue commands to this module. With the command ATOP_GETCNT_TGID
>>>> +** the current counters can be obtained on process level (thread group)
>>>> +** and with the command ATOP_GETCNT_PID the counters on thread level.
>>>> +** For both commands, the tgid/pid has to be passed of the required thread
>>>> +** (group). When the required thread (group) does not exist, an errno ESRCH
>>>> +** is given.
>>>> +**
>>>> +** The command ATOP_GETCNT_EXIT can be issued to obtain the counters of
>>>> +** an exited process. As stated above, such command has to be issued
>>>> +** within 15 seconds after a process has been declared 'finished' by
>>>> +** the garbage collector. Whenever this command is issued and no exited
>>>> +** process is in the exitlist, the requesting process is blocked until
>>>> +** an exited process is available.
>>>> +**
>>>> +** The command NETATOP_FORCE_GC activates the garbage collector of the
>>>> +** netatop module to determine if sockinfo's of old connections/ports
>>>> +** can be destroyed and if taskinfo's of exited processes can be
>>>> +** The command NETATOP_EMPTY_EXIT can be issued to wait until the exitlist
>>>> +** with the taskinfo's of exited processes is empty.
>>>> +** ----------------------------------------------------------------------
>>>> +** Copyright (C) 2012 Gerlof Langeveld (gerlof.langeveld at atoptool.nl)
>>>> +**
>>>> +** This program is free software; you can redistribute it and/or modify
>>>> +** it under the terms of the GNU General Public License version 2 as
>>>> +** published by the Free Software Foundation.
>>>> +*/
>>>> +#include <linux/init.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/netfilter.h>
>>>> +#include <linux/netfilter_ipv4.h>
>>>> +#include <linux/sched.h>
>>>> +#include <linux/skbuff.h>
>>>> +#include <linux/types.h>
>>>> +#include <linux/file.h>
>>>> +#include <linux/kthread.h>
>>>> +#include <linux/seq_file.h>
>>>> +#include <linux/version.h>
>>>> +#include <net/sock.h>
>>>> +#include <linux/ip.h>
>>>> +#include <linux/tcp.h>
>>>> +#include <linux/udp.h>
>>>> +#include <linux/proc_fs.h>
>>>> +#include <net/ip.h>
>>>> +#include <net/tcp.h>
>>>> +#include <net/udp.h>
>>>> +
>>>> +#include "netatop.h"
>>>> +#include "netatopversion.h"
>>>> +
>>>> +MODULE_LICENSE("GPL");
>>>> +MODULE_AUTHOR("Gerlof Langeveld <gerlof.langeveld at atoptool.nl>");
>>>> +MODULE_DESCRIPTION("Per-task network statistics");
>>>> +MODULE_VERSION(NETATOPVERSION);
>>>> +
>>>> +#define GCINTERVAL (HZ*15) // interval garbage collector (jiffies)
>>>> +#define GCMAXUDP (HZ*16) // max inactivity for UDP (jiffies)
>>>> +#define GCMAXTCP (HZ*1800) // max inactivity for TCP (jiffies)
>>>> +#define GCMAXUNREF (HZ*60) // max time without taskref (jiffies)
>>>> +
>>>> +#define SILIMIT (4096*1024) // maximum memory for sockinfo structs
>>>> +#define TILIMIT (2048*1024) // maximum memory for taskinfo structs
>>>> +
>>>> +#define NF_IP_PRE_ROUTING 0
>>>> +#define NF_IP_LOCAL_IN 1
>>>> +#define NF_IP_FORWARD 2
>>>> +#define NF_IP_LOCAL_OUT 3
>>>> +#define NF_IP_POST_ROUTING 4
>>>> +
>>>> +/*
>>>> +** struct that maintains statistics about the network
>>>> +** traffic caused per thread or thread group
>>>> +*/
>>>> +struct chainer {
>>>> + void *next;
>>>> + void *prev;
>>>> +};
>>>> +
>>>> +struct taskinfobucket;
>>>> +
>>>> +struct taskinfo {
>>>> + struct chainer ch;
>>>> +
>>>> + pid_t id; // tgid or pid
>>>> + char type; // 'g' (thread group) or
>>>> + // 't' (thread)
>>>> + unsigned char state; // see below
>>>> + char command[COMLEN];
>>>> + unsigned long btime; // start time of process
>>>> + unsigned long long exittime; // time inserted in exitlist
>>>> +
>>>> + struct taskcount tc;
>>>> +};
>>>> +
>>>> +static struct kmem_cache *ticache; // taskinfo cache
>>>> +
>>>> +// state values above
>>>> +#define CHECKED 1 // verified that task still exists
>>>> +#define INDELETE 2 // task exited but still in hash list
>>>> +#define FINISHED 3 // task on exit list
>>>> +
>>>> +/*
>>>> +** hash tables to find a particular thread group or thread
>>>> +*/
>>>> +#define TBUCKS 1024 // must be multiple of 2!
>>>> +#define THASH(x, t) (((x)+t)&(TBUCKS-1))
>>>> +
>>>> +struct taskinfobucket {
>>>> + struct chainer ch;
>>>> + spinlock_t lock;
>>>> +} thash[TBUCKS];
>>>> +
>>>> +static unsigned long nrt; // current number of taskinfo allocated
>>>> +static unsigned long nrt_ovf; // no taskinfo allocated due to overflow
>>>> +static DEFINE_SPINLOCK(nrtlock);
>>>> +
>>>> +
>>>> +static struct taskinfo *exithead; // linked list of exited processes
>>>> +static struct taskinfo *exittail;
>>>> +static DEFINE_SPINLOCK(exitlock);
>>>> +
>>>> +static DECLARE_WAIT_QUEUE_HEAD(exitlist_filled);
>>>> +static DECLARE_WAIT_QUEUE_HEAD(exitlist_empty);
>>>> +
>>>> +static unsigned long nre; // current number of taskinfo on exitlist
>>>> +
>>>> +/*
>>>> +** structs that uniquely identify a TCP connection (host endian format)
>>>> +*/
>>>> +struct tcpv4_ident {
>>>> + uint32_t laddr; /* local IP address */
>>>> + uint32_t raddr; /* remote IP address */
>>>> + uint16_t lport; /* local port number */
>>>> + uint16_t rport; /* remote port number */
>>>> +};
>>>> +
>>>> +struct tcpv6_ident {
>>>> + struct in6_addr laddr; /* local IP address */
>>>> + struct in6_addr raddr; /* remote IP address */
>>>> + uint16_t lport; /* local port number */
>>>> + uint16_t rport; /* remote port number */
>>>> +};
>>>> +
>>>> +/*
>>>> +** struct to maintain the reference from a socket
>>>> +** to a thread and thread-group
>>>> +*/
>>>> +struct sockinfo {
>>>> + struct chainer ch;
>>>> +
>>>> + unsigned char last_state; // last known state of socket
>>>> + uint8_t proto; // protocol
>>>> +
>>>> + union keydef {
>>>> + uint16_t udp; // UDP ident (only portnumber)
>>>> + struct tcpv4_ident tcp4; // TCP connection ident IPv4
>>>> + struct tcpv6_ident tcp6; // TCP connection ident IPv6
>>>> + } key;
>>>> +
>>>> + struct taskinfo *tgp; // ref to thread group
>>>> + struct taskinfo *thp; // ref to thread (or NULL)
>>>> +
>>>> + short tgh; // hash number of thread group
>>>> + short thh; // hash number of thread
>>>> +
>>>> + unsigned int sndpacks; // temporary counters in case
>>>> + unsigned int rcvpacks; // known yet
>>>> + unsigned long sndbytes; // no relation to process is
>>>> + unsigned long rcvbytes;
>>>> +
>>>> + unsigned long long lastact; // last updated (jiffies)
>>>> +};
>>>> +
>>>> +static struct kmem_cache *sicache; // sockinfo cache
>>>> +
>>>> +/*
>>>> +** hash table to find a socket reference
>>>> +*/
>>>> +#define SBUCKS 1024 // must be multiple of 2!
>>>> +#define SHASHTCP4(x) (((x).raddr+(x).lport+(x).rport)&(SBUCKS-1))
>>>> +#define SHASHUDP(x) ((x)&(SBUCKS-1))
>>>> +
>>>> +struct {
>>>> + struct chainer ch;
>>>> + spinlock_t lock;
>>>> +} shash[SBUCKS];
>>>> +
>>>> +static unsigned long nrs; // current number sockinfo allocated
>>>> +static unsigned long nrs_ovf; // no sockinfo allocated due to overflow
>>>> +static DEFINE_SPINLOCK(nrslock);
>>>> +
>>>> +/*
>>>> +** various static counters
>>>> +*/
>>>> +static unsigned long icmpsndbytes;
>>>> +static unsigned long icmpsndpacks;
>>>> +static unsigned long icmprcvbytes;
>>>> +static unsigned long icmprcvpacks;
>>>> +
>>>> +static unsigned long tcpsndpacks;
>>>> +static unsigned long tcprcvpacks;
>>>> +static unsigned long udpsndpacks;
>>>> +static unsigned long udprcvpacks;
>>>> +static unsigned long unidentudpsndpacks;
>>>> +static unsigned long unidentudprcvpacks;
>>>> +static unsigned long unidenttcpsndpacks;
>>>> +static unsigned long unidenttcprcvpacks;
>>>> +
>>>> +static unsigned long unknownproto;
>>>> +
>>>> +static DEFINE_MUTEX(gclock);
>>>> +static unsigned long long gclast; // last garbage collection (jiffies)
>>>> +
>>>> +static struct task_struct *knetatop_task;
>>>> +
>>>> +static struct timespec boottime;
>>>> +
>>>> +/*
>>>> +** function prototypes
>>>> +*/
>>>> +static void analyze_tcpv4_packet(struct sk_buff *,
>>>> + const struct net_device *, int, char,
>>>> + struct iphdr *, void *);
>>>> +
>>>> +static void analyze_udp_packet(struct sk_buff *,
>>>> + const struct net_device *, int, char,
>>>> + struct iphdr *, void *);
>>>> +
>>>> +static int sock2task(char, struct sockinfo *,
>>>> + struct taskinfo **, short *,
>>>> + struct sk_buff *, const struct net_device *,
>>>> + int, char);
>>>> +
>>>> +static void update_taskcounters(struct sk_buff *,
>>>> + const struct net_device *,
>>>> + struct taskinfo *, char);
>>>> +
>>>> +static void update_sockcounters(struct sk_buff *,
>>>> + const struct net_device *,
>>>> + struct sockinfo *, char);
>>>> +
>>>> +static void sock2task_sync(struct sk_buff *,
>>>> + struct sockinfo *, struct taskinfo *);
>>>> +
>>>> +static void register_unident(struct sockinfo *);
>>>> +
>>>> +static int calc_reallen(struct sk_buff *,
>>>> + const struct net_device *);
>>>> +
>>>> +static void get_tcpv4_ident(struct iphdr *, void *,
>>>> + char, union keydef *);
>>>> +
>>>> +static struct sockinfo *find_sockinfo(int, union keydef *, int, int);
>>>> +static struct sockinfo *make_sockinfo(int, union keydef *, int, int);
>>>> +
>>>> +static void wipesockinfo(void);
>>>> +static void wipetaskinfo(void);
>>>> +static void wipetaskexit(void);
>>>> +
>>>> +static void garbage_collector(void);
>>>> +static void gctaskexit(void);
>>>> +static void gcsockinfo(void);
>>>> +static void gctaskinfo(void);
>>>> +
>>>> +static void move_taskinfo(struct taskinfo *);
>>>> +static void delete_taskinfo(struct taskinfo *);
>>>> +static void delete_sockinfo(struct sockinfo *);
>>>> +
>>>> +static struct taskinfo *get_taskinfo(pid_t, char);
>>>> +
>>>> +static int getsockopt(struct sock *, int, void *, int *);
>>>> +
>>>> +static int netatop_open(struct inode *inode, struct file *file);
>>>> +
>>>> +/*
>>>> +** hook definitions
>>>> +*/
>>>> +static struct nf_hook_ops hookin_ipv4;
>>>> +static struct nf_hook_ops hookout_ipv4;
>>>> +
>>>> +/*
>>>> +** getsockopt definitions for communication with user space
>>>> +*/
>>>> +static struct nf_sockopt_ops sockopts = {
>>>> + .pf = PF_INET,
>>>> + .get_optmin = NETATOP_BASE_CTL,
>>>> + .get_optmax = NETATOP_BASE_CTL+6,
>>>> + .get = getsockopt,
>>>> + .owner = THIS_MODULE,
>>>> +};
>>>> +
>>>> +static struct file_operations netatop_proc_fops = {
>>>> + .open = netatop_open,
>>>> + .read = seq_read,
>>>> + .llseek = seq_lseek,
>>>> + .release = single_release,
>>>> + .owner = THIS_MODULE,
>>>> +};
>>>> +
>>>> +
>>>> +/*
>>>> +** hook function to be called for every incoming local packet
>>>> +*/
>>>> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
>>>> +# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
>>>> +# define HOOK_ARG_TYPE void *priv
>>>> +# else
>>>> +# define HOOK_ARG_TYPE const struct nf_hook_ops *ops
>>>> +# endif
>>>> +#else
>>>> +# define HOOK_ARG_TYPE unsigned int hooknum
>>>> +#endif
>>>> +
>>>> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
>>>> +#define HOOK_STATE_ARGS const struct nf_hook_state *state
>>>> +
>>>> +#define DEV_IN state->in
>>>> +#define DEV_OUT state->out
>>>> +#else
>>>> +# ifndef __GENKSYMS__
>>>> +# define HOOK_STATE_ARGS const struct net_device *in, \
>>>> + const struct net_device *out, \
>>>> + const struct nf_hook_state *state
>>>> +# else
>>>> +# define HOOK_STATE_ARGS const struct net_device *in, \
>>>> + const struct net_device *out, \
>>>> + int (*okfn)(struct sk_buff *)
>>>> +# endif
>>>> +#define DEV_IN in
>>>> +#define DEV_OUT out
>>>> +#endif
>>>> +
>>>> +
>>>> +static unsigned int
>>>> +ipv4_hookin(HOOK_ARG_TYPE, struct sk_buff *skb, HOOK_STATE_ARGS)
>>>> +{
>>>> + struct iphdr *iph;
>>>> + void *trh;
>>>> +
>>>> + if (skb == NULL) // useless socket buffer?
>>>> + return NF_ACCEPT;
>>>> +
>>>> + /*
>>>> + ** get pointer to IP header and transport header
>>>> + */
>>>> + iph = (struct iphdr *)skb_network_header(skb);
>>>> + trh = ((char *)iph + (iph->ihl * 4));
>>>> +
>>>> + /*
>>>> + ** react on protocol number
>>>> + */
>>>> + switch (iph->protocol) {
>>>> + case IPPROTO_TCP:
>>>> + tcprcvpacks++;
>>>> + analyze_tcpv4_packet(skb, DEV_IN, 0, 'i', iph, trh);
>>>> + break;
>>>> +
>>>> + case IPPROTO_UDP:
>>>> + udprcvpacks++;
>>>> + analyze_udp_packet(skb, DEV_IN, 0, 'i', iph, trh);
>>>> + break;
>>>> +
>>>> + case IPPROTO_ICMP:
>>>> + icmprcvpacks++;
>>>> + icmprcvbytes += skb->len + DEV_IN->hard_header_len + 4;
>>>> + break;
>>>> +
>>>> + default:
>>>> + unknownproto++;
>>>> + }
>>>> +
>>>> + // accept every packet after stats gathering
>>>> + return NF_ACCEPT;
>>>> +}
>>>> +
>>>> +/*
>>>> +** hook function to be called for every outgoing local packet
>>>> +*/
>>>> +static unsigned int
>>>> +ipv4_hookout(HOOK_ARG_TYPE, struct sk_buff *skb, HOOK_STATE_ARGS)
>>>> +{
>>>> + int in_syscall = !in_interrupt();
>>>> + struct iphdr *iph;
>>>> + void *trh;
>>>> +
>>>> + if (skb == NULL) // useless socket buffer?
>>>> + return NF_ACCEPT;
>>>> +
>>>> + /*
>>>> + ** get pointer to IP header and transport header
>>>> + */
>>>> + iph = (struct iphdr *)skb_network_header(skb);
>>>> + trh = skb_transport_header(skb);
>>>> +
>>>> + /*
>>>> + ** react on protocol number
>>>> + */
>>>> + switch (iph->protocol) {
>>>> + case IPPROTO_TCP:
>>>> + tcpsndpacks++;
>>>> + analyze_tcpv4_packet(skb, DEV_OUT, in_syscall, 'o', iph, trh);
>>>> + break;
>>>> +
>>>> + case IPPROTO_UDP:
>>>> + udpsndpacks++;
>>>> + analyze_udp_packet(skb, DEV_OUT, in_syscall, 'o', iph, trh);
>>>> + break;
>>>> +
>>>> + case IPPROTO_ICMP:
>>>> + icmpsndpacks++;
>>>> + icmpsndbytes += skb->len + DEV_OUT->hard_header_len + 4;
>>>> + break;
>>>> +
>>>> + default:
>>>> + unknownproto++;
>>>> + }
>>>> +
>>>> + // accept every packet after stats gathering
>>>> + return NF_ACCEPT;
>>>> +}
>>>> +
>>>> +/*
>>>> +** generic function (for input and output) to analyze the current packet
>>>> +*/
>>>> +static void
>>>> +analyze_tcpv4_packet(struct sk_buff *skb,
>>>> + const struct net_device *ndev, // interface description
>>>> + int in_syscall, // called during system call?
>>>> + char direction, // incoming ('i') or outgoing ('o')
>>>> + struct iphdr *iph, void *trh)
>>>> +{
>>>> + union keydef key;
>>>> + struct sockinfo *sip;
>>>> + int bs; // hash bucket for sockinfo
>>>> + unsigned long sflags;
>>>> +
>>>> + /*
>>>> + ** determine tcpv4_ident that identifies this TCP packet
>>>> + ** and calculate hash bucket in sockinfo hash
>>>> + */
>>>> + get_tcpv4_ident(iph, trh, direction, &key);
>>>> +
>>>> + /*
>>>> + ** check if we have seen this tcpv4_ident before with a
>>>> + ** corresponding thread and thread group
>>>> + */
>>>> + bs = SHASHTCP4(key.tcp4);
>>>> +
>>>> + spin_lock_irqsave(&shash[bs].lock, sflags);
>>>> +
>>>> + if ( (sip = find_sockinfo(IPPROTO_TCP, &key, sizeof key.tcp4, bs))
>>>> + == NULL) {
>>>> + // no sockinfo yet: create one
>>>> + if ( (sip = make_sockinfo(IPPROTO_TCP, &key,
>>>> + sizeof key.tcp4, bs)) == NULL) {
>>>> + if (direction == 'i')
>>>> + unidenttcprcvpacks++;
>>>> + else
>>>> + unidenttcpsndpacks++;
>>>> + goto unlocks;
>>>> + }
>>>> + }
>>>> +
>>>> + if (skb->sk)
>>>> + sip->last_state = skb->sk->sk_state;
>>>> +
>>>> + /*
>>>> + ** if needed (re)connect the sockinfo to a taskinfo and update
>>>> + ** the counters
>>>> + */
>>>> +
>>>> + // connect to thread group and update
>>>> + if (sock2task('g', sip, &sip->tgp, &sip->tgh,
>>>> + skb, ndev, in_syscall, direction)) {
>>>> + // connect to thread and update
>>>> + (void) sock2task('t', sip, &sip->thp, &sip->thh,
>>>> + skb, ndev, in_syscall, direction);
>>>> + }
>>>> +
>>>> +unlocks:
>>>> + spin_unlock_irqrestore(&shash[bs].lock, sflags);
>>>> +}
>>>> +
>>>> +
>>>> +/*
>>>> +** generic function (for input and output) to analyze the current packet
>>>> +*/
>>>> +static void
>>>> +analyze_udp_packet(struct sk_buff *skb,
>>>> + const struct net_device *ndev, // interface description
>>>> + int in_syscall, // called during system call?
>>>> + char direction, // incoming ('i') or outgoing ('o')
>>>> + struct iphdr *iph, void *trh)
>>>> +{
>>>> + struct udphdr *udph = (struct udphdr *)trh;
>>>> + uint16_t udplocal = (direction == 'i' ?
>>>> + ntohs(udph->dest) : ntohs(udph->source));
>>>> + int bs; // hash bucket for sockinfo
>>>> +
>>>> + union keydef key;
>>>> + struct sockinfo *sip;
>>>> + unsigned long sflags;
>>>> +
>>>> + /*
>>>> + ** check if we have seen this local UDP port before with a
>>>> + ** corresponding thread and thread group
>>>> + */
>>>> + key.udp = udplocal;
>>>> + bs = SHASHUDP(udplocal);
>>>> +
>>>> + spin_lock_irqsave(&shash[bs].lock, sflags);
>>>> +
>>>> + if ( (sip = find_sockinfo(IPPROTO_UDP, &key, sizeof key.udp, bs))
>>>> + == NULL) {
>>>> + // no sockinfo yet: create one
>>>> + if ( (sip = make_sockinfo(IPPROTO_UDP, &key,
>>>> + sizeof key.udp, bs)) == NULL) {
>>>> + if (direction == 'i')
>>>> + unidentudprcvpacks++;
>>>> + else
>>>> + unidentudpsndpacks++;
>>>> + goto unlocks;
>>>> + }
>>>> + }
>>>> +
>>>> + /*
>>>> + ** if needed (re)connect the sockinfo to a taskinfo and update
>>>> + ** the counters
>>>> + */
>>>> +
>>>> + // connect to thread group and update
>>>> + if (sock2task('g', sip, &sip->tgp, &sip->tgh,
>>>> + skb, ndev, in_syscall, direction)) {
>>>> + // connect to thread and update
>>>> + (void) sock2task('t', sip, &sip->thp, &sip->thh,
>>>> + skb, ndev, in_syscall, direction);
>>>> + }
>>>> +
>>>> +unlocks:
>>>> + spin_unlock_irqrestore(&shash[bs].lock, sflags);
>>>> +}
>>>> +
>>>> +/*
>>>> +** connect the sockinfo to the correct taskinfo and update the counters
>>>> +*/
>>>> +static int
>>>> +sock2task(char idtype, struct sockinfo *sip, struct taskinfo **tipp,
>>>> + short *hash, struct sk_buff *skb, const struct net_device *ndev,
>>>> + int in_syscall, char direction)
>>>> +{
>>>> + pid_t curid;
>>>> + unsigned long tflags;
>>>> +
>>>> + if (*tipp == NULL) {
>>>> + /*
>>>> + ** no taskinfo connected yet for this reference from
>>>> + ** sockinfo; to connect to a taskinfo, we must
>>>> + ** be in system call handling now --> verify
>>>> + */
>>>> + if (!in_syscall) {
>>>> + if (idtype == 'g')
>>>> + update_sockcounters(skb, ndev, sip, direction);
>>>> +
>>>> + return 0; // failed
>>>> + }
>>>> +
>>>> + /*
>>>> + ** try to find existing taskinfo or create new taskinfo
>>>> + */
>>>> + curid = (idtype == 'g' ? current->tgid : current->pid);
>>>> +
>>>> + *hash = THASH(curid, idtype); // calc hashQ
>>>> +
>>>> + spin_lock_irqsave(&thash[*hash].lock, tflags);
>>>> +
>>>> + if ( (*tipp = get_taskinfo(curid, idtype)) == NULL) {
>>>> + /*
>>>> + ** not possible to connect
>>>> + */
>>>> + spin_unlock_irqrestore(&thash[*hash].lock, tflags);
>>>> +
>>>> + if (idtype == 'g')
>>>> + update_sockcounters(skb, ndev, sip, direction);
>>>> +
>>>> + return 0; // failed
>>>> + }
>>>> +
>>>> + /*
>>>> + ** new connection made:
>>>> + ** update task counters with sock counters
>>>> + */
>>>> + sock2task_sync(skb, sip, *tipp);
>>>> + } else {
>>>> + /*
>>>> + ** already related to thread group or thread
>>>> + ** lock existing task
>>>> + */
>>>> + spin_lock_irqsave(&thash[*hash].lock, tflags);
>>>> +
>>>> + /*
>>>> + ** check if socket has been passed to another process in the
>>>> + ** meantime, like programs as xinetd use to do
>>>> + ** if so, connect sockinfo to the new task
>>>> + */
>>>> + if (in_syscall) {
>>>> + curid = (idtype == 'g' ? current->tgid : current->pid);
>>>> +
>>>> + if ((*tipp)->id != curid) {
>>>> + spin_unlock_irqrestore(&thash[*hash].lock,
>>>> + tflags);
>>>> + *hash = THASH(curid, idtype);
>>>> +
>>>> + spin_lock_irqsave(&thash[*hash].lock, tflags);
>>>> +
>>>> + if ( (*tipp = get_taskinfo(curid, idtype))
>>>> + == NULL) {
>>>> + spin_unlock_irqrestore(
>>>> + &thash[*hash].lock, tflags);
>>>> + return 0;
>>>> + }
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + update_taskcounters(skb, ndev, *tipp, direction);
>>>> +
>>>> + spin_unlock_irqrestore(&thash[*hash].lock, tflags);
>>>> +
>>>> + return 1;
>>>> +}
>>>> +
>>>> +/*
>>>> +** update the statistics of a particular thread group or thread
>>>> +*/
>>>> +static void
>>>> +update_taskcounters(struct sk_buff *skb, const struct net_device *ndev,
>>>> + struct taskinfo *tip, char direction)
>>>> +{
>>>> + struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
>>>> + int reallen = calc_reallen(skb, ndev);
>>>> +
>>>> + switch (iph->protocol) {
>>>> + case IPPROTO_TCP:
>>>> + if (direction == 'i') {
>>>> + tip->tc.tcprcvpacks++;
>>>> + tip->tc.tcprcvbytes += reallen;
>>>> + } else {
>>>> + tip->tc.tcpsndpacks++;
>>>> + tip->tc.tcpsndbytes += reallen;
>>>> + }
>>>> + break;
>>>> +
>>>> + case IPPROTO_UDP:
>>>> + if (direction == 'i') {
>>>> + tip->tc.udprcvpacks++;
>>>> + tip->tc.udprcvbytes += reallen;
>>>> + } else {
>>>> + tip->tc.udpsndpacks++;
>>>> + tip->tc.udpsndbytes += reallen;
>>>> + }
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** update the statistics of a sockinfo without a connected task
>>>> +*/
>>>> +static void
>>>> +update_sockcounters(struct sk_buff *skb, const struct net_device *ndev,
>>>> + struct sockinfo *sip, char direction)
>>>> +{
>>>> + int reallen = calc_reallen(skb, ndev);
>>>> +
>>>> + if (direction == 'i') {
>>>> + sip->rcvpacks++;
>>>> + sip->rcvbytes += reallen;
>>>> + } else {
>>>> + sip->sndpacks++;
>>>> + sip->sndbytes += reallen;
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** add the temporary counters in the sockinfo to the new connected task
>>>> +*/
>>>> +static void
>>>> +sock2task_sync(struct sk_buff *skb, struct sockinfo *sip, struct taskinfo *tip)
>>>> +{
>>>> + struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
>>>> +
>>>> + switch (iph->protocol) {
>>>> + case IPPROTO_TCP:
>>>> + tip->tc.tcprcvpacks += sip->rcvpacks;
>>>> + tip->tc.tcprcvbytes += sip->rcvbytes;
>>>> + tip->tc.tcpsndpacks += sip->sndpacks;
>>>> + tip->tc.tcpsndbytes += sip->sndbytes;
>>>> + break;
>>>> +
>>>> + case IPPROTO_UDP:
>>>> + tip->tc.udprcvpacks += sip->rcvpacks;
>>>> + tip->tc.udprcvbytes += sip->rcvbytes;
>>>> + tip->tc.udpsndpacks += sip->sndpacks;
>>>> + tip->tc.udpsndbytes += sip->sndbytes;
>>>> + }
>>>> +}
>>>> +
>>>> +static void
>>>> +register_unident(struct sockinfo *sip)
>>>> +{
>>>> + switch (sip->proto) {
>>>> + case IPPROTO_TCP:
>>>> + unidenttcprcvpacks += sip->rcvpacks;
>>>> + unidenttcpsndpacks += sip->sndpacks;
>>>> + break;
>>>> +
>>>> + case IPPROTO_UDP:
>>>> + unidentudprcvpacks += sip->rcvpacks;
>>>> + unidentudpsndpacks += sip->sndpacks;
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** calculate the number of bytes that are really sent or received
>>>> +*/
>>>> +static int
>>>> +calc_reallen(struct sk_buff *skb, const struct net_device *ndev)
>>>> +{
>>>> + /*
>>>> + ** calculate the real load of this packet on the network:
>>>> + **
>>>> + ** - length of IP header, TCP/UDP header and data (skb->len)
>>>> + **
>>>> + ** since packet assembly/disassembly is done by the IP layer
>>>> + ** (we get an input packet that has been assembled already and
>>>> + ** an output packet that still has to be disassembled), additional
>>>> + ** IP headers/interface headers and interface headers have
>>>> + ** to be calculated for packets that are larger than the mtu
>>>> + **
>>>> + ** - interface header length + 4 bytes crc
>>>> + */
>>>> + int reallen = skb->len;
>>>> +
>>>> + if (reallen > ndev->mtu)
>>>> + reallen += (reallen / ndev->mtu) *
>>>> + (sizeof(struct iphdr) + ndev->hard_header_len + 4);
>>>> +
>>>> + reallen += ndev->hard_header_len + 4;
>>>> +
>>>> + return reallen;
>>>> +}
>>>> +
>>>> +/*
>>>> +** find the tcpv4_ident for the current packet, represented by
>>>> +** the skb_buff
>>>> +*/
>>>> +static void
>>>> +get_tcpv4_ident(struct iphdr *iph, void *trh, char direction, union keydef *key)
>>>> +{
>>>> + struct tcphdr *tcph = (struct tcphdr *)trh;
>>>> +
>>>> + memset(key, 0, sizeof *key); // important for memcmp later on
>>>> +
>>>> + /*
>>>> + ** determine local/remote IP address and
>>>> + ** determine local/remote port number
>>>> + */
>>>> + switch (direction) {
>>>> + case 'i': // incoming packet
>>>> + key->tcp4.laddr = ntohl(iph->daddr);
>>>> + key->tcp4.raddr = ntohl(iph->saddr);
>>>> + key->tcp4.lport = ntohs(tcph->dest);
>>>> + key->tcp4.rport = ntohs(tcph->source);
>>>> + break;
>>>> +
>>>> + case 'o': // outgoing packet
>>>> + key->tcp4.laddr = ntohl(iph->saddr);
>>>> + key->tcp4.raddr = ntohl(iph->daddr);
>>>> + key->tcp4.lport = ntohs(tcph->source);
>>>> + key->tcp4.rport = ntohs(tcph->dest);
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** search for the sockinfo holding the given address info
>>>> +** the appropriate hash bucket must have been locked before calling
>>>> +*/
>>>> +static struct sockinfo *
>>>> +find_sockinfo(int proto, union keydef *identp, int identsz, int hash)
>>>> +{
>>>> + struct sockinfo *sip = shash[hash].ch.next;
>>>> +
>>>> + /*
>>>> + ** search for appropriate struct
>>>> + */
>>>> + while (sip != (void *)&shash[hash].ch) {
>>>> + if ( memcmp(&sip->key, identp, identsz) == 0 &&
>>>> + sip->proto == proto) {
>>>> + sip->lastact = jiffies_64;
>>>> + return sip;
>>>> + }
>>>> +
>>>> + sip = sip->ch.next;
>>>> + }
>>>> +
>>>> + return NULL; // not existing
>>>> +}
>>>> +
>>>> +/*
>>>> +** create a new sockinfo and fill
>>>> +** the appropriate hash bucket must have been locked before calling
>>>> +*/
>>>> +static struct sockinfo *
>>>> +make_sockinfo(int proto, union keydef *identp, int identsz, int hash)
>>>> +{
>>>> + struct sockinfo *sip;
>>>> + unsigned long flags;
>>>> +
>>>> + /*
>>>> + ** check if the threshold of memory used for sockinfo structs
>>>> + ** is reached to avoid that a fork bomb of processes opening
>>>> + ** a socket leads to memory overload
>>>> + */
>>>> + if ( (nrs+1) * sizeof(struct sockinfo) > SILIMIT) {
>>>> + spin_lock_irqsave(&nrslock, flags);
>>>> + nrs_ovf++;
>>>> + spin_unlock_irqrestore(&nrslock, flags);
>>>> + return NULL;
>>>> + }
>>>> +
>>>> + if ( (sip = kmem_cache_alloc(sicache, GFP_ATOMIC)) == NULL)
>>>> + return NULL;
>>>> +
>>>> + spin_lock_irqsave(&nrslock, flags);
>>>> + nrs++;
>>>> + spin_unlock_irqrestore(&nrslock, flags);
>>>> +
>>>> + /*
>>>> + ** insert new struct in doubly linked list
>>>> + */
>>>> + memset(sip, '\0', sizeof *sip);
>>>> +
>>>> + sip->ch.next = &shash[hash].ch;
>>>> + sip->ch.prev = shash[hash].ch.prev;
>>>> + ((struct sockinfo *)shash[hash].ch.prev)->ch.next = sip;
>>>> + shash[hash].ch.prev = sip;
>>>> +
>>>> + sip->proto = proto;
>>>> + sip->lastact = jiffies_64;
>>>> + sip->key = *identp;
>>>> +
>>>> + return sip;
>>>> +}
>>>> +
>>>> +/*
>>>> +** search the taskinfo structure holding the info about the given id/type
>>>> +** if such taskinfo is not yet present, create a new one
>>>> +*/
>>>> +static struct taskinfo *
>>>> +get_taskinfo(pid_t id, char type)
>>>> +{
>>>> + int bt = THASH(id, type);
>>>> + struct taskinfo *tip = thash[bt].ch.next;
>>>> + unsigned long tflags;
>>>> +
>>>> + /*
>>>> + ** search if id exists already
>>>> + */
>>>> + while (tip != (void *)&thash[bt].ch) {
>>>> + if (tip->id == id && tip->type == type)
>>>> + return tip;
>>>> +
>>>> + tip = tip->ch.next;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if the threshold of memory used for taskinfo structs
>>>> + ** is reached to avoid that a fork bomb of processes opening
>>>> + ** a socket lead to memory overload
>>>> + */
>>>> + if ( (nre+nrt+1) * sizeof(struct taskinfo) > TILIMIT) {
>>>> + spin_lock_irqsave(&nrtlock, tflags);
>>>> + nrt_ovf++;
>>>> + spin_unlock_irqrestore(&nrtlock, tflags);
>>>> + return NULL;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** id not known yet
>>>> + ** add new entry to hash list
>>>> + */
>>>> + if ( (tip = kmem_cache_alloc(ticache, GFP_ATOMIC)) == NULL)
>>>> + return NULL;
>>>> +
>>>> + spin_lock_irqsave(&nrtlock, tflags);
>>>> + nrt++;
>>>> + spin_unlock_irqrestore(&nrtlock, tflags);
>>>> +
>>>> + /*
>>>> + ** insert new struct in doubly linked list
>>>> + ** and fill values
>>>> + */
>>>> + memset(tip, '\0', sizeof *tip);
>>>> +
>>>> + tip->ch.next = &thash[bt].ch;
>>>> + tip->ch.prev = thash[bt].ch.prev;
>>>> + ((struct taskinfo *)thash[bt].ch.prev)->ch.next = tip;
>>>> + thash[bt].ch.prev = tip;
>>>> +
>>>> + tip->id = id;
>>>> + tip->type = type;
>>>> +
>>>> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
>>>> + tip->btime = div_u64((current->real_start_time +
>>>> + (boottime.tv_sec * NSEC_PER_SEC +
>>>> + boottime.tv_sec)), NSEC_PER_SEC);
>>>> +#else
>>>> + // current->real_start_time is type u64
>>>> + tip->btime = current->real_start_time.tv_sec + boottime.tv_sec;
>>>> +
>>>> + if (current->real_start_time.tv_nsec + boottime.tv_nsec > NSEC_PER_SEC)
>>>> + tip->btime++;
>>>> +#endif
>>>> +
>>>> + strncpy(tip->command, current->comm, COMLEN);
>>>> +
>>>> + return tip;
>>>> +}
>>>> +
>>>> +/*
>>>> +** garbage collector that removes:
>>>> +** - exited tasks that are no longer used by user mode programs
>>>> +** - sockinfo's that are not used any more
>>>> +** - taskinfo's that do not exist any more
>>>> +**
>>>> +** a mutex avoids that the garbage collector runs several times in parallel
>>>> +**
>>>> +** this function may only be called in process context!
>>>> +*/
>>>> +static void
>>>> +garbage_collector(void)
>>>> +{
>>>> + mutex_lock(&gclock);
>>>> +
>>>> + if (jiffies_64 < gclast + (HZ/2)) { // maximum 2 GC cycles per second
>>>> + mutex_unlock(&gclock);
>>>> + return;
>>>> + }
>>>> +
>>>> + gctaskexit(); // remove remaining taskinfo structs from exit list
>>>> +
>>>> + gcsockinfo(); // clean up sockinfo structs in shash list
>>>> +
>>>> + gctaskinfo(); // clean up taskinfo structs in thash list
>>>> +
>>>> + gclast = jiffies_64;
>>>> +
>>>> + mutex_unlock(&gclock);
>>>> +}
>>>> +
>>>> +/*
>>>> +** tasks in the exitlist can be read by a user mode process for a limited
>>>> +** amount of time; this function removes all taskinfo structures that have
>>>> +** not been read within that period of time
>>>> +** notice that exited processes are chained to the tail, so the oldest
>>>> +** can be found at the head
>>>> +*/
>>>> +static void
>>>> +gctaskexit()
>>>> +{
>>>> + unsigned long flags;
>>>> + struct taskinfo *tip;
>>>> +
>>>> + spin_lock_irqsave(&exitlock, flags);
>>>> +
>>>> + for (tip=exithead; tip;) {
>>>> + if (jiffies_64 < tip->exittime + GCINTERVAL)
>>>> + break;
>>>> +
>>>> + // remove taskinfo from exitlist
>>>> + exithead = tip->ch.next;
>>>> + kmem_cache_free(ticache, tip);
>>>> + nre--;
>>>> + tip = exithead;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** if list empty now, then exithead and exittail both NULL
>>>> + ** wakeup waiters for emptylist
>>>> + */
>>>> + if (nre == 0) {
>>>> + exittail = NULL;
>>>> + wake_up_interruptible(&exitlist_empty);
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&exitlock, flags);
>>>> +}
>>>> +
>>>> +/*
>>>> +** cleanup sockinfo structures that are connected to finished processes
>>>> +*/
>>>> +static void
>>>> +gcsockinfo()
>>>> +{
>>>> + int i;
>>>> + struct sockinfo *sip, *sipsave;
>>>> + unsigned long sflags, tflags;
>>>> + struct pid *pid;
>>>> +
>>>> + /*
>>>> + ** go through all sockinfo hash buckets
>>>> + */
>>>> + for (i=0; i < SBUCKS; i++) {
>>>> + if (shash[i].ch.next == (void *)&shash[i].ch)
>>>> + continue; // quick return without lock
>>>> +
>>>> + spin_lock_irqsave(&shash[i].lock, sflags);
>>>> +
>>>> + sip = shash[i].ch.next;
>>>> +
>>>> + /*
>>>> + ** search all sockinfo structs chained in one bucket
>>>> + */
>>>> + while (sip != (void *)&shash[i].ch) {
>>>> + /*
>>>> + ** TCP connections that were not in
>>>> + ** state ESTABLISHED or LISTEN can be
>>>> + ** eliminated
>>>> + */
>>>> + if (sip->proto == IPPROTO_TCP) {
>>>> + switch (sip->last_state) {
>>>> + case TCP_ESTABLISHED:
>>>> + case TCP_LISTEN:
>>>> + break;
>>>> +
>>>> + default:
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + continue;
>>>> + }
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if this sockinfo has no relation
>>>> + ** for a while with a thread group
>>>> + ** if so, delete the sockinfo
>>>> + */
>>>> + if (sip->tgp == NULL) {
>>>> + if (sip->lastact + GCMAXUNREF < jiffies_64) {
>>>> + register_unident(sip);
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + } else {
>>>> + sip = sip->ch.next;
>>>> + }
>>>> + continue;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if referred thread group is
>>>> + ** already marked as 'indelete' during this
>>>> + ** sockinfo search
>>>> + ** if so, delete this sockinfo
>>>> + */
>>>> + spin_lock_irqsave(&thash[sip->tgh].lock, tflags);
>>>> +
>>>> + if (sip->tgp->state == INDELETE) {
>>>> + spin_unlock_irqrestore(&thash[sip->tgh].lock,
>>>> + tflags);
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + continue;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if referred thread group still exists;
>>>> + ** this step will be skipped if we already verified
>>>> + ** the existance of the thread group earlier during
>>>> + ** this garbage collection cycle
>>>> + */
>>>> + if (sip->tgp->state != CHECKED) {
>>>> + /*
>>>> + ** connected thread group not yet verified
>>>> + ** during this cycle, so check if it still
>>>> + ** exists
>>>> + ** if not, mark the thread group as 'indelete'
>>>> + ** (it can not be deleted right now because
>>>> + ** we might find other sockinfo's referring
>>>> + ** to this thread group during the current
>>>> + ** cycle) and delete this sockinfo
>>>> + ** if the thread group exists, just mark
>>>> + ** it as 'checked' for this cycle
>>>> + */
>>>> + rcu_read_lock();
>>>> + pid = find_vpid(sip->tgp->id);
>>>> + rcu_read_unlock();
>>>> +
>>>> + if (pid == NULL) {
>>>> + sip->tgp->state = INDELETE;
>>>> + spin_unlock_irqrestore(
>>>> + &thash[sip->tgh].lock, tflags);
>>>> +
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + continue;
>>>> + } else {
>>>> + sip->tgp->state = CHECKED;
>>>> + }
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&thash[sip->tgh].lock, tflags);
>>>> +
>>>> + /*
>>>> + ** check if this sockinfo has a relation with a thread
>>>> + ** if not, skip further handling of this sockinfo
>>>> + */
>>>> + if (sip->thp == NULL) {
>>>> + sip = sip->ch.next;
>>>> + continue;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if referred thread is already marked
>>>> + ** as 'indelete' during this sockinfo search
>>>> + ** if so, break connection
>>>> + */
>>>> + spin_lock_irqsave(&thash[sip->thh].lock, tflags);
>>>> +
>>>> + if (sip->thp->state == INDELETE) {
>>>> + spin_unlock_irqrestore(&thash[sip->thh].lock,
>>>> + tflags);
>>>> + sip->thp = NULL;
>>>> + sip = sip->ch.next;
>>>> + continue;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if referred thread is already checked
>>>> + ** during this sockinfo search
>>>> + */
>>>> + if (sip->thp->state == CHECKED) {
>>>> + spin_unlock_irqrestore(&thash[sip->thh].lock,
>>>> + tflags);
>>>> + sip = sip->ch.next;
>>>> + continue;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** connected thread not yet verified
>>>> + ** check if it still exists
>>>> + ** if not, mark it as 'indelete' and break connection
>>>> + ** if thread exists, mark it 'checked'
>>>> + */
>>>> + rcu_read_lock();
>>>> + pid = find_vpid(sip->thp->id);
>>>> + rcu_read_unlock();
>>>> +
>>>> + if (pid == NULL) {
>>>> + sip->thp->state = INDELETE;
>>>> + sip->thp = NULL;
>>>> + } else {
>>>> + sip->thp->state = CHECKED;
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&thash[sip->thh].lock, tflags);
>>>> +
>>>> + /*
>>>> + ** check if a TCP port has not been used
>>>> + ** for some time --> destroy even if the thread
>>>> + ** (group) is still there
>>>> + */
>>>> + if (sip->proto == IPPROTO_TCP &&
>>>> + sip->lastact + GCMAXTCP < jiffies_64) {
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + continue;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** check if a UDP port has not been used
>>>> + ** for some time --> destroy even if the thread
>>>> + ** (group) is still there
>>>> + ** e.g. outgoing DNS requests (to remote port 53) are
>>>> + ** issued every time with another source port being
>>>> + ** a new object that should not be kept too long;
>>>> + ** local well-known ports are useful to keep
>>>> + */
>>>> + if (sip->proto == IPPROTO_UDP &&
>>>> + sip->lastact + GCMAXUDP < jiffies_64 &&
>>>> + sip->key.udp > 1024) {
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + continue;
>>>> + }
>>>> +
>>>> + sip = sip->ch.next;
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&shash[i].lock, sflags);
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** remove taskinfo structures of finished tasks from hash list
>>>> +*/
>>>> +static void
>>>> +gctaskinfo()
>>>> +{
>>>> + int i;
>>>> + struct taskinfo *tip, *tipsave;
>>>> + unsigned long tflags;
>>>> + struct pid *pid;
>>>> +
>>>> + /*
>>>> + ** go through all taskinfo hash buckets
>>>> + */
>>>> + for (i=0; i < TBUCKS; i++) {
>>>> + if (thash[i].ch.next == (void *)&thash[i].ch)
>>>> + continue; // quick return without lock
>>>> +
>>>> + spin_lock_irqsave(&thash[i].lock, tflags);
>>>> +
>>>> + tip = thash[i].ch.next;
>>>> +
>>>> + /*
>>>> + ** check all taskinfo structs chained to this bucket
>>>> + */
>>>> + while (tip != (void *)&thash[i].ch) {
>>>> + switch (tip->state) {
>>>> + /*
>>>> + ** remove INDELETE tasks from the hash buckets
>>>> + ** -- move thread group to exitlist
>>>> + ** -- destroy thread right away
>>>> + */
>>>> + case INDELETE:
>>>> + tipsave = tip->ch.next;
>>>> +
>>>> + if (tip->type == 'g')
>>>> + move_taskinfo(tip); // thread group
>>>> + else
>>>> + delete_taskinfo(tip); // thread
>>>> +
>>>> + tip = tipsave;
>>>> + break;
>>>> +
>>>> + case CHECKED:
>>>> + tip->state = 0;
>>>> + tip = tip->ch.next;
>>>> + break;
>>>> +
>>>> + default: // not checked yet
>>>> + rcu_read_lock();
>>>> + pid = find_vpid(tip->id);
>>>> + rcu_read_unlock();
>>>> +
>>>> + if (pid == NULL) {
>>>> + tipsave = tip->ch.next;
>>>> +
>>>> + if (tip->type == 'g')
>>>> + move_taskinfo(tip);
>>>> + else
>>>> + delete_taskinfo(tip);
>>>> +
>>>> + tip = tipsave;
>>>> + } else {
>>>> + tip = tip->ch.next;
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&thash[i].lock, tflags);
>>>> + }
>>>> +}
>>>> +
>>>> +
>>>> +/*
>>>> +** remove all sockinfo structs
>>>> +*/
>>>> +static void
>>>> +wipesockinfo()
>>>> +{
>>>> + struct sockinfo *sip, *sipsave;
>>>> + int i;
>>>> + unsigned long sflags;
>>>> +
>>>> + for (i=0; i < SBUCKS; i++) {
>>>> + spin_lock_irqsave(&shash[i].lock, sflags);
>>>> +
>>>> + sip = shash[i].ch.next;
>>>> +
>>>> + /*
>>>> + ** free all structs chained in one bucket
>>>> + */
>>>> + while (sip != (void *)&shash[i].ch) {
>>>> + sipsave = sip->ch.next;
>>>> + delete_sockinfo(sip);
>>>> + sip = sipsave;
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&shash[i].lock, sflags);
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** remove all taskinfo structs from hash list
>>>> +*/
>>>> +static void
>>>> +wipetaskinfo()
>>>> +{
>>>> + struct taskinfo *tip, *tipsave;
>>>> + int i;
>>>> + unsigned long tflags;
>>>> +
>>>> + for (i=0; i < TBUCKS; i++) {
>>>> + spin_lock_irqsave(&thash[i].lock, tflags);
>>>> +
>>>> + tip = thash[i].ch.next;
>>>> +
>>>> + /*
>>>> + ** free all structs chained in one bucket
>>>> + */
>>>> + while (tip != (void *)&thash[i].ch) {
>>>> + tipsave = tip->ch.next;
>>>> + delete_taskinfo(tip);
>>>> + tip = tipsave;
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&thash[i].lock, tflags);
>>>> + }
>>>> +}
>>>> +
>>>> +/*
>>>> +** remove all taskinfo structs from exit list
>>>> +*/
>>>> +static void
>>>> +wipetaskexit()
>>>> +{
>>>> + gctaskexit();
>>>> +}
>>>> +
>>>> +/*
>>>> +** move one taskinfo struct from hash bucket to exitlist
>>>> +*/
>>>> +static void
>>>> +move_taskinfo(struct taskinfo *tip)
>>>> +{
>>>> + unsigned long flags;
>>>> +
>>>> + /*
>>>> + ** remove from hash list
>>>> + */
>>>> + ((struct taskinfo *)tip->ch.next)->ch.prev = tip->ch.prev;
>>>> + ((struct taskinfo *)tip->ch.prev)->ch.next = tip->ch.next;
>>>> +
>>>> + spin_lock_irqsave(&nrtlock, flags);
>>>> + nrt--;
>>>> + spin_unlock_irqrestore(&nrtlock, flags);
>>>> +
>>>> + /*
>>>> + ** add to exitlist
>>>> + */
>>>> + tip->ch.next = NULL;
>>>> + tip->state = FINISHED;
>>>> + tip->exittime = jiffies_64;
>>>> +
>>>> + spin_lock_irqsave(&exitlock, flags);
>>>> +
>>>> + if (exittail) { // list filled?
>>>> + exittail->ch.next = tip;
>>>> + exittail = tip;
>>>> + } else { // list empty
>>>> + exithead = exittail = tip;
>>>> + }
>>>> +
>>>> + nre++;
>>>> +
>>>> + wake_up_interruptible(&exitlist_filled);
>>>> +
>>>> + spin_unlock_irqrestore(&exitlock, flags);
>>>> +}
>>>> +
>>>> +/*
>>>> +** remove one taskinfo struct for the hash bucket chain
>>>> +*/
>>>> +static void
>>>> +delete_taskinfo(struct taskinfo *tip)
>>>> +{
>>>> + unsigned long flags;
>>>> +
>>>> + ((struct taskinfo *)tip->ch.next)->ch.prev = tip->ch.prev;
>>>> + ((struct taskinfo *)tip->ch.prev)->ch.next = tip->ch.next;
>>>> +
>>>> + kmem_cache_free(ticache, tip);
>>>> +
>>>> + spin_lock_irqsave(&nrtlock, flags);
>>>> + nrt--;
>>>> + spin_unlock_irqrestore(&nrtlock, flags);
>>>> +}
>>>> +
>>>> +/*
>>>> +** remove one sockinfo struct for the hash bucket chain
>>>> +*/
>>>> +static void
>>>> +delete_sockinfo(struct sockinfo *sip)
>>>> +{
>>>> + unsigned long flags;
>>>> +
>>>> + ((struct sockinfo *)sip->ch.next)->ch.prev = sip->ch.prev;
>>>> + ((struct sockinfo *)sip->ch.prev)->ch.next = sip->ch.next;
>>>> +
>>>> + kmem_cache_free(sicache, sip);
>>>> +
>>>> + spin_lock_irqsave(&nrslock, flags);
>>>> + nrs--;
>>>> + spin_unlock_irqrestore(&nrslock, flags);
>>>> +}
>>>> +
>>>> +/*
>>>> +** read function for /proc/netatop
>>>> +*/
>>>> +static int
>>>> +netatop_show(struct seq_file *m, void *v)
>>>> +{
>>>> + seq_printf(m, "tcpsndpacks: %12lu (unident: %9lu)\n"
>>>> + "tcprcvpacks: %12lu (unident: %9lu)\n"
>>>> + "udpsndpacks: %12lu (unident: %9lu)\n"
>>>> + "udprcvpacks: %12lu (unident: %9lu)\n\n"
>>>> + "icmpsndpacks: %12lu\n"
>>>> + "icmprcvpacks: %12lu\n\n"
>>>> + "#sockinfo: %12lu (overflow: %8lu)\n"
>>>> + "#taskinfo: %12lu (overflow: %8lu)\n"
>>>> + "#taskexit: %12lu\n\n"
>>>> + "modversion: %14s\n",
>>>> + tcpsndpacks, unidenttcpsndpacks,
>>>> + tcprcvpacks, unidenttcprcvpacks,
>>>> + udpsndpacks, unidentudpsndpacks,
>>>> + udprcvpacks, unidentudprcvpacks,
>>>> + icmpsndpacks, icmprcvpacks,
>>>> + nrs, nrs_ovf,
>>>> + nrt, nrt_ovf,
>>>> + nre, NETATOPVERSION);
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int
>>>> +netatop_open(struct inode *inode, struct file *file)
>>>> +{
>>>> + return single_open(file, netatop_show, NULL);
>>>> +}
>>>> +
>>>> +/*
>>>> +** called when user spce issues system call getsockopt()
>>>> +*/
>>>> +static int
>>>> +getsockopt(struct sock *sk, int cmd, void __user *user, int *len)
>>>> +{
>>>> + int bt;
>>>> + struct taskinfo *tip;
>>>> + char tasktype = 't';
>>>> + struct netpertask npt;
>>>> + unsigned long tflags;
>>>> +
>>>> + /*
>>>> + ** verify the proper privileges
>>>> + */
>>>> + if (!capable(CAP_NET_ADMIN))
>>>> + return -EPERM;
>>>> +
>>>> + /*
>>>> + ** react on command
>>>> + */
>>>> + switch (cmd) {
>>>> + case NETATOP_PROBE:
>>>> + break;
>>>> +
>>>> + case NETATOP_FORCE_GC:
>>>> + garbage_collector();
>>>> + break;
>>>> +
>>>> + case NETATOP_EMPTY_EXIT:
>>>> + while (nre > 0) {
>>>> + if (wait_event_interruptible(exitlist_empty, nre == 0))
>>>> + return -ERESTARTSYS;
>>>> + }
>>>> + break;
>>>> +
>>>> + case NETATOP_GETCNT_EXIT:
>>>> + if (nre == 0)
>>>> + wake_up_interruptible(&exitlist_empty);
>>>> +
>>>> + if (*len < sizeof(pid_t))
>>>> + return -EINVAL;
>>>> +
>>>> + if (*len > sizeof npt)
>>>> + *len = sizeof npt;
>>>> +
>>>> + spin_lock_irqsave(&exitlock, tflags);
>>>> +
>>>> + /*
>>>> + ** check if an exited process is present
>>>> + ** if not, wait for it...
>>>> + */
>>>> + while (nre == 0) {
>>>> + spin_unlock_irqrestore(&exitlock, tflags);
>>>> +
>>>> + if ( wait_event_interruptible(exitlist_filled, nre > 0))
>>>> + return -ERESTARTSYS;
>>>> +
>>>> + spin_lock_irqsave(&exitlock, tflags);
>>>> + }
>>>> +
>>>> + /*
>>>> + ** get first eprocess from exitlist and remove it from there
>>>> + */
>>>> + tip = exithead;
>>>> +
>>>> + if ( (exithead = tip->ch.next) == NULL)
>>>> + exittail = NULL;
>>>> +
>>>> + nre--;
>>>> +
>>>> + spin_unlock_irqrestore(&exitlock, tflags);
>>>> +
>>>> + /*
>>>> + ** pass relevant info to user mode
>>>> + ** and free taskinfo struct
>>>> + */
>>>> + npt.id = tip->id;
>>>> + npt.tc = tip->tc;
>>>> + npt.btime = tip->btime;
>>>> + memcpy(npt.command, tip->command, COMLEN);
>>>> +
>>>> + if (copy_to_user(user, &npt, *len) != 0)
>>>> + return -EFAULT;
>>>> +
>>>> + kmem_cache_free(ticache, tip);
>>>> +
>>>> + return 0;
>>>> +
>>>> + case NETATOP_GETCNT_TGID:
>>>> + tasktype = 'g';
>>>> +
>>>> + case NETATOP_GETCNT_PID:
>>>> + if (*len < sizeof(pid_t))
>>>> + return -EINVAL;
>>>> +
>>>> + if (*len > sizeof npt)
>>>> + *len = sizeof npt;
>>>> +
>>>> + if (copy_from_user(&npt, user, *len) != 0)
>>>> + return -EFAULT;
>>>> +
>>>> + /*
>>>> + ** search requested id in taskinfo hash
>>>> + */
>>>> + bt = THASH(npt.id, tasktype); // calculate hash
>>>> +
>>>> + if (thash[bt].ch.next == (void *)&thash[bt].ch)
>>>> + return -ESRCH; // quick return without lock
>>>> +
>>>> + spin_lock_irqsave(&thash[bt].lock, tflags);
>>>> +
>>>> + tip = thash[bt].ch.next;
>>>> +
>>>> + while (tip != (void *)&thash[bt].ch) {
>>>> + // is this the one?
>>>> + if (tip->id == npt.id && tip->type == tasktype) {
>>>> + /*
>>>> + ** found: copy results to user space
>>>> + */
>>>> + memcpy(npt.command, tip->command, COMLEN);
>>>> + npt.tc = tip->tc;
>>>> + npt.btime = tip->btime;
>>>> +
>>>> + spin_unlock_irqrestore(&thash[bt].lock, tflags);
>>>> +
>>>> + if (copy_to_user(user, &npt, *len) != 0)
>>>> + return -EFAULT;
>>>> + else
>>>> + return 0;
>>>> + }
>>>> +
>>>> + tip = tip->ch.next;
>>>> + }
>>>> +
>>>> + spin_unlock_irqrestore(&thash[bt].lock, tflags);
>>>> + return -ESRCH;
>>>> +
>>>> + default:
>>>> + printk(KERN_INFO "unknown getsockopt command %d\n", cmd);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/*
>>>> +** kernel mode thread: initiate garbage collection every N seconds
>>>> +*/
>>>> +static int netatop_thread(void *dummy)
>>>> +{
>>>> + while (!kthread_should_stop()) {
>>>> + /*
>>>> + ** do garbage collection
>>>> + */
>>>> + garbage_collector();
>>>> +
>>>> + /*
>>>> + ** wait a while
>>>> + */
>>>> + (void) schedule_timeout_interruptible(GCINTERVAL);
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
>>>> +static int __net_init netatop_nf_register(struct net *net)
>>>> +{
>>>> + int err;
>>>> +
>>>> + err = nf_register_net_hooks(net, &hookin_ipv4, 1);
>>>> + if (err)
>>>> + return err;
>>>> +
>>>> + err = nf_register_net_hooks(net, &hookout_ipv4, 1);
>>>> + if (err)
>>>> + nf_unregister_net_hooks(net, &hookin_ipv4, 1);
>>>> +
>>>> + return err;
>>>> +}
>>>> +
>>>> +static void __net_exit netatop_nf_unregister(struct net *net)
>>>> +{
>>>> + nf_unregister_net_hooks(net, &hookin_ipv4, 1);
>>>> + nf_unregister_net_hooks(net, &hookout_ipv4, 1);
>>>> +}
>>>> +
>>>> +static struct pernet_operations netatop_net_ops = {
>>>> + .init = netatop_nf_register,
>>>> + .exit = netatop_nf_unregister,
>>>> +};
>>>> +#endif
>>>> +
>>>> +/*
>>>> +** called when module loaded
>>>> +*/
>>>> +int
>>>> +init_module()
>>>> +{
>>>> + int i;
>>>> +
>>>> + /*
>>>> + ** initialize caches for taskinfo and sockinfo
>>>> + */
>>>> + ticache = kmem_cache_create("Netatop_taskinfo",
>>>> + sizeof (struct taskinfo), 0, 0, NULL);
>>>> + if (!ticache)
>>>> + return -EFAULT;
>>>> +
>>>> + sicache = kmem_cache_create("Netatop_sockinfo",
>>>> + sizeof (struct sockinfo), 0, 0, NULL);
>>>> + if (!sicache) {
>>>> + kmem_cache_destroy(ticache);
>>>> + return -EFAULT;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** initialize hash table for taskinfo and sockinfo
>>>> + */
>>>> + for (i=0; i < TBUCKS; i++) {
>>>> + thash[i].ch.next = &thash[i].ch;
>>>> + thash[i].ch.prev = &thash[i].ch;
>>>> + spin_lock_init(&thash[i].lock);
>>>> + }
>>>> +
>>>> + for (i=0; i < SBUCKS; i++) {
>>>> + shash[i].ch.next = &shash[i].ch;
>>>> + shash[i].ch.prev = &shash[i].ch;
>>>> + spin_lock_init(&shash[i].lock);
>>>> + }
>>>> +
>>>> + getboottime(&boottime);
>>>> +
>>>> + /*
>>>> + ** register getsockopt for user space communication
>>>> + */
>>>> + if (nf_register_sockopt(&sockopts) < 0) {
>>>> + kmem_cache_destroy(ticache);
>>>> + kmem_cache_destroy(sicache);
>>>> + return -1;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** create a new kernel mode thread for time-driven garbage collection
>>>> + ** after creation, the thread waits until it is woken up
>>>> + */
>>>> + knetatop_task = kthread_create(netatop_thread, NULL, "knetatop");
>>>> +
>>>> + if (IS_ERR(knetatop_task)) {
>>>> + nf_unregister_sockopt(&sockopts);
>>>> + kmem_cache_destroy(ticache);
>>>> + kmem_cache_destroy(sicache);
>>>> + return -1;
>>>> + }
>>>> +
>>>> + /*
>>>> + ** prepare hooks for input and output packets
>>>> + */
>>>> + hookin_ipv4.hooknum = NF_IP_LOCAL_IN; // input packs
>>>> + hookin_ipv4.hook = ipv4_hookin; // func to call
>>>> + hookin_ipv4.pf = PF_INET; // IPV4 packets
>>>> + hookin_ipv4.priority = NF_IP_PRI_FIRST; // highest prio
>>>> +
>>>> + hookout_ipv4.hooknum = NF_IP_LOCAL_OUT; // output packs
>>>> + hookout_ipv4.hook = ipv4_hookout; // func to call
>>>> + hookout_ipv4.pf = PF_INET; // IPV4 packets
>>>> + hookout_ipv4.priority = NF_IP_PRI_FIRST; // highest prio
>>>> +
>>>> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
>>>> + i = register_pernet_subsys(&netatop_net_ops);
>>>> + if (i)
>>>> + return i;
>>>> +#else
>>>> + nf_register_hook(&hookin_ipv4); // register hook
>>>> + nf_register_hook(&hookout_ipv4); // register hook
>>>> +#endif
>>>> +
>>>> + /*
>>>> + ** create a /proc-entry to produce status-info on request
>>>> + */
>>>> + proc_create("netatop", 0444, NULL, &netatop_proc_fops);
>>>> +
>>>> + /*
>>>> + ** all admi prepared; kick off kernel mode thread
>>>> + */
>>>> + wake_up_process(knetatop_task);
>>>> +
>>>> + return 0; // return success
>>>> +}
>>>> +
>>>> +/*
>>>> +** called when module unloaded
>>>> +*/
>>>> +void
>>>> +cleanup_module()
>>>> +{
>>>> + /*
>>>> + ** tell kernel daemon to stop
>>>> + */
>>>> + kthread_stop(knetatop_task);
>>>> +
>>>> + /*
>>>> + ** unregister netfilter hooks and other miscellaneous stuff
>>>> + */
>>>> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
>>>> + unregister_pernet_subsys(&netatop_net_ops);
>>>> +#else
>>>> + nf_unregister_hook(&hookin_ipv4);
>>>> + nf_unregister_hook(&hookout_ipv4);
>>>> +#endif
>>>> +
>>>> + remove_proc_entry("netatop", NULL);
>>>> +
>>>> + nf_unregister_sockopt(&sockopts);
>>>> +
>>>> + /*
>>>> + ** destroy allocated stats
>>>> + */
>>>> + wipesockinfo();
>>>> + wipetaskinfo();
>>>> + wipetaskexit();
>>>> +
>>>> + /*
>>>> + ** destroy caches
>>>> + */
>>>> + kmem_cache_destroy(ticache);
>>>> + kmem_cache_destroy(sicache);
>>>> +}
>>>> +
>>>> +module_init(init_module);
>>>> +module_exit(cleanup_module);
>>>> diff --git a/drivers/staging/netatop/netatop.h b/drivers/staging/netatop/netatop.h
>>>> new file mode 100644
>>>> index 000000000000..a14e6fa5db52
>>>> --- /dev/null
>>>> +++ b/drivers/staging/netatop/netatop.h
>>>> @@ -0,0 +1,47 @@
>>>> +#define COMLEN 16
>>>> +
>>>> +struct taskcount {
>>>> + unsigned long long tcpsndpacks;
>>>> + unsigned long long tcpsndbytes;
>>>> + unsigned long long tcprcvpacks;
>>>> + unsigned long long tcprcvbytes;
>>>> +
>>>> + unsigned long long udpsndpacks;
>>>> + unsigned long long udpsndbytes;
>>>> + unsigned long long udprcvpacks;
>>>> + unsigned long long udprcvbytes;
>>>> +
>>>> + /* space for future extensions */
>>>> +};
>>>> +
>>>> +struct netpertask {
>>>> + pid_t id; // tgid or tid (depending on command)
>>>> + unsigned long btime;
>>>> + char command[COMLEN];
>>>> +
>>>> + struct taskcount tc;
>>>> +};
>>>> +
>>>> +
>>>> +/*
>>>> +** getsocktop commands
>>>> +*/
>>>> +#define NETATOP_BASE_CTL 15661
>>>> +
>>>> +// just probe if the netatop module is active
>>>> +#define NETATOP_PROBE (NETATOP_BASE_CTL)
>>>> +
>>>> +// force garbage collection to make finished processes available
>>>> +#define NETATOP_FORCE_GC (NETATOP_BASE_CTL+1)
>>>> +
>>>> +// wait until all finished processes are read (blocks until done)
>>>> +#define NETATOP_EMPTY_EXIT (NETATOP_BASE_CTL+2)
>>>> +
>>>> +// get info for finished process (blocks until available)
>>>> +#define NETATOP_GETCNT_EXIT (NETATOP_BASE_CTL+3)
>>>> +
>>>> +// get counters for thread group (i.e. process): input is 'id' (pid)
>>>> +#define NETATOP_GETCNT_TGID (NETATOP_BASE_CTL+4)
>>>> +
>>>> +// get counters for thread: input is 'id' (tid)
>>>> +#define NETATOP_GETCNT_PID (NETATOP_BASE_CTL+5)
>>>> diff --git a/drivers/staging/netatop/netatopversion.h b/drivers/staging/netatop/netatopversion.h
>>>> new file mode 100644
>>>> index 000000000000..eb011f5a13c9
>>>> --- /dev/null
>>>> +++ b/drivers/staging/netatop/netatopversion.h
>>>> @@ -0,0 +1,2 @@
>>>> +#define NETATOPVERSION "2.0"
>>>> +#define NETATOPDATE "2017/09/30 12:06:19"
>>>>
>>>
>>
>>
>
More information about the linux-yocto
mailing list