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