1549c243eSVlad Buslov // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2549c243eSVlad Buslov /* Copyright (c) 2020 Mellanox Technologies. */
3549c243eSVlad Buslov 
4549c243eSVlad Buslov #include <linux/refcount.h>
5549c243eSVlad Buslov #include <linux/list.h>
6549c243eSVlad Buslov #include <linux/rculist.h>
7549c243eSVlad Buslov #include <linux/rtnetlink.h>
8549c243eSVlad Buslov #include <linux/workqueue.h>
9549c243eSVlad Buslov #include <linux/spinlock.h>
10549c243eSVlad Buslov #include <linux/notifier.h>
11549c243eSVlad Buslov #include <net/netevent.h>
128fab0175SAlaa Hleihel #include <net/arp.h>
13549c243eSVlad Buslov #include "neigh.h"
14549c243eSVlad Buslov #include "tc.h"
15549c243eSVlad Buslov #include "en_rep.h"
16549c243eSVlad Buslov #include "fs_core.h"
17549c243eSVlad Buslov #include "diag/en_rep_tracepoint.h"
18549c243eSVlad Buslov 
mlx5e_rep_ipv6_interval(void)19549c243eSVlad Buslov static unsigned long mlx5e_rep_ipv6_interval(void)
20549c243eSVlad Buslov {
21549c243eSVlad Buslov 	if (IS_ENABLED(CONFIG_IPV6) && ipv6_stub->nd_tbl)
22549c243eSVlad Buslov 		return NEIGH_VAR(&ipv6_stub->nd_tbl->parms, DELAY_PROBE_TIME);
23549c243eSVlad Buslov 
24549c243eSVlad Buslov 	return ~0UL;
25549c243eSVlad Buslov }
26549c243eSVlad Buslov 
mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv * rpriv)27549c243eSVlad Buslov static void mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv *rpriv)
28549c243eSVlad Buslov {
29549c243eSVlad Buslov 	unsigned long ipv4_interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
30549c243eSVlad Buslov 	unsigned long ipv6_interval = mlx5e_rep_ipv6_interval();
31549c243eSVlad Buslov 	struct net_device *netdev = rpriv->netdev;
32549c243eSVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(netdev);
33549c243eSVlad Buslov 
34549c243eSVlad Buslov 	rpriv->neigh_update.min_interval = min_t(unsigned long, ipv6_interval, ipv4_interval);
35549c243eSVlad Buslov 	mlx5_fc_update_sampling_interval(priv->mdev, rpriv->neigh_update.min_interval);
36549c243eSVlad Buslov }
37549c243eSVlad Buslov 
mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv * priv)38549c243eSVlad Buslov void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv)
39549c243eSVlad Buslov {
40549c243eSVlad Buslov 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
41549c243eSVlad Buslov 	struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
42549c243eSVlad Buslov 
43549c243eSVlad Buslov 	mlx5_fc_queue_stats_work(priv->mdev,
44549c243eSVlad Buslov 				 &neigh_update->neigh_stats_work,
45549c243eSVlad Buslov 				 neigh_update->min_interval);
46549c243eSVlad Buslov }
47549c243eSVlad Buslov 
mlx5e_rep_neigh_entry_hold(struct mlx5e_neigh_hash_entry * nhe)48549c243eSVlad Buslov static bool mlx5e_rep_neigh_entry_hold(struct mlx5e_neigh_hash_entry *nhe)
49549c243eSVlad Buslov {
50549c243eSVlad Buslov 	return refcount_inc_not_zero(&nhe->refcnt);
51549c243eSVlad Buslov }
52549c243eSVlad Buslov 
53549c243eSVlad Buslov static void mlx5e_rep_neigh_entry_remove(struct mlx5e_neigh_hash_entry *nhe);
54549c243eSVlad Buslov 
mlx5e_rep_neigh_entry_release(struct mlx5e_neigh_hash_entry * nhe)55549c243eSVlad Buslov void mlx5e_rep_neigh_entry_release(struct mlx5e_neigh_hash_entry *nhe)
56549c243eSVlad Buslov {
57549c243eSVlad Buslov 	if (refcount_dec_and_test(&nhe->refcnt)) {
58549c243eSVlad Buslov 		mlx5e_rep_neigh_entry_remove(nhe);
59549c243eSVlad Buslov 		kfree_rcu(nhe, rcu);
60549c243eSVlad Buslov 	}
61549c243eSVlad Buslov }
62549c243eSVlad Buslov 
63549c243eSVlad Buslov static struct mlx5e_neigh_hash_entry *
mlx5e_get_next_nhe(struct mlx5e_rep_priv * rpriv,struct mlx5e_neigh_hash_entry * nhe)64549c243eSVlad Buslov mlx5e_get_next_nhe(struct mlx5e_rep_priv *rpriv,
65549c243eSVlad Buslov 		   struct mlx5e_neigh_hash_entry *nhe)
66549c243eSVlad Buslov {
67549c243eSVlad Buslov 	struct mlx5e_neigh_hash_entry *next = NULL;
68549c243eSVlad Buslov 
69549c243eSVlad Buslov 	rcu_read_lock();
70549c243eSVlad Buslov 
71549c243eSVlad Buslov 	for (next = nhe ?
72549c243eSVlad Buslov 		     list_next_or_null_rcu(&rpriv->neigh_update.neigh_list,
73549c243eSVlad Buslov 					   &nhe->neigh_list,
74549c243eSVlad Buslov 					   struct mlx5e_neigh_hash_entry,
75549c243eSVlad Buslov 					   neigh_list) :
76549c243eSVlad Buslov 		     list_first_or_null_rcu(&rpriv->neigh_update.neigh_list,
77549c243eSVlad Buslov 					    struct mlx5e_neigh_hash_entry,
78549c243eSVlad Buslov 					    neigh_list);
79549c243eSVlad Buslov 	     next;
80549c243eSVlad Buslov 	     next = list_next_or_null_rcu(&rpriv->neigh_update.neigh_list,
81549c243eSVlad Buslov 					  &next->neigh_list,
82549c243eSVlad Buslov 					  struct mlx5e_neigh_hash_entry,
83549c243eSVlad Buslov 					  neigh_list))
84549c243eSVlad Buslov 		if (mlx5e_rep_neigh_entry_hold(next))
85549c243eSVlad Buslov 			break;
86549c243eSVlad Buslov 
87549c243eSVlad Buslov 	rcu_read_unlock();
88549c243eSVlad Buslov 
89549c243eSVlad Buslov 	if (nhe)
90549c243eSVlad Buslov 		mlx5e_rep_neigh_entry_release(nhe);
91549c243eSVlad Buslov 
92549c243eSVlad Buslov 	return next;
93549c243eSVlad Buslov }
94549c243eSVlad Buslov 
mlx5e_rep_neigh_stats_work(struct work_struct * work)95549c243eSVlad Buslov static void mlx5e_rep_neigh_stats_work(struct work_struct *work)
96549c243eSVlad Buslov {
97549c243eSVlad Buslov 	struct mlx5e_rep_priv *rpriv = container_of(work, struct mlx5e_rep_priv,
98549c243eSVlad Buslov 						    neigh_update.neigh_stats_work.work);
99549c243eSVlad Buslov 	struct net_device *netdev = rpriv->netdev;
100549c243eSVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(netdev);
101549c243eSVlad Buslov 	struct mlx5e_neigh_hash_entry *nhe = NULL;
102549c243eSVlad Buslov 
103549c243eSVlad Buslov 	rtnl_lock();
104549c243eSVlad Buslov 	if (!list_empty(&rpriv->neigh_update.neigh_list))
105549c243eSVlad Buslov 		mlx5e_rep_queue_neigh_stats_work(priv);
106549c243eSVlad Buslov 
107549c243eSVlad Buslov 	while ((nhe = mlx5e_get_next_nhe(rpriv, nhe)) != NULL)
108549c243eSVlad Buslov 		mlx5e_tc_update_neigh_used_value(nhe);
109549c243eSVlad Buslov 
110549c243eSVlad Buslov 	rtnl_unlock();
111549c243eSVlad Buslov }
112549c243eSVlad Buslov 
1131253935aSVlad Buslov struct neigh_update_work {
1141253935aSVlad Buslov 	struct work_struct work;
1151253935aSVlad Buslov 	struct neighbour *n;
1161253935aSVlad Buslov 	struct mlx5e_neigh_hash_entry *nhe;
1171253935aSVlad Buslov };
1181253935aSVlad Buslov 
mlx5e_release_neigh_update_work(struct neigh_update_work * update_work)1191253935aSVlad Buslov static void mlx5e_release_neigh_update_work(struct neigh_update_work *update_work)
1201253935aSVlad Buslov {
1211253935aSVlad Buslov 	neigh_release(update_work->n);
1221253935aSVlad Buslov 	mlx5e_rep_neigh_entry_release(update_work->nhe);
1231253935aSVlad Buslov 	kfree(update_work);
1241253935aSVlad Buslov }
1251253935aSVlad Buslov 
mlx5e_rep_neigh_update(struct work_struct * work)126549c243eSVlad Buslov static void mlx5e_rep_neigh_update(struct work_struct *work)
127549c243eSVlad Buslov {
1281253935aSVlad Buslov 	struct neigh_update_work *update_work = container_of(work, struct neigh_update_work,
1291253935aSVlad Buslov 							     work);
1301253935aSVlad Buslov 	struct mlx5e_neigh_hash_entry *nhe = update_work->nhe;
1311253935aSVlad Buslov 	struct neighbour *n = update_work->n;
132*fb1a3132SVlad Buslov 	struct mlx5e_encap_entry *e = NULL;
1332221d954SVlad Buslov 	bool neigh_connected, same_dev;
134549c243eSVlad Buslov 	unsigned char ha[ETH_ALEN];
135549c243eSVlad Buslov 	u8 nud_state, dead;
136549c243eSVlad Buslov 
137549c243eSVlad Buslov 	rtnl_lock();
138549c243eSVlad Buslov 
139549c243eSVlad Buslov 	/* If these parameters are changed after we release the lock,
140549c243eSVlad Buslov 	 * we'll receive another event letting us know about it.
141549c243eSVlad Buslov 	 * We use this lock to avoid inconsistency between the neigh validity
142549c243eSVlad Buslov 	 * and it's hw address.
143549c243eSVlad Buslov 	 */
144549c243eSVlad Buslov 	read_lock_bh(&n->lock);
145549c243eSVlad Buslov 	memcpy(ha, n->ha, ETH_ALEN);
146549c243eSVlad Buslov 	nud_state = n->nud_state;
147549c243eSVlad Buslov 	dead = n->dead;
1482221d954SVlad Buslov 	same_dev = READ_ONCE(nhe->neigh_dev) == n->dev;
149549c243eSVlad Buslov 	read_unlock_bh(&n->lock);
150549c243eSVlad Buslov 
151549c243eSVlad Buslov 	neigh_connected = (nud_state & NUD_VALID) && !dead;
152549c243eSVlad Buslov 
153549c243eSVlad Buslov 	trace_mlx5e_rep_neigh_update(nhe, ha, neigh_connected);
154549c243eSVlad Buslov 
1552221d954SVlad Buslov 	if (!same_dev)
1562221d954SVlad Buslov 		goto out;
1572221d954SVlad Buslov 
158*fb1a3132SVlad Buslov 	/* mlx5e_get_next_init_encap() releases previous encap before returning
159*fb1a3132SVlad Buslov 	 * the next one.
160*fb1a3132SVlad Buslov 	 */
161*fb1a3132SVlad Buslov 	while ((e = mlx5e_get_next_init_encap(nhe, e)) != NULL)
162*fb1a3132SVlad Buslov 		mlx5e_rep_update_flows(netdev_priv(e->out_dev), e, neigh_connected, ha);
163549c243eSVlad Buslov 
1642221d954SVlad Buslov out:
165549c243eSVlad Buslov 	rtnl_unlock();
1661253935aSVlad Buslov 	mlx5e_release_neigh_update_work(update_work);
167549c243eSVlad Buslov }
168549c243eSVlad Buslov 
mlx5e_alloc_neigh_update_work(struct mlx5e_priv * priv,struct neighbour * n)1691253935aSVlad Buslov static struct neigh_update_work *mlx5e_alloc_neigh_update_work(struct mlx5e_priv *priv,
170549c243eSVlad Buslov 							       struct neighbour *n)
171549c243eSVlad Buslov {
1721253935aSVlad Buslov 	struct neigh_update_work *update_work;
1731253935aSVlad Buslov 	struct mlx5e_neigh_hash_entry *nhe;
1741253935aSVlad Buslov 	struct mlx5e_neigh m_neigh = {};
175549c243eSVlad Buslov 
1761253935aSVlad Buslov 	update_work = kzalloc(sizeof(*update_work), GFP_ATOMIC);
1771253935aSVlad Buslov 	if (WARN_ON(!update_work))
1781253935aSVlad Buslov 		return NULL;
179549c243eSVlad Buslov 
1801253935aSVlad Buslov 	m_neigh.family = n->ops->family;
1811253935aSVlad Buslov 	memcpy(&m_neigh.dst_ip, n->primary_key, n->tbl->key_len);
1821253935aSVlad Buslov 
1831253935aSVlad Buslov 	/* Obtain reference to nhe as last step in order not to release it in
1841253935aSVlad Buslov 	 * atomic context.
1851253935aSVlad Buslov 	 */
1861253935aSVlad Buslov 	rcu_read_lock();
1871253935aSVlad Buslov 	nhe = mlx5e_rep_neigh_entry_lookup(priv, &m_neigh);
1881253935aSVlad Buslov 	rcu_read_unlock();
1891253935aSVlad Buslov 	if (!nhe) {
1901253935aSVlad Buslov 		kfree(update_work);
1911253935aSVlad Buslov 		return NULL;
192549c243eSVlad Buslov 	}
1931253935aSVlad Buslov 
1941253935aSVlad Buslov 	INIT_WORK(&update_work->work, mlx5e_rep_neigh_update);
1951253935aSVlad Buslov 	neigh_hold(n);
1961253935aSVlad Buslov 	update_work->n = n;
1971253935aSVlad Buslov 	update_work->nhe = nhe;
1981253935aSVlad Buslov 
1991253935aSVlad Buslov 	return update_work;
200549c243eSVlad Buslov }
201549c243eSVlad Buslov 
mlx5e_rep_netevent_event(struct notifier_block * nb,unsigned long event,void * ptr)202549c243eSVlad Buslov static int mlx5e_rep_netevent_event(struct notifier_block *nb,
203549c243eSVlad Buslov 				    unsigned long event, void *ptr)
204549c243eSVlad Buslov {
205549c243eSVlad Buslov 	struct mlx5e_rep_priv *rpriv = container_of(nb, struct mlx5e_rep_priv,
206549c243eSVlad Buslov 						    neigh_update.netevent_nb);
207549c243eSVlad Buslov 	struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
208549c243eSVlad Buslov 	struct net_device *netdev = rpriv->netdev;
209549c243eSVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(netdev);
210549c243eSVlad Buslov 	struct mlx5e_neigh_hash_entry *nhe = NULL;
2111253935aSVlad Buslov 	struct neigh_update_work *update_work;
212549c243eSVlad Buslov 	struct neigh_parms *p;
213549c243eSVlad Buslov 	struct neighbour *n;
214549c243eSVlad Buslov 	bool found = false;
215549c243eSVlad Buslov 
216549c243eSVlad Buslov 	switch (event) {
217549c243eSVlad Buslov 	case NETEVENT_NEIGH_UPDATE:
218549c243eSVlad Buslov 		n = ptr;
219549c243eSVlad Buslov #if IS_ENABLED(CONFIG_IPV6)
220549c243eSVlad Buslov 		if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl)
221549c243eSVlad Buslov #else
222549c243eSVlad Buslov 		if (n->tbl != &arp_tbl)
223549c243eSVlad Buslov #endif
224549c243eSVlad Buslov 			return NOTIFY_DONE;
225549c243eSVlad Buslov 
2261253935aSVlad Buslov 		update_work = mlx5e_alloc_neigh_update_work(priv, n);
2271253935aSVlad Buslov 		if (!update_work)
228549c243eSVlad Buslov 			return NOTIFY_DONE;
229549c243eSVlad Buslov 
2301253935aSVlad Buslov 		queue_work(priv->wq, &update_work->work);
231549c243eSVlad Buslov 		break;
232549c243eSVlad Buslov 
233549c243eSVlad Buslov 	case NETEVENT_DELAY_PROBE_TIME_UPDATE:
234549c243eSVlad Buslov 		p = ptr;
235549c243eSVlad Buslov 
236549c243eSVlad Buslov 		/* We check the device is present since we don't care about
237549c243eSVlad Buslov 		 * changes in the default table, we only care about changes
238549c243eSVlad Buslov 		 * done per device delay prob time parameter.
239549c243eSVlad Buslov 		 */
240549c243eSVlad Buslov #if IS_ENABLED(CONFIG_IPV6)
241549c243eSVlad Buslov 		if (!p->dev || (p->tbl != ipv6_stub->nd_tbl && p->tbl != &arp_tbl))
242549c243eSVlad Buslov #else
243549c243eSVlad Buslov 		if (!p->dev || p->tbl != &arp_tbl)
244549c243eSVlad Buslov #endif
245549c243eSVlad Buslov 			return NOTIFY_DONE;
246549c243eSVlad Buslov 
247549c243eSVlad Buslov 		rcu_read_lock();
248549c243eSVlad Buslov 		list_for_each_entry_rcu(nhe, &neigh_update->neigh_list,
249549c243eSVlad Buslov 					neigh_list) {
2502221d954SVlad Buslov 			if (p->dev == READ_ONCE(nhe->neigh_dev)) {
251549c243eSVlad Buslov 				found = true;
252549c243eSVlad Buslov 				break;
253549c243eSVlad Buslov 			}
254549c243eSVlad Buslov 		}
255549c243eSVlad Buslov 		rcu_read_unlock();
256549c243eSVlad Buslov 		if (!found)
257549c243eSVlad Buslov 			return NOTIFY_DONE;
258549c243eSVlad Buslov 
259549c243eSVlad Buslov 		neigh_update->min_interval = min_t(unsigned long,
260549c243eSVlad Buslov 						   NEIGH_VAR(p, DELAY_PROBE_TIME),
261549c243eSVlad Buslov 						   neigh_update->min_interval);
262549c243eSVlad Buslov 		mlx5_fc_update_sampling_interval(priv->mdev,
263549c243eSVlad Buslov 						 neigh_update->min_interval);
264549c243eSVlad Buslov 		break;
265549c243eSVlad Buslov 	}
266549c243eSVlad Buslov 	return NOTIFY_DONE;
267549c243eSVlad Buslov }
268549c243eSVlad Buslov 
269549c243eSVlad Buslov static const struct rhashtable_params mlx5e_neigh_ht_params = {
270549c243eSVlad Buslov 	.head_offset = offsetof(struct mlx5e_neigh_hash_entry, rhash_node),
271549c243eSVlad Buslov 	.key_offset = offsetof(struct mlx5e_neigh_hash_entry, m_neigh),
272549c243eSVlad Buslov 	.key_len = sizeof(struct mlx5e_neigh),
273549c243eSVlad Buslov 	.automatic_shrinking = true,
274549c243eSVlad Buslov };
275549c243eSVlad Buslov 
mlx5e_rep_neigh_init(struct mlx5e_rep_priv * rpriv)276549c243eSVlad Buslov int mlx5e_rep_neigh_init(struct mlx5e_rep_priv *rpriv)
277549c243eSVlad Buslov {
278549c243eSVlad Buslov 	struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
279549c243eSVlad Buslov 	int err;
280549c243eSVlad Buslov 
281549c243eSVlad Buslov 	err = rhashtable_init(&neigh_update->neigh_ht, &mlx5e_neigh_ht_params);
282549c243eSVlad Buslov 	if (err)
2836b424e13SRoi Dayan 		goto out_err;
284549c243eSVlad Buslov 
285549c243eSVlad Buslov 	INIT_LIST_HEAD(&neigh_update->neigh_list);
286549c243eSVlad Buslov 	mutex_init(&neigh_update->encap_lock);
287549c243eSVlad Buslov 	INIT_DELAYED_WORK(&neigh_update->neigh_stats_work,
288549c243eSVlad Buslov 			  mlx5e_rep_neigh_stats_work);
289549c243eSVlad Buslov 	mlx5e_rep_neigh_update_init_interval(rpriv);
290549c243eSVlad Buslov 
2916b424e13SRoi Dayan 	neigh_update->netevent_nb.notifier_call = mlx5e_rep_netevent_event;
2926b424e13SRoi Dayan 	err = register_netevent_notifier(&neigh_update->netevent_nb);
293549c243eSVlad Buslov 	if (err)
2946b424e13SRoi Dayan 		goto out_notifier;
295549c243eSVlad Buslov 	return 0;
296549c243eSVlad Buslov 
2976b424e13SRoi Dayan out_notifier:
2986b424e13SRoi Dayan 	neigh_update->netevent_nb.notifier_call = NULL;
299549c243eSVlad Buslov 	rhashtable_destroy(&neigh_update->neigh_ht);
3006b424e13SRoi Dayan out_err:
3016b424e13SRoi Dayan 	netdev_warn(rpriv->netdev,
3026b424e13SRoi Dayan 		    "Failed to initialize neighbours handling for vport %d\n",
3036b424e13SRoi Dayan 		    rpriv->rep->vport);
304549c243eSVlad Buslov 	return err;
305549c243eSVlad Buslov }
306549c243eSVlad Buslov 
mlx5e_rep_neigh_cleanup(struct mlx5e_rep_priv * rpriv)307549c243eSVlad Buslov void mlx5e_rep_neigh_cleanup(struct mlx5e_rep_priv *rpriv)
308549c243eSVlad Buslov {
309549c243eSVlad Buslov 	struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
310549c243eSVlad Buslov 	struct mlx5e_priv *priv = netdev_priv(rpriv->netdev);
311549c243eSVlad Buslov 
3126b424e13SRoi Dayan 	if (!rpriv->neigh_update.netevent_nb.notifier_call)
3136b424e13SRoi Dayan 		return;
3146b424e13SRoi Dayan 
315549c243eSVlad Buslov 	unregister_netevent_notifier(&neigh_update->netevent_nb);
316549c243eSVlad Buslov 
317549c243eSVlad Buslov 	flush_workqueue(priv->wq); /* flush neigh update works */
318549c243eSVlad Buslov 
319549c243eSVlad Buslov 	cancel_delayed_work_sync(&rpriv->neigh_update.neigh_stats_work);
320549c243eSVlad Buslov 
321549c243eSVlad Buslov 	mutex_destroy(&neigh_update->encap_lock);
322549c243eSVlad Buslov 	rhashtable_destroy(&neigh_update->neigh_ht);
323549c243eSVlad Buslov }
324549c243eSVlad Buslov 
mlx5e_rep_neigh_entry_insert(struct mlx5e_priv * priv,struct mlx5e_neigh_hash_entry * nhe)325549c243eSVlad Buslov static int mlx5e_rep_neigh_entry_insert(struct mlx5e_priv *priv,
326549c243eSVlad Buslov 					struct mlx5e_neigh_hash_entry *nhe)
327549c243eSVlad Buslov {
328549c243eSVlad Buslov 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
329549c243eSVlad Buslov 	int err;
330549c243eSVlad Buslov 
331549c243eSVlad Buslov 	err = rhashtable_insert_fast(&rpriv->neigh_update.neigh_ht,
332549c243eSVlad Buslov 				     &nhe->rhash_node,
333549c243eSVlad Buslov 				     mlx5e_neigh_ht_params);
334549c243eSVlad Buslov 	if (err)
335549c243eSVlad Buslov 		return err;
336549c243eSVlad Buslov 
337549c243eSVlad Buslov 	list_add_rcu(&nhe->neigh_list, &rpriv->neigh_update.neigh_list);
338549c243eSVlad Buslov 
339549c243eSVlad Buslov 	return err;
340549c243eSVlad Buslov }
341549c243eSVlad Buslov 
mlx5e_rep_neigh_entry_remove(struct mlx5e_neigh_hash_entry * nhe)342549c243eSVlad Buslov static void mlx5e_rep_neigh_entry_remove(struct mlx5e_neigh_hash_entry *nhe)
343549c243eSVlad Buslov {
344549c243eSVlad Buslov 	struct mlx5e_rep_priv *rpriv = nhe->priv->ppriv;
345549c243eSVlad Buslov 
346549c243eSVlad Buslov 	mutex_lock(&rpriv->neigh_update.encap_lock);
347549c243eSVlad Buslov 
348549c243eSVlad Buslov 	list_del_rcu(&nhe->neigh_list);
349549c243eSVlad Buslov 
350549c243eSVlad Buslov 	rhashtable_remove_fast(&rpriv->neigh_update.neigh_ht,
351549c243eSVlad Buslov 			       &nhe->rhash_node,
352549c243eSVlad Buslov 			       mlx5e_neigh_ht_params);
353549c243eSVlad Buslov 	mutex_unlock(&rpriv->neigh_update.encap_lock);
354549c243eSVlad Buslov }
355549c243eSVlad Buslov 
356549c243eSVlad Buslov /* This function must only be called under the representor's encap_lock or
357549c243eSVlad Buslov  * inside rcu read lock section.
358549c243eSVlad Buslov  */
359549c243eSVlad Buslov struct mlx5e_neigh_hash_entry *
mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv * priv,struct mlx5e_neigh * m_neigh)360549c243eSVlad Buslov mlx5e_rep_neigh_entry_lookup(struct mlx5e_priv *priv,
361549c243eSVlad Buslov 			     struct mlx5e_neigh *m_neigh)
362549c243eSVlad Buslov {
363549c243eSVlad Buslov 	struct mlx5e_rep_priv *rpriv = priv->ppriv;
364549c243eSVlad Buslov 	struct mlx5e_neigh_update_table *neigh_update = &rpriv->neigh_update;
365549c243eSVlad Buslov 	struct mlx5e_neigh_hash_entry *nhe;
366549c243eSVlad Buslov 
367549c243eSVlad Buslov 	nhe = rhashtable_lookup_fast(&neigh_update->neigh_ht, m_neigh,
368549c243eSVlad Buslov 				     mlx5e_neigh_ht_params);
369549c243eSVlad Buslov 	return nhe && mlx5e_rep_neigh_entry_hold(nhe) ? nhe : NULL;
370549c243eSVlad Buslov }
371549c243eSVlad Buslov 
mlx5e_rep_neigh_entry_create(struct mlx5e_priv * priv,struct mlx5e_neigh * m_neigh,struct net_device * neigh_dev,struct mlx5e_neigh_hash_entry ** nhe)372549c243eSVlad Buslov int mlx5e_rep_neigh_entry_create(struct mlx5e_priv *priv,
3732221d954SVlad Buslov 				 struct mlx5e_neigh *m_neigh,
3742221d954SVlad Buslov 				 struct net_device *neigh_dev,
375549c243eSVlad Buslov 				 struct mlx5e_neigh_hash_entry **nhe)
376549c243eSVlad Buslov {
377549c243eSVlad Buslov 	int err;
378549c243eSVlad Buslov 
379549c243eSVlad Buslov 	*nhe = kzalloc(sizeof(**nhe), GFP_KERNEL);
380549c243eSVlad Buslov 	if (!*nhe)
381549c243eSVlad Buslov 		return -ENOMEM;
382549c243eSVlad Buslov 
383549c243eSVlad Buslov 	(*nhe)->priv = priv;
3842221d954SVlad Buslov 	memcpy(&(*nhe)->m_neigh, m_neigh, sizeof(*m_neigh));
385549c243eSVlad Buslov 	spin_lock_init(&(*nhe)->encap_list_lock);
386549c243eSVlad Buslov 	INIT_LIST_HEAD(&(*nhe)->encap_list);
387549c243eSVlad Buslov 	refcount_set(&(*nhe)->refcnt, 1);
3882221d954SVlad Buslov 	WRITE_ONCE((*nhe)->neigh_dev, neigh_dev);
389549c243eSVlad Buslov 
390549c243eSVlad Buslov 	err = mlx5e_rep_neigh_entry_insert(priv, *nhe);
391549c243eSVlad Buslov 	if (err)
392549c243eSVlad Buslov 		goto out_free;
393549c243eSVlad Buslov 	return 0;
394549c243eSVlad Buslov 
395549c243eSVlad Buslov out_free:
396549c243eSVlad Buslov 	kfree(*nhe);
397549c243eSVlad Buslov 	return err;
398549c243eSVlad Buslov }
399