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 
ionic_rx_filter_free(struct ionic_lif * lif,struct ionic_rx_filter * f)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 
ionic_rx_filter_replay(struct ionic_lif * lif)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 
ionic_rx_filters_init(struct ionic_lif * lif)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 
ionic_rx_filters_deinit(struct ionic_lif * lif)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 
ionic_rx_filter_save(struct ionic_lif * lif,u32 flow_id,u16 rxq_index,u32 hash,struct ionic_admin_ctx * ctx,enum ionic_filter_state state)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 
ionic_rx_filter_by_vlan(struct ionic_lif * lif,u16 vid)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 
ionic_rx_filter_by_addr(struct ionic_lif * lif,const u8 * addr)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 
ionic_rx_filter_rxsteer(struct ionic_lif * lif)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 
ionic_rx_filter_find(struct ionic_lif * lif,struct ionic_rx_filter_add_cmd * ac)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 
ionic_lif_list_addr(struct ionic_lif * lif,const u8 * addr,bool mode)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 
ionic_lif_filter_add(struct ionic_lif * lif,struct ionic_rx_filter_add_cmd * ac)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.
3409b0b6ba6SShannon Nelson 	 * Since the FW doesn't have a way to tell us the vlan limit,
3419b0b6ba6SShannon 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)) {
3449b0b6ba6SShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
3459b0b6ba6SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter add VLAN %d\n",
3469b0b6ba6SShannon Nelson 			   __func__, ctx.cmd.rx_filter_add.vlan.vlan);
3479b0b6ba6SShannon Nelson 		if (lif->max_vlans && lif->nvlans >= lif->max_vlans)
3489b0b6ba6SShannon Nelson 			err = -ENOSPC;
3499b0b6ba6SShannon 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)
360f91958ccSShannon Nelson 		err = ionic_adminq_post_wait_nomsg(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 
379584fb767SBrett Creeley 		/* store the max_vlans limit that we found */
380584fb767SBrett Creeley 		if (err == -ENOSPC &&
381584fb767SBrett Creeley 		    le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN)
3829b0b6ba6SShannon Nelson 			lif->max_vlans = lif->nvlans;
383584fb767SBrett Creeley 
384584fb767SBrett Creeley 		/* Prevent unnecessary error messages on recoverable
385584fb767SBrett Creeley 		 * errors as the filter will get retried on the next
386584fb767SBrett Creeley 		 * sync attempt.
387584fb767SBrett Creeley 		 */
388584fb767SBrett Creeley 		switch (err) {
389584fb767SBrett Creeley 		case -ENOSPC:
390584fb767SBrett Creeley 		case -ENXIO:
391584fb767SBrett Creeley 		case -ETIMEDOUT:
392584fb767SBrett Creeley 		case -EAGAIN:
393584fb767SBrett Creeley 		case -EBUSY:
3941d4ddc4aSShannon Nelson 			return 0;
395584fb767SBrett Creeley 		default:
396584fb767SBrett Creeley 			break;
3979b0b6ba6SShannon Nelson 		}
398eba688b1SShannon Nelson 
399f91958ccSShannon Nelson 		ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
400f91958ccSShannon Nelson 					      ctx.comp.comp.status, err);
401f91958ccSShannon Nelson 		switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
402f91958ccSShannon Nelson 		case IONIC_RX_FILTER_MATCH_VLAN:
403f91958ccSShannon Nelson 			netdev_info(lif->netdev, "rx_filter add failed: VLAN %d\n",
404f91958ccSShannon Nelson 				    ctx.cmd.rx_filter_add.vlan.vlan);
405f91958ccSShannon Nelson 			break;
406f91958ccSShannon Nelson 		case IONIC_RX_FILTER_MATCH_MAC:
407f91958ccSShannon Nelson 			netdev_info(lif->netdev, "rx_filter add failed: ADDR %pM\n",
408f91958ccSShannon Nelson 				    ctx.cmd.rx_filter_add.mac.addr);
409f91958ccSShannon Nelson 			break;
410f91958ccSShannon Nelson 		}
411f91958ccSShannon Nelson 
4121d4ddc4aSShannon Nelson 		return err;
4131d4ddc4aSShannon Nelson 	}
4141d4ddc4aSShannon Nelson 
415eba688b1SShannon Nelson 	switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) {
4169b0b6ba6SShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
4179b0b6ba6SShannon Nelson 		lif->nvlans++;
4189b0b6ba6SShannon Nelson 		break;
419eba688b1SShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
420eba688b1SShannon Nelson 		if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr))
4211d4ddc4aSShannon Nelson 			lif->nmcast++;
4221d4ddc4aSShannon Nelson 		else
4231d4ddc4aSShannon Nelson 			lif->nucast++;
424eba688b1SShannon Nelson 		break;
425eba688b1SShannon Nelson 	}
4261d4ddc4aSShannon Nelson 
427ff542fbeSShannon Nelson 	f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add);
4281d4ddc4aSShannon Nelson 	if (f && f->state == IONIC_FILTER_STATE_OLD) {
4291d4ddc4aSShannon Nelson 		/* Someone requested a delete while we were adding
4301d4ddc4aSShannon Nelson 		 * so update the filter info with the results from the add
4311d4ddc4aSShannon Nelson 		 * and the data will be there for the delete on the next
4321d4ddc4aSShannon Nelson 		 * sync cycle.
4331d4ddc4aSShannon Nelson 		 */
4341d4ddc4aSShannon Nelson 		err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
4351d4ddc4aSShannon Nelson 					   IONIC_FILTER_STATE_OLD);
4361d4ddc4aSShannon Nelson 	} else {
4371d4ddc4aSShannon Nelson 		err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx,
4381d4ddc4aSShannon Nelson 					   IONIC_FILTER_STATE_SYNCED);
4391d4ddc4aSShannon Nelson 	}
4401d4ddc4aSShannon Nelson 
4411d4ddc4aSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
4421d4ddc4aSShannon Nelson 
4431d4ddc4aSShannon Nelson 	return err;
4441d4ddc4aSShannon Nelson }
4451d4ddc4aSShannon Nelson 
ionic_lif_addr_add(struct ionic_lif * lif,const u8 * addr)446eba688b1SShannon Nelson int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr)
447eba688b1SShannon Nelson {
448eba688b1SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
449eba688b1SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
450eba688b1SShannon Nelson 	};
451eba688b1SShannon Nelson 
452eba688b1SShannon Nelson 	memcpy(&ac.mac.addr, addr, ETH_ALEN);
453eba688b1SShannon Nelson 
454eba688b1SShannon Nelson 	return ionic_lif_filter_add(lif, &ac);
455eba688b1SShannon Nelson }
456eba688b1SShannon Nelson 
ionic_lif_vlan_add(struct ionic_lif * lif,const u16 vid)4579b0b6ba6SShannon Nelson int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid)
4589b0b6ba6SShannon Nelson {
4599b0b6ba6SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
4609b0b6ba6SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
4619b0b6ba6SShannon Nelson 		.vlan.vlan = cpu_to_le16(vid),
4629b0b6ba6SShannon Nelson 	};
4639b0b6ba6SShannon Nelson 
4649b0b6ba6SShannon Nelson 	return ionic_lif_filter_add(lif, &ac);
4659b0b6ba6SShannon Nelson }
4669b0b6ba6SShannon Nelson 
ionic_lif_filter_del(struct ionic_lif * lif,struct ionic_rx_filter_add_cmd * ac)467c2b63d34SShannon Nelson static int ionic_lif_filter_del(struct ionic_lif *lif,
468c2b63d34SShannon Nelson 				struct ionic_rx_filter_add_cmd *ac)
4691d4ddc4aSShannon Nelson {
4701d4ddc4aSShannon Nelson 	struct ionic_admin_ctx ctx = {
4711d4ddc4aSShannon Nelson 		.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work),
4721d4ddc4aSShannon Nelson 		.cmd.rx_filter_del = {
4731d4ddc4aSShannon Nelson 			.opcode = IONIC_CMD_RX_FILTER_DEL,
4741d4ddc4aSShannon Nelson 			.lif_index = cpu_to_le16(lif->index),
4751d4ddc4aSShannon Nelson 		},
4761d4ddc4aSShannon Nelson 	};
4771d4ddc4aSShannon Nelson 	struct ionic_rx_filter *f;
4781d4ddc4aSShannon Nelson 	int state;
4791d4ddc4aSShannon Nelson 	int err;
4801d4ddc4aSShannon Nelson 
4811d4ddc4aSShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
482c2b63d34SShannon Nelson 	f = ionic_rx_filter_find(lif, ac);
4831d4ddc4aSShannon Nelson 	if (!f) {
4841d4ddc4aSShannon Nelson 		spin_unlock_bh(&lif->rx_filters.lock);
4851d4ddc4aSShannon Nelson 		return -ENOENT;
4861d4ddc4aSShannon Nelson 	}
4871d4ddc4aSShannon Nelson 
488c2b63d34SShannon Nelson 	switch (le16_to_cpu(ac->match)) {
4899b0b6ba6SShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
4909b0b6ba6SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter del VLAN %d id %d\n",
4919b0b6ba6SShannon Nelson 			   __func__, ac->vlan.vlan, f->filter_id);
4929b0b6ba6SShannon Nelson 		lif->nvlans--;
4939b0b6ba6SShannon Nelson 		break;
494c2b63d34SShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
495c2b63d34SShannon Nelson 		netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n",
496c2b63d34SShannon Nelson 			   __func__, ac->mac.addr, f->filter_id);
497c2b63d34SShannon Nelson 		if (is_multicast_ether_addr(ac->mac.addr) && lif->nmcast)
498c2b63d34SShannon Nelson 			lif->nmcast--;
499c2b63d34SShannon Nelson 		else if (!is_multicast_ether_addr(ac->mac.addr) && lif->nucast)
500c2b63d34SShannon Nelson 			lif->nucast--;
501c2b63d34SShannon Nelson 		break;
502c2b63d34SShannon Nelson 	}
5031d4ddc4aSShannon Nelson 
5041d4ddc4aSShannon Nelson 	state = f->state;
5051d4ddc4aSShannon Nelson 	ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id);
5061d4ddc4aSShannon Nelson 	ionic_rx_filter_free(lif, f);
5071d4ddc4aSShannon Nelson 
5081d4ddc4aSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
5091d4ddc4aSShannon Nelson 
5101d4ddc4aSShannon Nelson 	if (state != IONIC_FILTER_STATE_NEW) {
511584fb767SBrett Creeley 		err = ionic_adminq_post_wait_nomsg(lif, &ctx);
512584fb767SBrett Creeley 
513584fb767SBrett Creeley 		switch (err) {
514584fb767SBrett Creeley 			/* ignore these errors */
515584fb767SBrett Creeley 		case -EEXIST:
516584fb767SBrett Creeley 		case -ENXIO:
517584fb767SBrett Creeley 		case -ETIMEDOUT:
518584fb767SBrett Creeley 		case -EAGAIN:
519584fb767SBrett Creeley 		case -EBUSY:
520584fb767SBrett Creeley 		case 0:
521584fb767SBrett Creeley 			break;
522584fb767SBrett Creeley 		default:
523584fb767SBrett Creeley 			ionic_adminq_netdev_err_print(lif, ctx.cmd.cmd.opcode,
524584fb767SBrett Creeley 						      ctx.comp.comp.status, err);
5251d4ddc4aSShannon Nelson 			return err;
5261d4ddc4aSShannon Nelson 		}
527584fb767SBrett Creeley 	}
5281d4ddc4aSShannon Nelson 
5291d4ddc4aSShannon Nelson 	return 0;
5301d4ddc4aSShannon Nelson }
5311d4ddc4aSShannon Nelson 
ionic_lif_addr_del(struct ionic_lif * lif,const u8 * addr)532c2b63d34SShannon Nelson int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr)
533c2b63d34SShannon Nelson {
534c2b63d34SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
535c2b63d34SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC),
536c2b63d34SShannon Nelson 	};
537c2b63d34SShannon Nelson 
538c2b63d34SShannon Nelson 	memcpy(&ac.mac.addr, addr, ETH_ALEN);
539c2b63d34SShannon Nelson 
540c2b63d34SShannon Nelson 	return ionic_lif_filter_del(lif, &ac);
541c2b63d34SShannon Nelson }
542c2b63d34SShannon Nelson 
ionic_lif_vlan_del(struct ionic_lif * lif,const u16 vid)5439b0b6ba6SShannon Nelson int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid)
5449b0b6ba6SShannon Nelson {
5459b0b6ba6SShannon Nelson 	struct ionic_rx_filter_add_cmd ac = {
5469b0b6ba6SShannon Nelson 		.match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN),
5479b0b6ba6SShannon Nelson 		.vlan.vlan = cpu_to_le16(vid),
5489b0b6ba6SShannon Nelson 	};
5499b0b6ba6SShannon Nelson 
5509b0b6ba6SShannon Nelson 	return ionic_lif_filter_del(lif, &ac);
5519b0b6ba6SShannon Nelson }
5529b0b6ba6SShannon Nelson 
553969f8439SShannon Nelson struct sync_item {
554969f8439SShannon Nelson 	struct list_head list;
555969f8439SShannon Nelson 	struct ionic_rx_filter f;
556969f8439SShannon Nelson };
557969f8439SShannon Nelson 
ionic_rx_filter_sync(struct ionic_lif * lif)558969f8439SShannon Nelson void ionic_rx_filter_sync(struct ionic_lif *lif)
559969f8439SShannon Nelson {
560969f8439SShannon Nelson 	struct device *dev = lif->ionic->dev;
561969f8439SShannon Nelson 	struct list_head sync_add_list;
562969f8439SShannon Nelson 	struct list_head sync_del_list;
563969f8439SShannon Nelson 	struct sync_item *sync_item;
564969f8439SShannon Nelson 	struct ionic_rx_filter *f;
565969f8439SShannon Nelson 	struct hlist_head *head;
566969f8439SShannon Nelson 	struct hlist_node *tmp;
567969f8439SShannon Nelson 	struct sync_item *spos;
568969f8439SShannon Nelson 	unsigned int i;
569969f8439SShannon Nelson 
570969f8439SShannon Nelson 	INIT_LIST_HEAD(&sync_add_list);
571969f8439SShannon Nelson 	INIT_LIST_HEAD(&sync_del_list);
572969f8439SShannon Nelson 
573969f8439SShannon Nelson 	clear_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state);
574969f8439SShannon Nelson 
575969f8439SShannon Nelson 	/* Copy the filters to be added and deleted
576969f8439SShannon Nelson 	 * into a separate local list that needs no locking.
577969f8439SShannon Nelson 	 */
578969f8439SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
579969f8439SShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
580969f8439SShannon Nelson 		head = &lif->rx_filters.by_id[i];
581969f8439SShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id) {
582969f8439SShannon Nelson 			if (f->state == IONIC_FILTER_STATE_NEW ||
583969f8439SShannon Nelson 			    f->state == IONIC_FILTER_STATE_OLD) {
584969f8439SShannon Nelson 				sync_item = devm_kzalloc(dev, sizeof(*sync_item),
58552a67fbfSDan Carpenter 							 GFP_ATOMIC);
586969f8439SShannon Nelson 				if (!sync_item)
587969f8439SShannon Nelson 					goto loop_out;
588969f8439SShannon Nelson 
589969f8439SShannon Nelson 				sync_item->f = *f;
590969f8439SShannon Nelson 
591969f8439SShannon Nelson 				if (f->state == IONIC_FILTER_STATE_NEW)
592969f8439SShannon Nelson 					list_add(&sync_item->list, &sync_add_list);
593969f8439SShannon Nelson 				else
594969f8439SShannon Nelson 					list_add(&sync_item->list, &sync_del_list);
595969f8439SShannon Nelson 			}
596969f8439SShannon Nelson 		}
597969f8439SShannon Nelson 	}
598969f8439SShannon Nelson loop_out:
599969f8439SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
600969f8439SShannon Nelson 
601969f8439SShannon Nelson 	/* If the add or delete fails, it won't get marked as sync'd
602969f8439SShannon Nelson 	 * and will be tried again in the next sync action.
603969f8439SShannon Nelson 	 * Do the deletes first in case we're in an overflow state and
604969f8439SShannon Nelson 	 * they can clear room for some new filters
605969f8439SShannon Nelson 	 */
606969f8439SShannon Nelson 	list_for_each_entry_safe(sync_item, spos, &sync_del_list, list) {
607*896de449SShannon Nelson 		ionic_lif_filter_del(lif, &sync_item->f.cmd);
608969f8439SShannon Nelson 
609969f8439SShannon Nelson 		list_del(&sync_item->list);
610969f8439SShannon Nelson 		devm_kfree(dev, sync_item);
611969f8439SShannon Nelson 	}
612969f8439SShannon Nelson 
613969f8439SShannon Nelson 	list_for_each_entry_safe(sync_item, spos, &sync_add_list, list) {
614*896de449SShannon Nelson 		ionic_lif_filter_add(lif, &sync_item->f.cmd);
615969f8439SShannon Nelson 
616969f8439SShannon Nelson 		list_del(&sync_item->list);
617969f8439SShannon Nelson 		devm_kfree(dev, sync_item);
618969f8439SShannon Nelson 	}
619969f8439SShannon Nelson }
620