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