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>
7c1e329ebSShannon Nelson 
8c1e329ebSShannon Nelson #include "ionic.h"
9c1e329ebSShannon Nelson #include "ionic_lif.h"
10c1e329ebSShannon Nelson #include "ionic_rx_filter.h"
11c1e329ebSShannon Nelson 
12c1e329ebSShannon Nelson void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f)
13c1e329ebSShannon Nelson {
14c1e329ebSShannon Nelson 	struct device *dev = lif->ionic->dev;
15c1e329ebSShannon Nelson 
16c1e329ebSShannon Nelson 	hlist_del(&f->by_id);
17c1e329ebSShannon Nelson 	hlist_del(&f->by_hash);
18c1e329ebSShannon Nelson 	devm_kfree(dev, f);
19c1e329ebSShannon Nelson }
20c1e329ebSShannon Nelson 
217e4d4759SShannon Nelson void ionic_rx_filter_replay(struct ionic_lif *lif)
22c1e329ebSShannon Nelson {
237e4d4759SShannon Nelson 	struct ionic_rx_filter_add_cmd *ac;
24cc4428c4SShannon Nelson 	struct hlist_head new_id_list;
257e4d4759SShannon Nelson 	struct ionic_admin_ctx ctx;
267e4d4759SShannon Nelson 	struct ionic_rx_filter *f;
277e4d4759SShannon Nelson 	struct hlist_head *head;
287e4d4759SShannon Nelson 	struct hlist_node *tmp;
29cc4428c4SShannon Nelson 	unsigned int key;
307e4d4759SShannon Nelson 	unsigned int i;
312c0df9f9SShannon Nelson 	int err;
32c1e329ebSShannon Nelson 
33cc4428c4SShannon Nelson 	INIT_HLIST_HEAD(&new_id_list);
347e4d4759SShannon Nelson 	ac = &ctx.cmd.rx_filter_add;
357e4d4759SShannon Nelson 
367e4d4759SShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
377e4d4759SShannon Nelson 		head = &lif->rx_filters.by_id[i];
387e4d4759SShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id) {
397e4d4759SShannon Nelson 			ctx.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work);
407e4d4759SShannon Nelson 			memcpy(ac, &f->cmd, sizeof(f->cmd));
417e4d4759SShannon Nelson 			dev_dbg(&lif->netdev->dev, "replay filter command:\n");
427e4d4759SShannon Nelson 			dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
437e4d4759SShannon Nelson 					 &ctx.cmd, sizeof(ctx.cmd), true);
447e4d4759SShannon Nelson 
457e4d4759SShannon Nelson 			err = ionic_adminq_post_wait(lif, &ctx);
467e4d4759SShannon Nelson 			if (err) {
477e4d4759SShannon Nelson 				switch (le16_to_cpu(ac->match)) {
487e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_VLAN:
497e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d\n",
507e4d4759SShannon Nelson 						    err,
517e4d4759SShannon Nelson 						    le16_to_cpu(ac->vlan.vlan));
527e4d4759SShannon Nelson 					break;
537e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_MAC:
547e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: mac %pM\n",
557e4d4759SShannon Nelson 						    err, ac->mac.addr);
567e4d4759SShannon Nelson 					break;
577e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_MAC_VLAN:
587e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d mac %pM\n",
597e4d4759SShannon Nelson 						    err,
607e4d4759SShannon Nelson 						    le16_to_cpu(ac->vlan.vlan),
617e4d4759SShannon Nelson 						    ac->mac.addr);
627e4d4759SShannon Nelson 					break;
637e4d4759SShannon Nelson 				}
64cc4428c4SShannon Nelson 				spin_lock_bh(&lif->rx_filters.lock);
65cc4428c4SShannon Nelson 				ionic_rx_filter_free(lif, f);
66cc4428c4SShannon Nelson 				spin_unlock_bh(&lif->rx_filters.lock);
67cc4428c4SShannon Nelson 
68cc4428c4SShannon Nelson 				continue;
69cc4428c4SShannon Nelson 			}
70cc4428c4SShannon Nelson 
71cc4428c4SShannon Nelson 			/* remove from old id list, save new id in tmp list */
72cc4428c4SShannon Nelson 			spin_lock_bh(&lif->rx_filters.lock);
73cc4428c4SShannon Nelson 			hlist_del(&f->by_id);
74cc4428c4SShannon Nelson 			spin_unlock_bh(&lif->rx_filters.lock);
75cc4428c4SShannon Nelson 			f->filter_id = le32_to_cpu(ctx.comp.rx_filter_add.filter_id);
76cc4428c4SShannon Nelson 			hlist_add_head(&f->by_id, &new_id_list);
777e4d4759SShannon Nelson 		}
787e4d4759SShannon Nelson 	}
79cc4428c4SShannon Nelson 
80cc4428c4SShannon Nelson 	/* rebuild the by_id hash lists with the new filter ids */
81cc4428c4SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
82cc4428c4SShannon Nelson 	hlist_for_each_entry_safe(f, tmp, &new_id_list, by_id) {
83cc4428c4SShannon Nelson 		key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
84cc4428c4SShannon Nelson 		head = &lif->rx_filters.by_id[key];
85cc4428c4SShannon Nelson 		hlist_add_head(&f->by_id, head);
867e4d4759SShannon Nelson 	}
87cc4428c4SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
88c1e329ebSShannon Nelson }
89c1e329ebSShannon Nelson 
90c1e329ebSShannon Nelson int ionic_rx_filters_init(struct ionic_lif *lif)
91c1e329ebSShannon Nelson {
92c1e329ebSShannon Nelson 	unsigned int i;
93c1e329ebSShannon Nelson 
94c1e329ebSShannon Nelson 	spin_lock_init(&lif->rx_filters.lock);
95c1e329ebSShannon Nelson 
96cbec2153SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
97c1e329ebSShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
98c1e329ebSShannon Nelson 		INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
99c1e329ebSShannon Nelson 		INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
100c1e329ebSShannon Nelson 	}
101cbec2153SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
102c1e329ebSShannon Nelson 
103c1e329ebSShannon Nelson 	return 0;
104c1e329ebSShannon Nelson }
105c1e329ebSShannon Nelson 
106c1e329ebSShannon Nelson void ionic_rx_filters_deinit(struct ionic_lif *lif)
107c1e329ebSShannon Nelson {
108c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
109c1e329ebSShannon Nelson 	struct hlist_head *head;
110c1e329ebSShannon Nelson 	struct hlist_node *tmp;
111c1e329ebSShannon Nelson 	unsigned int i;
112c1e329ebSShannon Nelson 
113cbec2153SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
114c1e329ebSShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
115c1e329ebSShannon Nelson 		head = &lif->rx_filters.by_id[i];
116c1e329ebSShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id)
117c1e329ebSShannon Nelson 			ionic_rx_filter_free(lif, f);
118c1e329ebSShannon Nelson 	}
119cbec2153SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
120c1e329ebSShannon Nelson }
121c1e329ebSShannon Nelson 
122c1e329ebSShannon Nelson int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
123c1e329ebSShannon Nelson 			 u32 hash, struct ionic_admin_ctx *ctx)
124c1e329ebSShannon Nelson {
125c1e329ebSShannon Nelson 	struct device *dev = lif->ionic->dev;
126c1e329ebSShannon Nelson 	struct ionic_rx_filter_add_cmd *ac;
127c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
128c1e329ebSShannon Nelson 	struct hlist_head *head;
129c1e329ebSShannon Nelson 	unsigned int key;
130c1e329ebSShannon Nelson 
131c1e329ebSShannon Nelson 	ac = &ctx->cmd.rx_filter_add;
132c1e329ebSShannon Nelson 
133c1e329ebSShannon Nelson 	switch (le16_to_cpu(ac->match)) {
134c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
135c1e329ebSShannon Nelson 		key = le16_to_cpu(ac->vlan.vlan);
136c1e329ebSShannon Nelson 		break;
137c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
138c1e329ebSShannon Nelson 		key = *(u32 *)ac->mac.addr;
139c1e329ebSShannon Nelson 		break;
140c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC_VLAN:
141c1e329ebSShannon Nelson 		key = le16_to_cpu(ac->mac_vlan.vlan);
142c1e329ebSShannon Nelson 		break;
143*ab470bbeSShannon Nelson 	case IONIC_RX_FILTER_STEER_PKTCLASS:
144*ab470bbeSShannon Nelson 		key = 0;
145*ab470bbeSShannon Nelson 		break;
146c1e329ebSShannon Nelson 	default:
147c1e329ebSShannon Nelson 		return -EINVAL;
148c1e329ebSShannon Nelson 	}
149c1e329ebSShannon Nelson 
150c1e329ebSShannon Nelson 	f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
151c1e329ebSShannon Nelson 	if (!f)
152c1e329ebSShannon Nelson 		return -ENOMEM;
153c1e329ebSShannon Nelson 
154c1e329ebSShannon Nelson 	f->flow_id = flow_id;
155c1e329ebSShannon Nelson 	f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
156c1e329ebSShannon Nelson 	f->rxq_index = rxq_index;
157c1e329ebSShannon Nelson 	memcpy(&f->cmd, ac, sizeof(f->cmd));
158cbec2153SShannon Nelson 	netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
159c1e329ebSShannon Nelson 
160c1e329ebSShannon Nelson 	INIT_HLIST_NODE(&f->by_hash);
161c1e329ebSShannon Nelson 	INIT_HLIST_NODE(&f->by_id);
162c1e329ebSShannon Nelson 
163c1e329ebSShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
164c1e329ebSShannon Nelson 
165c1e329ebSShannon Nelson 	key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
166c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
167c1e329ebSShannon Nelson 	hlist_add_head(&f->by_hash, head);
168c1e329ebSShannon Nelson 
169c1e329ebSShannon Nelson 	key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
170c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_id[key];
171c1e329ebSShannon Nelson 	hlist_add_head(&f->by_id, head);
172c1e329ebSShannon Nelson 
173c1e329ebSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
174c1e329ebSShannon Nelson 
175c1e329ebSShannon Nelson 	return 0;
176c1e329ebSShannon Nelson }
177c1e329ebSShannon Nelson 
178c1e329ebSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid)
179c1e329ebSShannon Nelson {
180c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
181c1e329ebSShannon Nelson 	struct hlist_head *head;
182c1e329ebSShannon Nelson 	unsigned int key;
183c1e329ebSShannon Nelson 
184c1e329ebSShannon Nelson 	key = hash_32(vid, IONIC_RX_FILTER_HASH_BITS);
185c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
186c1e329ebSShannon Nelson 
187c1e329ebSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
188c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_VLAN)
189c1e329ebSShannon Nelson 			continue;
190c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
191c1e329ebSShannon Nelson 			return f;
192c1e329ebSShannon Nelson 	}
193c1e329ebSShannon Nelson 
194c1e329ebSShannon Nelson 	return NULL;
195c1e329ebSShannon Nelson }
196c1e329ebSShannon Nelson 
197c1e329ebSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
198c1e329ebSShannon Nelson 						const u8 *addr)
199c1e329ebSShannon Nelson {
200c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
201c1e329ebSShannon Nelson 	struct hlist_head *head;
202c1e329ebSShannon Nelson 	unsigned int key;
203c1e329ebSShannon Nelson 
204c1e329ebSShannon Nelson 	key = hash_32(*(u32 *)addr, IONIC_RX_FILTER_HASH_BITS);
205c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
206c1e329ebSShannon Nelson 
207c1e329ebSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
208c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_MAC)
209c1e329ebSShannon Nelson 			continue;
210c1e329ebSShannon Nelson 		if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
211c1e329ebSShannon Nelson 			return f;
212c1e329ebSShannon Nelson 	}
213c1e329ebSShannon Nelson 
214c1e329ebSShannon Nelson 	return NULL;
215c1e329ebSShannon Nelson }
216*ab470bbeSShannon Nelson 
217*ab470bbeSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
218*ab470bbeSShannon Nelson {
219*ab470bbeSShannon Nelson 	struct ionic_rx_filter *f;
220*ab470bbeSShannon Nelson 	struct hlist_head *head;
221*ab470bbeSShannon Nelson 	unsigned int key;
222*ab470bbeSShannon Nelson 
223*ab470bbeSShannon Nelson 	key = hash_32(0, IONIC_RX_FILTER_HASH_BITS);
224*ab470bbeSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
225*ab470bbeSShannon Nelson 
226*ab470bbeSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
227*ab470bbeSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_STEER_PKTCLASS)
228*ab470bbeSShannon Nelson 			continue;
229*ab470bbeSShannon Nelson 		return f;
230*ab470bbeSShannon Nelson 	}
231*ab470bbeSShannon Nelson 
232*ab470bbeSShannon Nelson 	return NULL;
233*ab470bbeSShannon Nelson }
234