1c1e329ebSShannon Nelson // SPDX-License-Identifier: GPL-2.0
2c1e329ebSShannon Nelson /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
3c1e329ebSShannon Nelson 
4c1e329ebSShannon Nelson #include <linux/netdevice.h>
5555cd19dSShannon Nelson #include <linux/dynamic_debug.h>
6c1e329ebSShannon Nelson #include <linux/etherdevice.h>
7969f8439SShannon Nelson #include <linux/list.h>
8c1e329ebSShannon Nelson 
9c1e329ebSShannon Nelson #include "ionic.h"
10c1e329ebSShannon Nelson #include "ionic_lif.h"
11c1e329ebSShannon Nelson #include "ionic_rx_filter.h"
12c1e329ebSShannon Nelson 
13c1e329ebSShannon Nelson void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f)
14c1e329ebSShannon Nelson {
15c1e329ebSShannon Nelson 	struct device *dev = lif->ionic->dev;
16c1e329ebSShannon Nelson 
17c1e329ebSShannon Nelson 	hlist_del(&f->by_id);
18c1e329ebSShannon Nelson 	hlist_del(&f->by_hash);
19c1e329ebSShannon Nelson 	devm_kfree(dev, f);
20c1e329ebSShannon Nelson }
21c1e329ebSShannon Nelson 
227e4d4759SShannon Nelson void ionic_rx_filter_replay(struct ionic_lif *lif)
23c1e329ebSShannon Nelson {
247e4d4759SShannon Nelson 	struct ionic_rx_filter_add_cmd *ac;
25cc4428c4SShannon Nelson 	struct hlist_head new_id_list;
267e4d4759SShannon Nelson 	struct ionic_admin_ctx ctx;
277e4d4759SShannon Nelson 	struct ionic_rx_filter *f;
287e4d4759SShannon Nelson 	struct hlist_head *head;
297e4d4759SShannon Nelson 	struct hlist_node *tmp;
30cc4428c4SShannon Nelson 	unsigned int key;
317e4d4759SShannon Nelson 	unsigned int i;
322c0df9f9SShannon Nelson 	int err;
33c1e329ebSShannon Nelson 
34cc4428c4SShannon Nelson 	INIT_HLIST_HEAD(&new_id_list);
357e4d4759SShannon Nelson 	ac = &ctx.cmd.rx_filter_add;
367e4d4759SShannon Nelson 
377e4d4759SShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
387e4d4759SShannon Nelson 		head = &lif->rx_filters.by_id[i];
397e4d4759SShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id) {
407e4d4759SShannon Nelson 			ctx.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work);
417e4d4759SShannon Nelson 			memcpy(ac, &f->cmd, sizeof(f->cmd));
427e4d4759SShannon Nelson 			dev_dbg(&lif->netdev->dev, "replay filter command:\n");
437e4d4759SShannon Nelson 			dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
447e4d4759SShannon Nelson 					 &ctx.cmd, sizeof(ctx.cmd), true);
457e4d4759SShannon Nelson 
467e4d4759SShannon Nelson 			err = ionic_adminq_post_wait(lif, &ctx);
477e4d4759SShannon Nelson 			if (err) {
487e4d4759SShannon Nelson 				switch (le16_to_cpu(ac->match)) {
497e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_VLAN:
507e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d\n",
517e4d4759SShannon Nelson 						    err,
527e4d4759SShannon Nelson 						    le16_to_cpu(ac->vlan.vlan));
537e4d4759SShannon Nelson 					break;
547e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_MAC:
557e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: mac %pM\n",
567e4d4759SShannon Nelson 						    err, ac->mac.addr);
577e4d4759SShannon Nelson 					break;
587e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_MAC_VLAN:
597e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d mac %pM\n",
607e4d4759SShannon Nelson 						    err,
617e4d4759SShannon Nelson 						    le16_to_cpu(ac->vlan.vlan),
627e4d4759SShannon Nelson 						    ac->mac.addr);
637e4d4759SShannon Nelson 					break;
647e4d4759SShannon Nelson 				}
65cc4428c4SShannon Nelson 				spin_lock_bh(&lif->rx_filters.lock);
66cc4428c4SShannon Nelson 				ionic_rx_filter_free(lif, f);
67cc4428c4SShannon Nelson 				spin_unlock_bh(&lif->rx_filters.lock);
68cc4428c4SShannon Nelson 
69cc4428c4SShannon Nelson 				continue;
70cc4428c4SShannon Nelson 			}
71cc4428c4SShannon Nelson 
72cc4428c4SShannon Nelson 			/* remove from old id list, save new id in tmp list */
73cc4428c4SShannon Nelson 			spin_lock_bh(&lif->rx_filters.lock);
74cc4428c4SShannon Nelson 			hlist_del(&f->by_id);
75cc4428c4SShannon Nelson 			spin_unlock_bh(&lif->rx_filters.lock);
76cc4428c4SShannon Nelson 			f->filter_id = le32_to_cpu(ctx.comp.rx_filter_add.filter_id);
77cc4428c4SShannon Nelson 			hlist_add_head(&f->by_id, &new_id_list);
787e4d4759SShannon Nelson 		}
797e4d4759SShannon Nelson 	}
80cc4428c4SShannon Nelson 
81cc4428c4SShannon Nelson 	/* rebuild the by_id hash lists with the new filter ids */
82cc4428c4SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
83cc4428c4SShannon Nelson 	hlist_for_each_entry_safe(f, tmp, &new_id_list, by_id) {
84cc4428c4SShannon Nelson 		key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
85cc4428c4SShannon Nelson 		head = &lif->rx_filters.by_id[key];
86cc4428c4SShannon Nelson 		hlist_add_head(&f->by_id, head);
877e4d4759SShannon Nelson 	}
88cc4428c4SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
89c1e329ebSShannon Nelson }
90c1e329ebSShannon Nelson 
91c1e329ebSShannon Nelson int ionic_rx_filters_init(struct ionic_lif *lif)
92c1e329ebSShannon Nelson {
93c1e329ebSShannon Nelson 	unsigned int i;
94c1e329ebSShannon Nelson 
95c1e329ebSShannon Nelson 	spin_lock_init(&lif->rx_filters.lock);
96c1e329ebSShannon Nelson 
97cbec2153SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
98c1e329ebSShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
99c1e329ebSShannon Nelson 		INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
100c1e329ebSShannon Nelson 		INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
101c1e329ebSShannon Nelson 	}
102cbec2153SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
103c1e329ebSShannon Nelson 
104c1e329ebSShannon Nelson 	return 0;
105c1e329ebSShannon Nelson }
106c1e329ebSShannon Nelson 
107c1e329ebSShannon Nelson void ionic_rx_filters_deinit(struct ionic_lif *lif)
108c1e329ebSShannon Nelson {
109c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
110c1e329ebSShannon Nelson 	struct hlist_head *head;
111c1e329ebSShannon Nelson 	struct hlist_node *tmp;
112c1e329ebSShannon Nelson 	unsigned int i;
113c1e329ebSShannon Nelson 
114cbec2153SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
115c1e329ebSShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
116c1e329ebSShannon Nelson 		head = &lif->rx_filters.by_id[i];
117c1e329ebSShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id)
118c1e329ebSShannon Nelson 			ionic_rx_filter_free(lif, f);
119c1e329ebSShannon Nelson 	}
120cbec2153SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
121c1e329ebSShannon Nelson }
122c1e329ebSShannon Nelson 
123c1e329ebSShannon Nelson int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
124969f8439SShannon Nelson 			 u32 hash, struct ionic_admin_ctx *ctx,
125969f8439SShannon Nelson 			 enum ionic_filter_state state)
126c1e329ebSShannon Nelson {
127c1e329ebSShannon Nelson 	struct device *dev = lif->ionic->dev;
128c1e329ebSShannon Nelson 	struct ionic_rx_filter_add_cmd *ac;
129969f8439SShannon Nelson 	struct ionic_rx_filter *f = NULL;
130c1e329ebSShannon Nelson 	struct hlist_head *head;
131c1e329ebSShannon Nelson 	unsigned int key;
132c1e329ebSShannon Nelson 
133c1e329ebSShannon Nelson 	ac = &ctx->cmd.rx_filter_add;
134c1e329ebSShannon Nelson 
135c1e329ebSShannon Nelson 	switch (le16_to_cpu(ac->match)) {
136c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
137c1e329ebSShannon Nelson 		key = le16_to_cpu(ac->vlan.vlan);
138969f8439SShannon Nelson 		f = ionic_rx_filter_by_vlan(lif, le16_to_cpu(ac->vlan.vlan));
139c1e329ebSShannon Nelson 		break;
140c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
141c1e329ebSShannon Nelson 		key = *(u32 *)ac->mac.addr;
142969f8439SShannon Nelson 		f = ionic_rx_filter_by_addr(lif, ac->mac.addr);
143c1e329ebSShannon Nelson 		break;
144c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC_VLAN:
145c1e329ebSShannon Nelson 		key = le16_to_cpu(ac->mac_vlan.vlan);
146c1e329ebSShannon Nelson 		break;
147ab470bbeSShannon Nelson 	case IONIC_RX_FILTER_STEER_PKTCLASS:
148ab470bbeSShannon Nelson 		key = 0;
149ab470bbeSShannon Nelson 		break;
150c1e329ebSShannon Nelson 	default:
151c1e329ebSShannon Nelson 		return -EINVAL;
152c1e329ebSShannon Nelson 	}
153c1e329ebSShannon Nelson 
154969f8439SShannon Nelson 	if (f) {
155969f8439SShannon Nelson 		/* remove from current linking so we can refresh it */
156969f8439SShannon Nelson 		hlist_del(&f->by_id);
157969f8439SShannon Nelson 		hlist_del(&f->by_hash);
158969f8439SShannon Nelson 	} else {
159969f8439SShannon Nelson 		f = devm_kzalloc(dev, sizeof(*f), GFP_ATOMIC);
160c1e329ebSShannon Nelson 		if (!f)
161c1e329ebSShannon Nelson 			return -ENOMEM;
162969f8439SShannon Nelson 	}
163c1e329ebSShannon Nelson 
164c1e329ebSShannon Nelson 	f->flow_id = flow_id;
165c1e329ebSShannon Nelson 	f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
166969f8439SShannon Nelson 	f->state = state;
167c1e329ebSShannon Nelson 	f->rxq_index = rxq_index;
168c1e329ebSShannon Nelson 	memcpy(&f->cmd, ac, sizeof(f->cmd));
169cbec2153SShannon Nelson 	netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
170c1e329ebSShannon Nelson 
171c1e329ebSShannon Nelson 	INIT_HLIST_NODE(&f->by_hash);
172c1e329ebSShannon Nelson 	INIT_HLIST_NODE(&f->by_id);
173c1e329ebSShannon Nelson 
174c1e329ebSShannon Nelson 	key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
175c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
176c1e329ebSShannon Nelson 	hlist_add_head(&f->by_hash, head);
177c1e329ebSShannon Nelson 
178c1e329ebSShannon Nelson 	key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
179c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_id[key];
180c1e329ebSShannon Nelson 	hlist_add_head(&f->by_id, head);
181c1e329ebSShannon Nelson 
182c1e329ebSShannon Nelson 	return 0;
183c1e329ebSShannon Nelson }
184c1e329ebSShannon Nelson 
185c1e329ebSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid)
186c1e329ebSShannon Nelson {
187c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
188c1e329ebSShannon Nelson 	struct hlist_head *head;
189c1e329ebSShannon Nelson 	unsigned int key;
190c1e329ebSShannon Nelson 
191c1e329ebSShannon Nelson 	key = hash_32(vid, IONIC_RX_FILTER_HASH_BITS);
192c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
193c1e329ebSShannon Nelson 
194c1e329ebSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
195c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_VLAN)
196c1e329ebSShannon Nelson 			continue;
197c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
198c1e329ebSShannon Nelson 			return f;
199c1e329ebSShannon Nelson 	}
200c1e329ebSShannon Nelson 
201c1e329ebSShannon Nelson 	return NULL;
202c1e329ebSShannon Nelson }
203c1e329ebSShannon Nelson 
204c1e329ebSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
205c1e329ebSShannon Nelson 						const u8 *addr)
206c1e329ebSShannon Nelson {
207c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
208c1e329ebSShannon Nelson 	struct hlist_head *head;
209c1e329ebSShannon Nelson 	unsigned int key;
210c1e329ebSShannon Nelson 
211c1e329ebSShannon Nelson 	key = hash_32(*(u32 *)addr, IONIC_RX_FILTER_HASH_BITS);
212c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
213c1e329ebSShannon Nelson 
214c1e329ebSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
215c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_MAC)
216c1e329ebSShannon Nelson 			continue;
217c1e329ebSShannon Nelson 		if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
218c1e329ebSShannon Nelson 			return f;
219c1e329ebSShannon Nelson 	}
220c1e329ebSShannon Nelson 
221c1e329ebSShannon Nelson 	return NULL;
222c1e329ebSShannon Nelson }
223ab470bbeSShannon Nelson 
224ab470bbeSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
225ab470bbeSShannon Nelson {
226ab470bbeSShannon Nelson 	struct ionic_rx_filter *f;
227ab470bbeSShannon Nelson 	struct hlist_head *head;
228ab470bbeSShannon Nelson 	unsigned int key;
229ab470bbeSShannon Nelson 
230ab470bbeSShannon Nelson 	key = hash_32(0, IONIC_RX_FILTER_HASH_BITS);
231ab470bbeSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
232ab470bbeSShannon Nelson 
233ab470bbeSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
234ab470bbeSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_STEER_PKTCLASS)
235ab470bbeSShannon Nelson 			continue;
236ab470bbeSShannon Nelson 		return f;
237ab470bbeSShannon Nelson 	}
238ab470bbeSShannon Nelson 
239ab470bbeSShannon Nelson 	return NULL;
240ab470bbeSShannon Nelson }
241969f8439SShannon Nelson 
242ff542fbeSShannon Nelson static struct ionic_rx_filter *ionic_rx_filter_find(struct ionic_lif *lif,
243ff542fbeSShannon Nelson 						    struct ionic_rx_filter_add_cmd *ac)
244ff542fbeSShannon Nelson {
245ff542fbeSShannon Nelson 	switch (le16_to_cpu(ac->match)) {
246ff542fbeSShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
247ff542fbeSShannon Nelson 		return ionic_rx_filter_by_vlan(lif, le16_to_cpu(ac->vlan.vlan));
248ff542fbeSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
249ff542fbeSShannon Nelson 		return ionic_rx_filter_by_addr(lif, ac->mac.addr);
250ff542fbeSShannon Nelson 	default:
251ff542fbeSShannon Nelson 		netdev_err(lif->netdev, "unsupported filter match %d",
252ff542fbeSShannon Nelson 			   le16_to_cpu(ac->match));
253ff542fbeSShannon Nelson 		return NULL;
254ff542fbeSShannon Nelson 	}
255ff542fbeSShannon Nelson }
256ff542fbeSShannon Nelson 
257969f8439SShannon Nelson int ionic_lif_list_addr(struct ionic_lif *lif, const u8 *addr, bool mode)
258969f8439SShannon Nelson {
259969f8439SShannon Nelson 	struct ionic_rx_filter *f;
260969f8439SShannon Nelson 	int err;
261969f8439SShannon Nelson 
262969f8439SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
263969f8439SShannon Nelson 
264969f8439SShannon Nelson 	f = ionic_rx_filter_by_addr(lif, addr);
265969f8439SShannon Nelson 	if (mode == ADD_ADDR && !f) {
266969f8439SShannon Nelson 		struct ionic_admin_ctx ctx = {
267969f8439SShannon Nelson 			.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
268969f8439SShannon Nelson 			.cmd.rx_filter_add = {
269969f8439SShannon Nelson 				.opcode = IONIC_CMD_RX_FILTER_ADD,
270969f8439SShannon Nelson 				.lif_index = cpu_to_le16(lif->index),
271969f8439SShannon Nelson 				.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
272969f8439SShannon Nelson 			},
273969f8439SShannon Nelson 		};
274969f8439SShannon Nelson 
275969f8439SShannon Nelson 		memcpy(ctx.cmd.rx_filter_add.mac.addr, addr, ETH_ALEN);
276969f8439SShannon Nelson 		err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
277969f8439SShannon Nelson 					   IONIC_FILTER_STATE_NEW);
278969f8439SShannon Nelson 		if (err) {
279969f8439SShannon Nelson 			spin_unlock_bh(&lif->rx_filters.lock);
280969f8439SShannon Nelson 			return err;
281969f8439SShannon Nelson 		}
282969f8439SShannon Nelson 
283969f8439SShannon Nelson 	} else if (mode == ADD_ADDR && f) {
284969f8439SShannon Nelson 		if (f->state == IONIC_FILTER_STATE_OLD)
285969f8439SShannon Nelson 			f->state = IONIC_FILTER_STATE_SYNCED;
286969f8439SShannon Nelson 
287969f8439SShannon Nelson 	} else if (mode == DEL_ADDR && f) {
288969f8439SShannon Nelson 		if (f->state == IONIC_FILTER_STATE_NEW)
289969f8439SShannon Nelson 			ionic_rx_filter_free(lif, f);
290969f8439SShannon Nelson 		else if (f->state == IONIC_FILTER_STATE_SYNCED)
291969f8439SShannon Nelson 			f->state = IONIC_FILTER_STATE_OLD;
292969f8439SShannon Nelson 	} else if (mode == DEL_ADDR && !f) {
293969f8439SShannon Nelson 		spin_unlock_bh(&lif->rx_filters.lock);
294969f8439SShannon Nelson 		return -ENOENT;
295969f8439SShannon Nelson 	}
296969f8439SShannon Nelson 
297969f8439SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
298969f8439SShannon Nelson 
299969f8439SShannon Nelson 	set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
300969f8439SShannon Nelson 
301969f8439SShannon Nelson 	return 0;
302969f8439SShannon Nelson }
303969f8439SShannon Nelson 
304eba688b1SShannon Nelson static int ionic_lif_filter_add(struct ionic_lif *lif,
305eba688b1SShannon Nelson 				struct ionic_rx_filter_add_cmd *ac)
3061d4ddc4aSShannon Nelson {
3071d4ddc4aSShannon Nelson 	struct ionic_admin_ctx ctx = {
3081d4ddc4aSShannon Nelson 		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
3091d4ddc4aSShannon Nelson 	};
3101d4ddc4aSShannon Nelson 	struct ionic_rx_filter *f;
311eba688b1SShannon Nelson 	int nfilters;
3121d4ddc4aSShannon Nelson 	int err = 0;
3131d4ddc4aSShannon Nelson 
314eba688b1SShannon Nelson 	ctx.cmd.rx_filter_add = *ac;
315eba688b1SShannon Nelson 	ctx.cmd.rx_filter_add.opcode = IONIC_CMD_RX_FILTER_ADD,
316eba688b1SShannon Nelson 	ctx.cmd.rx_filter_add.lif_index = cpu_to_le16(lif->index),
3171d4ddc4aSShannon Nelson 
3181d4ddc4aSShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
319ff542fbeSShannon Nelson 	f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
3201d4ddc4aSShannon Nelson 	if (f) {
3211d4ddc4aSShannon Nelson 		/* don't bother if we already have it and it is sync'd */
3221d4ddc4aSShannon Nelson 		if (f->state == IONIC_FILTER_STATE_SYNCED) {
3231d4ddc4aSShannon Nelson 			spin_unlock_bh(&lif->rx_filters.lock);
3241d4ddc4aSShannon Nelson 			return 0;
3251d4ddc4aSShannon Nelson 		}
3261d4ddc4aSShannon Nelson 
3271d4ddc4aSShannon Nelson 		/* mark preemptively as sync'd to block any parallel attempts */
3281d4ddc4aSShannon Nelson 		f->state = IONIC_FILTER_STATE_SYNCED;
3291d4ddc4aSShannon Nelson 	} else {
3301d4ddc4aSShannon Nelson 		/* save as SYNCED to catch any DEL requests while processing */
3311d4ddc4aSShannon Nelson 		err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
3321d4ddc4aSShannon Nelson 					   IONIC_FILTER_STATE_SYNCED);
3331d4ddc4aSShannon Nelson 	}
3341d4ddc4aSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
3351d4ddc4aSShannon Nelson 	if (err)
3361d4ddc4aSShannon Nelson 		return err;
3371d4ddc4aSShannon Nelson 
3381d4ddc4aSShannon Nelson 	/* Don't bother with the write to FW if we know there's no room,
3391d4ddc4aSShannon Nelson 	 * we can try again on the next sync attempt.
340*9b0b6ba6SShannon Nelson 	 * Since the FW doesn't have a way to tell us the vlan limit,
341*9b0b6ba6SShannon Nelson 	 * we start max_vlans at 0 until we hit the ENOSPC error.
3421d4ddc4aSShannon Nelson 	 */
343eba688b1SShannon Nelson 	switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
344*9b0b6ba6SShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
345*9b0b6ba6SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter add VLAN %d\n",
346*9b0b6ba6SShannon Nelson 			   __func__, ctx.cmd.rx_filter_add.vlan.vlan);
347*9b0b6ba6SShannon Nelson 		if (lif->max_vlans && lif->nvlans >= lif->max_vlans)
348*9b0b6ba6SShannon Nelson 			err = -ENOSPC;
349*9b0b6ba6SShannon Nelson 		break;
350eba688b1SShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
351eba688b1SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter add ADDR %pM\n",
352eba688b1SShannon Nelson 			   __func__, ctx.cmd.rx_filter_add.mac.addr);
353eba688b1SShannon Nelson 		nfilters = le32_to_cpu(lif->identity->eth.max_ucast_filters);
3541d4ddc4aSShannon Nelson 		if ((lif->nucast + lif->nmcast) >= nfilters)
3551d4ddc4aSShannon Nelson 			err = -ENOSPC;
356eba688b1SShannon Nelson 		break;
357eba688b1SShannon Nelson 	}
358eba688b1SShannon Nelson 
359eba688b1SShannon Nelson 	if (err != -ENOSPC)
3601d4ddc4aSShannon Nelson 		err = ionic_adminq_post_wait(lif, &ctx);
3611d4ddc4aSShannon Nelson 
3621d4ddc4aSShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
363eba688b1SShannon Nelson 
3641d4ddc4aSShannon Nelson 	if (err && err != -EEXIST) {
3651d4ddc4aSShannon Nelson 		/* set the state back to NEW so we can try again later */
366ff542fbeSShannon Nelson 		f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
3671d4ddc4aSShannon Nelson 		if (f && f->state == IONIC_FILTER_STATE_SYNCED) {
3681d4ddc4aSShannon Nelson 			f->state = IONIC_FILTER_STATE_NEW;
369eba688b1SShannon Nelson 
370eba688b1SShannon Nelson 			/* If -ENOSPC we won't waste time trying to sync again
371eba688b1SShannon Nelson 			 * until there is a delete that might make room
372eba688b1SShannon Nelson 			 */
373eba688b1SShannon Nelson 			if (err != -ENOSPC)
3741d4ddc4aSShannon Nelson 				set_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
3751d4ddc4aSShannon Nelson 		}
3761d4ddc4aSShannon Nelson 
3771d4ddc4aSShannon Nelson 		spin_unlock_bh(&lif->rx_filters.lock);
3781d4ddc4aSShannon Nelson 
379*9b0b6ba6SShannon Nelson 		if (err == -ENOSPC) {
380*9b0b6ba6SShannon Nelson 			if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN)
381*9b0b6ba6SShannon Nelson 				lif->max_vlans = lif->nvlans;
3821d4ddc4aSShannon Nelson 			return 0;
383*9b0b6ba6SShannon Nelson 		}
384eba688b1SShannon Nelson 
3851d4ddc4aSShannon Nelson 		return err;
3861d4ddc4aSShannon Nelson 	}
3871d4ddc4aSShannon Nelson 
388eba688b1SShannon Nelson 	switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
389*9b0b6ba6SShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
390*9b0b6ba6SShannon Nelson 		lif->nvlans++;
391*9b0b6ba6SShannon Nelson 		break;
392eba688b1SShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
393eba688b1SShannon Nelson 		if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr))
3941d4ddc4aSShannon Nelson 			lif->nmcast++;
3951d4ddc4aSShannon Nelson 		else
3961d4ddc4aSShannon Nelson 			lif->nucast++;
397eba688b1SShannon Nelson 		break;
398eba688b1SShannon Nelson 	}
3991d4ddc4aSShannon Nelson 
400ff542fbeSShannon Nelson 	f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
4011d4ddc4aSShannon Nelson 	if (f && f->state == IONIC_FILTER_STATE_OLD) {
4021d4ddc4aSShannon Nelson 		/* Someone requested a delete while we were adding
4031d4ddc4aSShannon Nelson 		 * so update the filter info with the results from the add
4041d4ddc4aSShannon Nelson 		 * and the data will be there for the delete on the next
4051d4ddc4aSShannon Nelson 		 * sync cycle.
4061d4ddc4aSShannon Nelson 		 */
4071d4ddc4aSShannon Nelson 		err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
4081d4ddc4aSShannon Nelson 					   IONIC_FILTER_STATE_OLD);
4091d4ddc4aSShannon Nelson 	} else {
4101d4ddc4aSShannon Nelson 		err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
4111d4ddc4aSShannon Nelson 					   IONIC_FILTER_STATE_SYNCED);
4121d4ddc4aSShannon Nelson 	}
4131d4ddc4aSShannon Nelson 
4141d4ddc4aSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
4151d4ddc4aSShannon Nelson 
4161d4ddc4aSShannon Nelson 	return err;
4171d4ddc4aSShannon Nelson }
4181d4ddc4aSShannon Nelson 
419eba688b1SShannon Nelson int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
420eba688b1SShannon Nelson {
421eba688b1SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
422eba688b1SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
423eba688b1SShannon Nelson 	};
424eba688b1SShannon Nelson 
425eba688b1SShannon Nelson 	memcpy(&ac.mac.addr, addr, ETH_ALEN);
426eba688b1SShannon Nelson 
427eba688b1SShannon Nelson 	return ionic_lif_filter_add(lif, &ac);
428eba688b1SShannon Nelson }
429eba688b1SShannon Nelson 
430*9b0b6ba6SShannon Nelson int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid)
431*9b0b6ba6SShannon Nelson {
432*9b0b6ba6SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
433*9b0b6ba6SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
434*9b0b6ba6SShannon Nelson 		.vlan.vlan = cpu_to_le16(vid),
435*9b0b6ba6SShannon Nelson 	};
436*9b0b6ba6SShannon Nelson 
437*9b0b6ba6SShannon Nelson 	return ionic_lif_filter_add(lif, &ac);
438*9b0b6ba6SShannon Nelson }
439*9b0b6ba6SShannon Nelson 
440c2b63d34SShannon Nelson static int ionic_lif_filter_del(struct ionic_lif *lif,
441c2b63d34SShannon Nelson 				struct ionic_rx_filter_add_cmd *ac)
4421d4ddc4aSShannon Nelson {
4431d4ddc4aSShannon Nelson 	struct ionic_admin_ctx ctx = {
4441d4ddc4aSShannon Nelson 		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
4451d4ddc4aSShannon Nelson 		.cmd.rx_filter_del = {
4461d4ddc4aSShannon Nelson 			.opcode = IONIC_CMD_RX_FILTER_DEL,
4471d4ddc4aSShannon Nelson 			.lif_index = cpu_to_le16(lif->index),
4481d4ddc4aSShannon Nelson 		},
4491d4ddc4aSShannon Nelson 	};
4501d4ddc4aSShannon Nelson 	struct ionic_rx_filter *f;
4511d4ddc4aSShannon Nelson 	int state;
4521d4ddc4aSShannon Nelson 	int err;
4531d4ddc4aSShannon Nelson 
4541d4ddc4aSShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
455c2b63d34SShannon Nelson 	f = ionic_rx_filter_find(lif, ac);
4561d4ddc4aSShannon Nelson 	if (!f) {
4571d4ddc4aSShannon Nelson 		spin_unlock_bh(&lif->rx_filters.lock);
4581d4ddc4aSShannon Nelson 		return -ENOENT;
4591d4ddc4aSShannon Nelson 	}
4601d4ddc4aSShannon Nelson 
461c2b63d34SShannon Nelson 	switch (le16_to_cpu(ac->match)) {
462*9b0b6ba6SShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
463*9b0b6ba6SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter del VLAN %d id %d\n",
464*9b0b6ba6SShannon Nelson 			   __func__, ac->vlan.vlan, f->filter_id);
465*9b0b6ba6SShannon Nelson 		lif->nvlans--;
466*9b0b6ba6SShannon Nelson 		break;
467c2b63d34SShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
468c2b63d34SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n",
469c2b63d34SShannon Nelson 			   __func__, ac->mac.addr, f->filter_id);
470c2b63d34SShannon Nelson 		if (is_multicast_ether_addr(ac->mac.addr) && lif->nmcast)
471c2b63d34SShannon Nelson 			lif->nmcast--;
472c2b63d34SShannon Nelson 		else if (!is_multicast_ether_addr(ac->mac.addr) && lif->nucast)
473c2b63d34SShannon Nelson 			lif->nucast--;
474c2b63d34SShannon Nelson 		break;
475c2b63d34SShannon Nelson 	}
4761d4ddc4aSShannon Nelson 
4771d4ddc4aSShannon Nelson 	state = f->state;
4781d4ddc4aSShannon Nelson 	ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
4791d4ddc4aSShannon Nelson 	ionic_rx_filter_free(lif, f);
4801d4ddc4aSShannon Nelson 
4811d4ddc4aSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
4821d4ddc4aSShannon Nelson 
4831d4ddc4aSShannon Nelson 	if (state != IONIC_FILTER_STATE_NEW) {
4841d4ddc4aSShannon Nelson 		err = ionic_adminq_post_wait(lif, &ctx);
4851d4ddc4aSShannon Nelson 		if (err && err != -EEXIST)
4861d4ddc4aSShannon Nelson 			return err;
4871d4ddc4aSShannon Nelson 	}
4881d4ddc4aSShannon Nelson 
4891d4ddc4aSShannon Nelson 	return 0;
4901d4ddc4aSShannon Nelson }
4911d4ddc4aSShannon Nelson 
492c2b63d34SShannon Nelson int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
493c2b63d34SShannon Nelson {
494c2b63d34SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
495c2b63d34SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
496c2b63d34SShannon Nelson 	};
497c2b63d34SShannon Nelson 
498c2b63d34SShannon Nelson 	memcpy(&ac.mac.addr, addr, ETH_ALEN);
499c2b63d34SShannon Nelson 
500c2b63d34SShannon Nelson 	return ionic_lif_filter_del(lif, &ac);
501c2b63d34SShannon Nelson }
502c2b63d34SShannon Nelson 
503*9b0b6ba6SShannon Nelson int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid)
504*9b0b6ba6SShannon Nelson {
505*9b0b6ba6SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
506*9b0b6ba6SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
507*9b0b6ba6SShannon Nelson 		.vlan.vlan = cpu_to_le16(vid),
508*9b0b6ba6SShannon Nelson 	};
509*9b0b6ba6SShannon Nelson 
510*9b0b6ba6SShannon Nelson 	return ionic_lif_filter_del(lif, &ac);
511*9b0b6ba6SShannon Nelson }
512*9b0b6ba6SShannon Nelson 
513969f8439SShannon Nelson struct sync_item {
514969f8439SShannon Nelson 	struct list_head list;
515969f8439SShannon Nelson 	struct ionic_rx_filter f;
516969f8439SShannon Nelson };
517969f8439SShannon Nelson 
518969f8439SShannon Nelson void ionic_rx_filter_sync(struct ionic_lif *lif)
519969f8439SShannon Nelson {
520969f8439SShannon Nelson 	struct device *dev = lif->ionic->dev;
521969f8439SShannon Nelson 	struct list_head sync_add_list;
522969f8439SShannon Nelson 	struct list_head sync_del_list;
523969f8439SShannon Nelson 	struct sync_item *sync_item;
524969f8439SShannon Nelson 	struct ionic_rx_filter *f;
525969f8439SShannon Nelson 	struct hlist_head *head;
526969f8439SShannon Nelson 	struct hlist_node *tmp;
527969f8439SShannon Nelson 	struct sync_item *spos;
528969f8439SShannon Nelson 	unsigned int i;
529969f8439SShannon Nelson 
530969f8439SShannon Nelson 	INIT_LIST_HEAD(&sync_add_list);
531969f8439SShannon Nelson 	INIT_LIST_HEAD(&sync_del_list);
532969f8439SShannon Nelson 
533969f8439SShannon Nelson 	clear_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
534969f8439SShannon Nelson 
535969f8439SShannon Nelson 	/* Copy the filters to be added and deleted
536969f8439SShannon Nelson 	 * into a separate local list that needs no locking.
537969f8439SShannon Nelson 	 */
538969f8439SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
539969f8439SShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
540969f8439SShannon Nelson 		head = &lif->rx_filters.by_id[i];
541969f8439SShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id) {
542969f8439SShannon Nelson 			if (f->state == IONIC_FILTER_STATE_NEW ||
543969f8439SShannon Nelson 			    f->state == IONIC_FILTER_STATE_OLD) {
544969f8439SShannon Nelson 				sync_item = devm_kzalloc(dev, sizeof(*sync_item),
54552a67fbfSDan Carpenter 							 GFP_ATOMIC);
546969f8439SShannon Nelson 				if (!sync_item)
547969f8439SShannon Nelson 					goto loop_out;
548969f8439SShannon Nelson 
549969f8439SShannon Nelson 				sync_item->f = *f;
550969f8439SShannon Nelson 
551969f8439SShannon Nelson 				if (f->state == IONIC_FILTER_STATE_NEW)
552969f8439SShannon Nelson 					list_add(&sync_item->list, &sync_add_list);
553969f8439SShannon Nelson 				else
554969f8439SShannon Nelson 					list_add(&sync_item->list, &sync_del_list);
555969f8439SShannon Nelson 			}
556969f8439SShannon Nelson 		}
557969f8439SShannon Nelson 	}
558969f8439SShannon Nelson loop_out:
559969f8439SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
560969f8439SShannon Nelson 
561969f8439SShannon Nelson 	/* If the add or delete fails, it won't get marked as sync'd
562969f8439SShannon Nelson 	 * and will be tried again in the next sync action.
563969f8439SShannon Nelson 	 * Do the deletes first in case we're in an overflow state and
564969f8439SShannon Nelson 	 * they can clear room for some new filters
565969f8439SShannon Nelson 	 */
566969f8439SShannon Nelson 	list_for_each_entry_safe(sync_item, spos, &sync_del_list, list) {
567c2b63d34SShannon Nelson 		(void)ionic_lif_filter_del(lif, &sync_item->f.cmd);
568969f8439SShannon Nelson 
569969f8439SShannon Nelson 		list_del(&sync_item->list);
570969f8439SShannon Nelson 		devm_kfree(dev, sync_item);
571969f8439SShannon Nelson 	}
572969f8439SShannon Nelson 
573969f8439SShannon Nelson 	list_for_each_entry_safe(sync_item, spos, &sync_add_list, list) {
574eba688b1SShannon Nelson 		(void)ionic_lif_filter_add(lif, &sync_item->f.cmd);
575969f8439SShannon Nelson 
576969f8439SShannon Nelson 		list_del(&sync_item->list);
577969f8439SShannon Nelson 		devm_kfree(dev, sync_item);
578969f8439SShannon Nelson 	}
579969f8439SShannon Nelson }
580