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;
247e4d4759SShannon Nelson 	struct ionic_admin_ctx ctx;
257e4d4759SShannon Nelson 	struct ionic_rx_filter *f;
267e4d4759SShannon Nelson 	struct hlist_head *head;
277e4d4759SShannon Nelson 	struct hlist_node *tmp;
287e4d4759SShannon Nelson 	unsigned int i;
292c0df9f9SShannon Nelson 	int err;
30c1e329ebSShannon Nelson 
317e4d4759SShannon Nelson 	ac = &ctx.cmd.rx_filter_add;
327e4d4759SShannon Nelson 
337e4d4759SShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
347e4d4759SShannon Nelson 		head = &lif->rx_filters.by_id[i];
357e4d4759SShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id) {
367e4d4759SShannon Nelson 			ctx.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work);
377e4d4759SShannon Nelson 			memcpy(ac, &f->cmd, sizeof(f->cmd));
387e4d4759SShannon Nelson 			dev_dbg(&lif->netdev->dev, "replay filter command:\n");
397e4d4759SShannon Nelson 			dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
407e4d4759SShannon Nelson 					 &ctx.cmd, sizeof(ctx.cmd), true);
417e4d4759SShannon Nelson 
427e4d4759SShannon Nelson 			err = ionic_adminq_post_wait(lif, &ctx);
437e4d4759SShannon Nelson 			if (err) {
447e4d4759SShannon Nelson 				switch (le16_to_cpu(ac->match)) {
457e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_VLAN:
467e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d\n",
477e4d4759SShannon Nelson 						    err,
487e4d4759SShannon Nelson 						    le16_to_cpu(ac->vlan.vlan));
497e4d4759SShannon Nelson 					break;
507e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_MAC:
517e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: mac %pM\n",
527e4d4759SShannon Nelson 						    err, ac->mac.addr);
537e4d4759SShannon Nelson 					break;
547e4d4759SShannon Nelson 				case IONIC_RX_FILTER_MATCH_MAC_VLAN:
557e4d4759SShannon Nelson 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d mac %pM\n",
567e4d4759SShannon Nelson 						    err,
577e4d4759SShannon Nelson 						    le16_to_cpu(ac->vlan.vlan),
587e4d4759SShannon Nelson 						    ac->mac.addr);
597e4d4759SShannon Nelson 					break;
607e4d4759SShannon Nelson 				}
617e4d4759SShannon Nelson 			}
627e4d4759SShannon Nelson 		}
637e4d4759SShannon Nelson 	}
64c1e329ebSShannon Nelson }
65c1e329ebSShannon Nelson 
66c1e329ebSShannon Nelson int ionic_rx_filters_init(struct ionic_lif *lif)
67c1e329ebSShannon Nelson {
68c1e329ebSShannon Nelson 	unsigned int i;
69c1e329ebSShannon Nelson 
70c1e329ebSShannon Nelson 	spin_lock_init(&lif->rx_filters.lock);
71c1e329ebSShannon Nelson 
72cbec2153SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
73c1e329ebSShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
74c1e329ebSShannon Nelson 		INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
75c1e329ebSShannon Nelson 		INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
76c1e329ebSShannon Nelson 	}
77cbec2153SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
78c1e329ebSShannon Nelson 
79c1e329ebSShannon Nelson 	return 0;
80c1e329ebSShannon Nelson }
81c1e329ebSShannon Nelson 
82c1e329ebSShannon Nelson void ionic_rx_filters_deinit(struct ionic_lif *lif)
83c1e329ebSShannon Nelson {
84c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
85c1e329ebSShannon Nelson 	struct hlist_head *head;
86c1e329ebSShannon Nelson 	struct hlist_node *tmp;
87c1e329ebSShannon Nelson 	unsigned int i;
88c1e329ebSShannon Nelson 
89cbec2153SShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
90c1e329ebSShannon Nelson 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
91c1e329ebSShannon Nelson 		head = &lif->rx_filters.by_id[i];
92c1e329ebSShannon Nelson 		hlist_for_each_entry_safe(f, tmp, head, by_id)
93c1e329ebSShannon Nelson 			ionic_rx_filter_free(lif, f);
94c1e329ebSShannon Nelson 	}
95cbec2153SShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
96c1e329ebSShannon Nelson }
97c1e329ebSShannon Nelson 
98c1e329ebSShannon Nelson int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
99c1e329ebSShannon Nelson 			 u32 hash, struct ionic_admin_ctx *ctx)
100c1e329ebSShannon Nelson {
101c1e329ebSShannon Nelson 	struct device *dev = lif->ionic->dev;
102c1e329ebSShannon Nelson 	struct ionic_rx_filter_add_cmd *ac;
103c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
104c1e329ebSShannon Nelson 	struct hlist_head *head;
105c1e329ebSShannon Nelson 	unsigned int key;
106c1e329ebSShannon Nelson 
107c1e329ebSShannon Nelson 	ac = &ctx->cmd.rx_filter_add;
108c1e329ebSShannon Nelson 
109c1e329ebSShannon Nelson 	switch (le16_to_cpu(ac->match)) {
110c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_VLAN:
111c1e329ebSShannon Nelson 		key = le16_to_cpu(ac->vlan.vlan);
112c1e329ebSShannon Nelson 		break;
113c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC:
114c1e329ebSShannon Nelson 		key = *(u32 *)ac->mac.addr;
115c1e329ebSShannon Nelson 		break;
116c1e329ebSShannon Nelson 	case IONIC_RX_FILTER_MATCH_MAC_VLAN:
117c1e329ebSShannon Nelson 		key = le16_to_cpu(ac->mac_vlan.vlan);
118c1e329ebSShannon Nelson 		break;
119c1e329ebSShannon Nelson 	default:
120c1e329ebSShannon Nelson 		return -EINVAL;
121c1e329ebSShannon Nelson 	}
122c1e329ebSShannon Nelson 
123c1e329ebSShannon Nelson 	f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
124c1e329ebSShannon Nelson 	if (!f)
125c1e329ebSShannon Nelson 		return -ENOMEM;
126c1e329ebSShannon Nelson 
127c1e329ebSShannon Nelson 	f->flow_id = flow_id;
128c1e329ebSShannon Nelson 	f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
129c1e329ebSShannon Nelson 	f->rxq_index = rxq_index;
130c1e329ebSShannon Nelson 	memcpy(&f->cmd, ac, sizeof(f->cmd));
131cbec2153SShannon Nelson 	netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
132c1e329ebSShannon Nelson 
133c1e329ebSShannon Nelson 	INIT_HLIST_NODE(&f->by_hash);
134c1e329ebSShannon Nelson 	INIT_HLIST_NODE(&f->by_id);
135c1e329ebSShannon Nelson 
136c1e329ebSShannon Nelson 	spin_lock_bh(&lif->rx_filters.lock);
137c1e329ebSShannon Nelson 
138c1e329ebSShannon Nelson 	key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
139c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
140c1e329ebSShannon Nelson 	hlist_add_head(&f->by_hash, head);
141c1e329ebSShannon Nelson 
142c1e329ebSShannon Nelson 	key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
143c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_id[key];
144c1e329ebSShannon Nelson 	hlist_add_head(&f->by_id, head);
145c1e329ebSShannon Nelson 
146c1e329ebSShannon Nelson 	spin_unlock_bh(&lif->rx_filters.lock);
147c1e329ebSShannon Nelson 
148c1e329ebSShannon Nelson 	return 0;
149c1e329ebSShannon Nelson }
150c1e329ebSShannon Nelson 
151c1e329ebSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid)
152c1e329ebSShannon Nelson {
153c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
154c1e329ebSShannon Nelson 	struct hlist_head *head;
155c1e329ebSShannon Nelson 	unsigned int key;
156c1e329ebSShannon Nelson 
157c1e329ebSShannon Nelson 	key = hash_32(vid, IONIC_RX_FILTER_HASH_BITS);
158c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
159c1e329ebSShannon Nelson 
160c1e329ebSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
161c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_VLAN)
162c1e329ebSShannon Nelson 			continue;
163c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
164c1e329ebSShannon Nelson 			return f;
165c1e329ebSShannon Nelson 	}
166c1e329ebSShannon Nelson 
167c1e329ebSShannon Nelson 	return NULL;
168c1e329ebSShannon Nelson }
169c1e329ebSShannon Nelson 
170c1e329ebSShannon Nelson struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
171c1e329ebSShannon Nelson 						const u8 *addr)
172c1e329ebSShannon Nelson {
173c1e329ebSShannon Nelson 	struct ionic_rx_filter *f;
174c1e329ebSShannon Nelson 	struct hlist_head *head;
175c1e329ebSShannon Nelson 	unsigned int key;
176c1e329ebSShannon Nelson 
177c1e329ebSShannon Nelson 	key = hash_32(*(u32 *)addr, IONIC_RX_FILTER_HASH_BITS);
178c1e329ebSShannon Nelson 	head = &lif->rx_filters.by_hash[key];
179c1e329ebSShannon Nelson 
180c1e329ebSShannon Nelson 	hlist_for_each_entry(f, head, by_hash) {
181c1e329ebSShannon Nelson 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_MAC)
182c1e329ebSShannon Nelson 			continue;
183c1e329ebSShannon Nelson 		if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
184c1e329ebSShannon Nelson 			return f;
185c1e329ebSShannon Nelson 	}
186c1e329ebSShannon Nelson 
187c1e329ebSShannon Nelson 	return NULL;
188c1e329ebSShannon Nelson }
189