1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2017 - 2019 Pensando Systems, Inc */
3 
4 #include <linux/netdevice.h>
5 #include <linux/dynamic_debug.h>
6 #include <linux/etherdevice.h>
7 
8 #include "ionic.h"
9 #include "ionic_lif.h"
10 #include "ionic_rx_filter.h"
11 
12 void ionic_rx_filter_free(struct ionic_lif *lif, struct ionic_rx_filter *f)
13 {
14 	struct device *dev = lif->ionic->dev;
15 
16 	hlist_del(&f->by_id);
17 	hlist_del(&f->by_hash);
18 	devm_kfree(dev, f);
19 }
20 
21 void ionic_rx_filter_replay(struct ionic_lif *lif)
22 {
23 	struct ionic_rx_filter_add_cmd *ac;
24 	struct hlist_head new_id_list;
25 	struct ionic_admin_ctx ctx;
26 	struct ionic_rx_filter *f;
27 	struct hlist_head *head;
28 	struct hlist_node *tmp;
29 	unsigned int key;
30 	unsigned int i;
31 	int err;
32 
33 	INIT_HLIST_HEAD(&new_id_list);
34 	ac = &ctx.cmd.rx_filter_add;
35 
36 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
37 		head = &lif->rx_filters.by_id[i];
38 		hlist_for_each_entry_safe(f, tmp, head, by_id) {
39 			ctx.work = COMPLETION_INITIALIZER_ONSTACK(ctx.work);
40 			memcpy(ac, &f->cmd, sizeof(f->cmd));
41 			dev_dbg(&lif->netdev->dev, "replay filter command:\n");
42 			dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1,
43 					 &ctx.cmd, sizeof(ctx.cmd), true);
44 
45 			err = ionic_adminq_post_wait(lif, &ctx);
46 			if (err) {
47 				switch (le16_to_cpu(ac->match)) {
48 				case IONIC_RX_FILTER_MATCH_VLAN:
49 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d\n",
50 						    err,
51 						    le16_to_cpu(ac->vlan.vlan));
52 					break;
53 				case IONIC_RX_FILTER_MATCH_MAC:
54 					netdev_info(lif->netdev, "Replay failed - %d: mac %pM\n",
55 						    err, ac->mac.addr);
56 					break;
57 				case IONIC_RX_FILTER_MATCH_MAC_VLAN:
58 					netdev_info(lif->netdev, "Replay failed - %d: vlan %d mac %pM\n",
59 						    err,
60 						    le16_to_cpu(ac->vlan.vlan),
61 						    ac->mac.addr);
62 					break;
63 				}
64 				spin_lock_bh(&lif->rx_filters.lock);
65 				ionic_rx_filter_free(lif, f);
66 				spin_unlock_bh(&lif->rx_filters.lock);
67 
68 				continue;
69 			}
70 
71 			/* remove from old id list, save new id in tmp list */
72 			spin_lock_bh(&lif->rx_filters.lock);
73 			hlist_del(&f->by_id);
74 			spin_unlock_bh(&lif->rx_filters.lock);
75 			f->filter_id = le32_to_cpu(ctx.comp.rx_filter_add.filter_id);
76 			hlist_add_head(&f->by_id, &new_id_list);
77 		}
78 	}
79 
80 	/* rebuild the by_id hash lists with the new filter ids */
81 	spin_lock_bh(&lif->rx_filters.lock);
82 	hlist_for_each_entry_safe(f, tmp, &new_id_list, by_id) {
83 		key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
84 		head = &lif->rx_filters.by_id[key];
85 		hlist_add_head(&f->by_id, head);
86 	}
87 	spin_unlock_bh(&lif->rx_filters.lock);
88 }
89 
90 int ionic_rx_filters_init(struct ionic_lif *lif)
91 {
92 	unsigned int i;
93 
94 	spin_lock_init(&lif->rx_filters.lock);
95 
96 	spin_lock_bh(&lif->rx_filters.lock);
97 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
98 		INIT_HLIST_HEAD(&lif->rx_filters.by_hash[i]);
99 		INIT_HLIST_HEAD(&lif->rx_filters.by_id[i]);
100 	}
101 	spin_unlock_bh(&lif->rx_filters.lock);
102 
103 	return 0;
104 }
105 
106 void ionic_rx_filters_deinit(struct ionic_lif *lif)
107 {
108 	struct ionic_rx_filter *f;
109 	struct hlist_head *head;
110 	struct hlist_node *tmp;
111 	unsigned int i;
112 
113 	spin_lock_bh(&lif->rx_filters.lock);
114 	for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) {
115 		head = &lif->rx_filters.by_id[i];
116 		hlist_for_each_entry_safe(f, tmp, head, by_id)
117 			ionic_rx_filter_free(lif, f);
118 	}
119 	spin_unlock_bh(&lif->rx_filters.lock);
120 }
121 
122 int ionic_rx_filter_save(struct ionic_lif *lif, u32 flow_id, u16 rxq_index,
123 			 u32 hash, struct ionic_admin_ctx *ctx)
124 {
125 	struct device *dev = lif->ionic->dev;
126 	struct ionic_rx_filter_add_cmd *ac;
127 	struct ionic_rx_filter *f;
128 	struct hlist_head *head;
129 	unsigned int key;
130 
131 	ac = &ctx->cmd.rx_filter_add;
132 
133 	switch (le16_to_cpu(ac->match)) {
134 	case IONIC_RX_FILTER_MATCH_VLAN:
135 		key = le16_to_cpu(ac->vlan.vlan);
136 		break;
137 	case IONIC_RX_FILTER_MATCH_MAC:
138 		key = *(u32 *)ac->mac.addr;
139 		break;
140 	case IONIC_RX_FILTER_MATCH_MAC_VLAN:
141 		key = le16_to_cpu(ac->mac_vlan.vlan);
142 		break;
143 	default:
144 		return -EINVAL;
145 	}
146 
147 	f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
148 	if (!f)
149 		return -ENOMEM;
150 
151 	f->flow_id = flow_id;
152 	f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
153 	f->rxq_index = rxq_index;
154 	memcpy(&f->cmd, ac, sizeof(f->cmd));
155 	netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
156 
157 	INIT_HLIST_NODE(&f->by_hash);
158 	INIT_HLIST_NODE(&f->by_id);
159 
160 	spin_lock_bh(&lif->rx_filters.lock);
161 
162 	key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
163 	head = &lif->rx_filters.by_hash[key];
164 	hlist_add_head(&f->by_hash, head);
165 
166 	key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
167 	head = &lif->rx_filters.by_id[key];
168 	hlist_add_head(&f->by_id, head);
169 
170 	spin_unlock_bh(&lif->rx_filters.lock);
171 
172 	return 0;
173 }
174 
175 struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid)
176 {
177 	struct ionic_rx_filter *f;
178 	struct hlist_head *head;
179 	unsigned int key;
180 
181 	key = hash_32(vid, IONIC_RX_FILTER_HASH_BITS);
182 	head = &lif->rx_filters.by_hash[key];
183 
184 	hlist_for_each_entry(f, head, by_hash) {
185 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_VLAN)
186 			continue;
187 		if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
188 			return f;
189 	}
190 
191 	return NULL;
192 }
193 
194 struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
195 						const u8 *addr)
196 {
197 	struct ionic_rx_filter *f;
198 	struct hlist_head *head;
199 	unsigned int key;
200 
201 	key = hash_32(*(u32 *)addr, IONIC_RX_FILTER_HASH_BITS);
202 	head = &lif->rx_filters.by_hash[key];
203 
204 	hlist_for_each_entry(f, head, by_hash) {
205 		if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_MAC)
206 			continue;
207 		if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
208 			return f;
209 	}
210 
211 	return NULL;
212 }
213