cls_flow->rule = flow->rule;
 }
 
-static int nft_flow_offload_rule(struct nft_chain *chain,
-                                struct nft_rule *rule,
-                                struct nft_flow_rule *flow,
-                                enum flow_cls_command command)
+static int nft_flow_offload_cmd(const struct nft_chain *chain,
+                               const struct nft_rule *rule,
+                               struct nft_flow_rule *flow,
+                               enum flow_cls_command command,
+                               struct flow_cls_offload *cls_flow)
 {
        struct netlink_ext_ack extack = {};
-       struct flow_cls_offload cls_flow;
        struct nft_base_chain *basechain;
 
        if (!nft_is_base_chain(chain))
                return -EOPNOTSUPP;
 
        basechain = nft_base_chain(chain);
-       nft_flow_cls_offload_setup(&cls_flow, basechain, rule, flow, &extack,
+       nft_flow_cls_offload_setup(cls_flow, basechain, rule, flow, &extack,
                                   command);
 
-       return nft_setup_cb_call(TC_SETUP_CLSFLOWER, &cls_flow,
+       return nft_setup_cb_call(TC_SETUP_CLSFLOWER, cls_flow,
                                 &basechain->flow_block.cb_list);
 }
 
+static int nft_flow_offload_rule(const struct nft_chain *chain,
+                                struct nft_rule *rule,
+                                struct nft_flow_rule *flow,
+                                enum flow_cls_command command)
+{
+       struct flow_cls_offload cls_flow;
+
+       return nft_flow_offload_cmd(chain, rule, flow, command, &cls_flow);
+}
+
+int nft_flow_rule_stats(const struct nft_chain *chain,
+                       const struct nft_rule *rule)
+{
+       struct flow_cls_offload cls_flow = {};
+       struct nft_expr *expr, *next;
+       int err;
+
+       err = nft_flow_offload_cmd(chain, rule, NULL, FLOW_CLS_STATS,
+                                  &cls_flow);
+       if (err < 0)
+               return err;
+
+       nft_rule_for_each_expr(expr, next, rule) {
+               if (expr->ops->offload_stats)
+                       expr->ops->offload_stats(expr, &cls_flow.stats);
+       }
+
+       return 0;
+}
+
 static int nft_flow_offload_bind(struct flow_block_offload *bo,
                                 struct nft_base_chain *basechain)
 {
 
 #include <linux/netfilter.h>
 #include <linux/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_offload.h>
 
 struct nft_counter {
        s64             bytes;
        return 0;
 }
 
+static int nft_counter_offload(struct nft_offload_ctx *ctx,
+                              struct nft_flow_rule *flow,
+                              const struct nft_expr *expr)
+{
+       /* No specific offload action is needed, but report success. */
+       return 0;
+}
+
+static void nft_counter_offload_stats(struct nft_expr *expr,
+                                     const struct flow_stats *stats)
+{
+       struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
+       struct nft_counter *this_cpu;
+       seqcount_t *myseq;
+
+       preempt_disable();
+       this_cpu = this_cpu_ptr(priv->counter);
+       myseq = this_cpu_ptr(&nft_counter_seq);
+
+       write_seqcount_begin(myseq);
+       this_cpu->packets += stats->pkts;
+       this_cpu->bytes += stats->bytes;
+       write_seqcount_end(myseq);
+       preempt_enable();
+}
+
 static struct nft_expr_type nft_counter_type;
 static const struct nft_expr_ops nft_counter_ops = {
        .type           = &nft_counter_type,
        .destroy_clone  = nft_counter_destroy,
        .dump           = nft_counter_dump,
        .clone          = nft_counter_clone,
+       .offload        = nft_counter_offload,
+       .offload_stats  = nft_counter_offload_stats,
 };
 
 static struct nft_expr_type nft_counter_type __read_mostly = {