From: Eric Dumazet <eduma...@google.com>

1) tbl->gc_thresh1, tbl->gc_thresh2, tbl->gc_thresh3 and tbl->gc_interval
   can be written from sysfs.

2) tbl->last_flush is read locklessly from neigh_alloc()

3) tbl->proxy_queue.qlen is read locklessly from neightbl_fill_info()

4) neightbl_fill_info() reads cpu stats that can be changed concurrently.

Fixes: c7fb64db001f ("[NETLINK]: Neighbour table configuration and statistics 
via rtnetlink")
Signed-off-by: Eric Dumazet <eduma...@google.com>
Link: https://lore.kernel.org/r/20231019122104.1448310-1-eduma...@google.com
Signed-off-by: Jakub Kicinski <k...@kernel.org>
(cherry picked from commit a9beb7e81bcb876615e1fbb3c07f3f9dba69831f)
https://virtuozzo.atlassian.net/browse/PSBM-153199
Signed-off-by: Alexander Atanasov <alexander.atana...@virtuozzo.com>
---
 net/core/neighbour.c | 64 +++++++++++++++++++++++---------------------
 1 file changed, 34 insertions(+), 30 deletions(-)

diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index b3517ca7da499..d0ad2aee068dc 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -253,7 +253,8 @@ bool neigh_remove_one(struct neighbour *ndel, struct 
neigh_table *tbl)
 
 static int neigh_forced_gc(struct neigh_table *tbl, struct ve_struct *ve)
 {
-       int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2;
+       int max_clean = atomic_read(&tbl->gc_entries) -
+                       READ_ONCE(tbl->gc_thresh2);
        unsigned long tref = jiffies - 5 * HZ;
        struct neighbour *n, *tmp;
        int shrunk = 0;
@@ -284,7 +285,7 @@ static int neigh_forced_gc(struct neigh_table *tbl, struct 
ve_struct *ve)
                }
        }
 
-       tbl->last_flush = jiffies;
+       WRITE_ONCE(tbl->last_flush, jiffies);
 
        write_unlock_bh(&tbl->lock);
 
@@ -480,7 +481,7 @@ static struct neighbour *neigh_alloc(struct neigh_table 
*tbl,
 {
        struct neighbour *n = NULL;
        unsigned long now = jiffies;
-       int entries, glob_entries;
+       int entries, glob_entries, gc_thresh3;
        atomic_t *cnt;
        struct ve_struct *ve = dev_net(dev)->owner_ve;
 
@@ -495,12 +496,13 @@ static struct neighbour *neigh_alloc(struct neigh_table 
*tbl,
        glob_entries = atomic_inc_return(&tbl->entries) - 1;
        cnt = get_perve_tbl_entries_counter(tbl, ve);
        entries = cnt ? atomic_inc_return(cnt) - 1 : glob_entries;
+       gc_thresh3 = READ_ONCE(tbl->gc_thresh3);
 
-       if (entries >= tbl->gc_thresh3 ||
-           (glob_entries >= tbl->gc_thresh2 &&
+       if (entries >= gc_thresh3 ||
+           (glob_entries >= READ_ONCE(tbl->gc_thresh2) &&
             time_after(now, tbl->last_flush + 5 * HZ))) {
                if (!neigh_forced_gc(tbl, ve) &&
-                   entries >= tbl->gc_thresh3) {
+                   entries >= gc_thresh3) {
                        net_info_ratelimited("%s: neighbor table overflow!\n",
                                             tbl->id);
                        NEIGH_CACHE_STAT_INC(tbl, table_fulls);
@@ -1025,13 +1027,14 @@ static void neigh_periodic_work(struct work_struct 
*work)
 
        if (time_after(jiffies, tbl->last_rand + 300 * HZ)) {
                struct neigh_parms *p;
-               tbl->last_rand = jiffies;
+
+               WRITE_ONCE(tbl->last_rand, jiffies);
                list_for_each_entry(p, &tbl->parms_list, list)
                        p->reachable_time =
                                neigh_rand_reach_time(NEIGH_VAR(p, 
BASE_REACHABLE_TIME));
        }
 
-       if (atomic_read(&tbl->entries) < tbl->gc_thresh1)
+       if (atomic_read(&tbl->entries) < READ_ONCE(tbl->gc_thresh1))
                goto out;
 
        for (i = 0 ; i < (1 << nht->hash_shift); i++) {
@@ -2232,15 +2235,16 @@ static int neightbl_fill_info(struct sk_buff *skb, 
struct neigh_table *tbl,
        ndtmsg->ndtm_pad2   = 0;
 
        if (nla_put_string(skb, NDTA_NAME, tbl->id) ||
-           nla_put_msecs(skb, NDTA_GC_INTERVAL, tbl->gc_interval, NDTA_PAD) ||
-           nla_put_u32(skb, NDTA_THRESH1, tbl->gc_thresh1) ||
-           nla_put_u32(skb, NDTA_THRESH2, tbl->gc_thresh2) ||
-           nla_put_u32(skb, NDTA_THRESH3, tbl->gc_thresh3))
+           nla_put_msecs(skb, NDTA_GC_INTERVAL, READ_ONCE(tbl->gc_interval),
+                         NDTA_PAD) ||
+           nla_put_u32(skb, NDTA_THRESH1, READ_ONCE(tbl->gc_thresh1)) ||
+           nla_put_u32(skb, NDTA_THRESH2, READ_ONCE(tbl->gc_thresh2)) ||
+           nla_put_u32(skb, NDTA_THRESH3, READ_ONCE(tbl->gc_thresh3)))
                goto nla_put_failure;
        {
                unsigned long now = jiffies;
-               long flush_delta = now - tbl->last_flush;
-               long rand_delta = now - tbl->last_rand;
+               long flush_delta = now - READ_ONCE(tbl->last_flush);
+               long rand_delta = now - READ_ONCE(tbl->last_rand);
                struct neigh_hash_table *nht;
                struct ndt_config ndc = {
                        .ndtc_key_len           = tbl->key_len,
@@ -2248,7 +2252,7 @@ static int neightbl_fill_info(struct sk_buff *skb, struct 
neigh_table *tbl,
                        .ndtc_entries           = atomic_read(&tbl->entries),
                        .ndtc_last_flush        = jiffies_to_msecs(flush_delta),
                        .ndtc_last_rand         = jiffies_to_msecs(rand_delta),
-                       .ndtc_proxy_qlen        = tbl->proxy_queue.qlen,
+                       .ndtc_proxy_qlen        = 
READ_ONCE(tbl->proxy_queue.qlen),
                };
 
                rcu_read_lock_bh();
@@ -2271,17 +2275,17 @@ static int neightbl_fill_info(struct sk_buff *skb, 
struct neigh_table *tbl,
                        struct neigh_statistics *st;
 
                        st = per_cpu_ptr(tbl->stats, cpu);
-                       ndst.ndts_allocs                += st->allocs;
-                       ndst.ndts_destroys              += st->destroys;
-                       ndst.ndts_hash_grows            += st->hash_grows;
-                       ndst.ndts_res_failed            += st->res_failed;
-                       ndst.ndts_lookups               += st->lookups;
-                       ndst.ndts_hits                  += st->hits;
-                       ndst.ndts_rcv_probes_mcast      += st->rcv_probes_mcast;
-                       ndst.ndts_rcv_probes_ucast      += st->rcv_probes_ucast;
-                       ndst.ndts_periodic_gc_runs      += st->periodic_gc_runs;
-                       ndst.ndts_forced_gc_runs        += st->forced_gc_runs;
-                       ndst.ndts_table_fulls           += st->table_fulls;
+                       ndst.ndts_allocs                += 
READ_ONCE(st->allocs);
+                       ndst.ndts_destroys              += 
READ_ONCE(st->destroys);
+                       ndst.ndts_hash_grows            += 
READ_ONCE(st->hash_grows);
+                       ndst.ndts_res_failed            += 
READ_ONCE(st->res_failed);
+                       ndst.ndts_lookups               += 
READ_ONCE(st->lookups);
+                       ndst.ndts_hits                  += READ_ONCE(st->hits);
+                       ndst.ndts_rcv_probes_mcast      += 
READ_ONCE(st->rcv_probes_mcast);
+                       ndst.ndts_rcv_probes_ucast      += 
READ_ONCE(st->rcv_probes_ucast);
+                       ndst.ndts_periodic_gc_runs      += 
READ_ONCE(st->periodic_gc_runs);
+                       ndst.ndts_forced_gc_runs        += 
READ_ONCE(st->forced_gc_runs);
+                       ndst.ndts_table_fulls           += 
READ_ONCE(st->table_fulls);
                }
 
                if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst,
@@ -2505,16 +2509,16 @@ static int neightbl_set(struct sk_buff *skb, struct 
nlmsghdr *nlh,
                goto errout_tbl_lock;
 
        if (tb[NDTA_THRESH1])
-               tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]);
+               WRITE_ONCE(tbl->gc_thresh1, nla_get_u32(tb[NDTA_THRESH1]));
 
        if (tb[NDTA_THRESH2])
-               tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]);
+               WRITE_ONCE(tbl->gc_thresh2, nla_get_u32(tb[NDTA_THRESH2]));
 
        if (tb[NDTA_THRESH3])
-               tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]);
+               WRITE_ONCE(tbl->gc_thresh3, nla_get_u32(tb[NDTA_THRESH3]));
 
        if (tb[NDTA_GC_INTERVAL])
-               tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]);
+               WRITE_ONCE(tbl->gc_interval, 
nla_get_msecs(tb[NDTA_GC_INTERVAL]));
 
        err = 0;
 
-- 
2.39.3

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to