cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nft_counter.c (7846B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
      4 *
      5 * Development of this code funded by Astaro AG (http://www.astaro.com/)
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/init.h>
     10#include <linux/module.h>
     11#include <linux/seqlock.h>
     12#include <linux/netlink.h>
     13#include <linux/netfilter.h>
     14#include <linux/netfilter/nf_tables.h>
     15#include <net/netfilter/nf_tables.h>
     16#include <net/netfilter/nf_tables_core.h>
     17#include <net/netfilter/nf_tables_offload.h>
     18
     19struct nft_counter {
     20	s64		bytes;
     21	s64		packets;
     22};
     23
     24struct nft_counter_percpu_priv {
     25	struct nft_counter __percpu *counter;
     26};
     27
     28static DEFINE_PER_CPU(seqcount_t, nft_counter_seq);
     29
     30static inline void nft_counter_do_eval(struct nft_counter_percpu_priv *priv,
     31				       struct nft_regs *regs,
     32				       const struct nft_pktinfo *pkt)
     33{
     34	struct nft_counter *this_cpu;
     35	seqcount_t *myseq;
     36
     37	local_bh_disable();
     38	this_cpu = this_cpu_ptr(priv->counter);
     39	myseq = this_cpu_ptr(&nft_counter_seq);
     40
     41	write_seqcount_begin(myseq);
     42
     43	this_cpu->bytes += pkt->skb->len;
     44	this_cpu->packets++;
     45
     46	write_seqcount_end(myseq);
     47	local_bh_enable();
     48}
     49
     50static inline void nft_counter_obj_eval(struct nft_object *obj,
     51					struct nft_regs *regs,
     52					const struct nft_pktinfo *pkt)
     53{
     54	struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
     55
     56	nft_counter_do_eval(priv, regs, pkt);
     57}
     58
     59static int nft_counter_do_init(const struct nlattr * const tb[],
     60			       struct nft_counter_percpu_priv *priv)
     61{
     62	struct nft_counter __percpu *cpu_stats;
     63	struct nft_counter *this_cpu;
     64
     65	cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_KERNEL_ACCOUNT);
     66	if (cpu_stats == NULL)
     67		return -ENOMEM;
     68
     69	preempt_disable();
     70	this_cpu = this_cpu_ptr(cpu_stats);
     71	if (tb[NFTA_COUNTER_PACKETS]) {
     72	        this_cpu->packets =
     73			be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
     74	}
     75	if (tb[NFTA_COUNTER_BYTES]) {
     76		this_cpu->bytes =
     77			be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
     78	}
     79	preempt_enable();
     80	priv->counter = cpu_stats;
     81	return 0;
     82}
     83
     84static int nft_counter_obj_init(const struct nft_ctx *ctx,
     85				const struct nlattr * const tb[],
     86				struct nft_object *obj)
     87{
     88	struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
     89
     90	return nft_counter_do_init(tb, priv);
     91}
     92
     93static void nft_counter_do_destroy(struct nft_counter_percpu_priv *priv)
     94{
     95	free_percpu(priv->counter);
     96}
     97
     98static void nft_counter_obj_destroy(const struct nft_ctx *ctx,
     99				    struct nft_object *obj)
    100{
    101	struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
    102
    103	nft_counter_do_destroy(priv);
    104}
    105
    106static void nft_counter_reset(struct nft_counter_percpu_priv *priv,
    107			      struct nft_counter *total)
    108{
    109	struct nft_counter *this_cpu;
    110
    111	local_bh_disable();
    112	this_cpu = this_cpu_ptr(priv->counter);
    113	this_cpu->packets -= total->packets;
    114	this_cpu->bytes -= total->bytes;
    115	local_bh_enable();
    116}
    117
    118static void nft_counter_fetch(struct nft_counter_percpu_priv *priv,
    119			      struct nft_counter *total)
    120{
    121	struct nft_counter *this_cpu;
    122	const seqcount_t *myseq;
    123	u64 bytes, packets;
    124	unsigned int seq;
    125	int cpu;
    126
    127	memset(total, 0, sizeof(*total));
    128	for_each_possible_cpu(cpu) {
    129		myseq = per_cpu_ptr(&nft_counter_seq, cpu);
    130		this_cpu = per_cpu_ptr(priv->counter, cpu);
    131		do {
    132			seq	= read_seqcount_begin(myseq);
    133			bytes	= this_cpu->bytes;
    134			packets	= this_cpu->packets;
    135		} while (read_seqcount_retry(myseq, seq));
    136
    137		total->bytes	+= bytes;
    138		total->packets	+= packets;
    139	}
    140}
    141
    142static int nft_counter_do_dump(struct sk_buff *skb,
    143			       struct nft_counter_percpu_priv *priv,
    144			       bool reset)
    145{
    146	struct nft_counter total;
    147
    148	nft_counter_fetch(priv, &total);
    149
    150	if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes),
    151			 NFTA_COUNTER_PAD) ||
    152	    nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.packets),
    153			 NFTA_COUNTER_PAD))
    154		goto nla_put_failure;
    155
    156	if (reset)
    157		nft_counter_reset(priv, &total);
    158
    159	return 0;
    160
    161nla_put_failure:
    162	return -1;
    163}
    164
    165static int nft_counter_obj_dump(struct sk_buff *skb,
    166				struct nft_object *obj, bool reset)
    167{
    168	struct nft_counter_percpu_priv *priv = nft_obj_data(obj);
    169
    170	return nft_counter_do_dump(skb, priv, reset);
    171}
    172
    173static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
    174	[NFTA_COUNTER_PACKETS]	= { .type = NLA_U64 },
    175	[NFTA_COUNTER_BYTES]	= { .type = NLA_U64 },
    176};
    177
    178struct nft_object_type nft_counter_obj_type;
    179static const struct nft_object_ops nft_counter_obj_ops = {
    180	.type		= &nft_counter_obj_type,
    181	.size		= sizeof(struct nft_counter_percpu_priv),
    182	.eval		= nft_counter_obj_eval,
    183	.init		= nft_counter_obj_init,
    184	.destroy	= nft_counter_obj_destroy,
    185	.dump		= nft_counter_obj_dump,
    186};
    187
    188struct nft_object_type nft_counter_obj_type __read_mostly = {
    189	.type		= NFT_OBJECT_COUNTER,
    190	.ops		= &nft_counter_obj_ops,
    191	.maxattr	= NFTA_COUNTER_MAX,
    192	.policy		= nft_counter_policy,
    193	.owner		= THIS_MODULE,
    194};
    195
    196void nft_counter_eval(const struct nft_expr *expr, struct nft_regs *regs,
    197		      const struct nft_pktinfo *pkt)
    198{
    199	struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
    200
    201	nft_counter_do_eval(priv, regs, pkt);
    202}
    203
    204static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
    205{
    206	struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
    207
    208	return nft_counter_do_dump(skb, priv, false);
    209}
    210
    211static int nft_counter_init(const struct nft_ctx *ctx,
    212			    const struct nft_expr *expr,
    213			    const struct nlattr * const tb[])
    214{
    215	struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
    216
    217	return nft_counter_do_init(tb, priv);
    218}
    219
    220static void nft_counter_destroy(const struct nft_ctx *ctx,
    221				const struct nft_expr *expr)
    222{
    223	struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
    224
    225	nft_counter_do_destroy(priv);
    226}
    227
    228static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src)
    229{
    230	struct nft_counter_percpu_priv *priv = nft_expr_priv(src);
    231	struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst);
    232	struct nft_counter __percpu *cpu_stats;
    233	struct nft_counter *this_cpu;
    234	struct nft_counter total;
    235
    236	nft_counter_fetch(priv, &total);
    237
    238	cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_ATOMIC);
    239	if (cpu_stats == NULL)
    240		return -ENOMEM;
    241
    242	preempt_disable();
    243	this_cpu = this_cpu_ptr(cpu_stats);
    244	this_cpu->packets = total.packets;
    245	this_cpu->bytes = total.bytes;
    246	preempt_enable();
    247
    248	priv_clone->counter = cpu_stats;
    249	return 0;
    250}
    251
    252static int nft_counter_offload(struct nft_offload_ctx *ctx,
    253			       struct nft_flow_rule *flow,
    254			       const struct nft_expr *expr)
    255{
    256	/* No specific offload action is needed, but report success. */
    257	return 0;
    258}
    259
    260static void nft_counter_offload_stats(struct nft_expr *expr,
    261				      const struct flow_stats *stats)
    262{
    263	struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
    264	struct nft_counter *this_cpu;
    265	seqcount_t *myseq;
    266
    267	preempt_disable();
    268	this_cpu = this_cpu_ptr(priv->counter);
    269	myseq = this_cpu_ptr(&nft_counter_seq);
    270
    271	write_seqcount_begin(myseq);
    272	this_cpu->packets += stats->pkts;
    273	this_cpu->bytes += stats->bytes;
    274	write_seqcount_end(myseq);
    275	preempt_enable();
    276}
    277
    278void nft_counter_init_seqcount(void)
    279{
    280	int cpu;
    281
    282	for_each_possible_cpu(cpu)
    283		seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu));
    284}
    285
    286struct nft_expr_type nft_counter_type;
    287static const struct nft_expr_ops nft_counter_ops = {
    288	.type		= &nft_counter_type,
    289	.size		= NFT_EXPR_SIZE(sizeof(struct nft_counter_percpu_priv)),
    290	.eval		= nft_counter_eval,
    291	.init		= nft_counter_init,
    292	.destroy	= nft_counter_destroy,
    293	.destroy_clone	= nft_counter_destroy,
    294	.dump		= nft_counter_dump,
    295	.clone		= nft_counter_clone,
    296	.reduce		= NFT_REDUCE_READONLY,
    297	.offload	= nft_counter_offload,
    298	.offload_stats	= nft_counter_offload_stats,
    299};
    300
    301struct nft_expr_type nft_counter_type __read_mostly = {
    302	.name		= "counter",
    303	.ops		= &nft_counter_ops,
    304	.policy		= nft_counter_policy,
    305	.maxattr	= NFTA_COUNTER_MAX,
    306	.flags		= NFT_EXPR_STATEFUL,
    307	.owner		= THIS_MODULE,
    308};