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 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 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 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 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 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 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 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 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 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 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 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. 340*9b0b6ba6SShannon Nelson * Since the FW doesn't have a way to tell us the vlan limit, 341*9b0b6ba6SShannon 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)) { 344*9b0b6ba6SShannon Nelson case IONIC_RX_FILTER_MATCH_VLAN: 345*9b0b6ba6SShannon Nelson netdev_dbg(lif->netdev, "%s: rx_filter add VLAN %d\n", 346*9b0b6ba6SShannon Nelson __func__, ctx.cmd.rx_filter_add.vlan.vlan); 347*9b0b6ba6SShannon Nelson if (lif->max_vlans && lif->nvlans >= lif->max_vlans) 348*9b0b6ba6SShannon Nelson err = -ENOSPC; 349*9b0b6ba6SShannon 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) 3601d4ddc4aSShannon Nelson err = ionic_adminq_post_wait(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 379*9b0b6ba6SShannon Nelson if (err == -ENOSPC) { 380*9b0b6ba6SShannon Nelson if (le16_to_cpu(ctx.cmd.rx_filter_add.match) == IONIC_RX_FILTER_MATCH_VLAN) 381*9b0b6ba6SShannon Nelson lif->max_vlans = lif->nvlans; 3821d4ddc4aSShannon Nelson return 0; 383*9b0b6ba6SShannon Nelson } 384eba688b1SShannon Nelson 3851d4ddc4aSShannon Nelson return err; 3861d4ddc4aSShannon Nelson } 3871d4ddc4aSShannon Nelson 388eba688b1SShannon Nelson switch (le16_to_cpu(ctx.cmd.rx_filter_add.match)) { 389*9b0b6ba6SShannon Nelson case IONIC_RX_FILTER_MATCH_VLAN: 390*9b0b6ba6SShannon Nelson lif->nvlans++; 391*9b0b6ba6SShannon Nelson break; 392eba688b1SShannon Nelson case IONIC_RX_FILTER_MATCH_MAC: 393eba688b1SShannon Nelson if (is_multicast_ether_addr(ctx.cmd.rx_filter_add.mac.addr)) 3941d4ddc4aSShannon Nelson lif->nmcast++; 3951d4ddc4aSShannon Nelson else 3961d4ddc4aSShannon Nelson lif->nucast++; 397eba688b1SShannon Nelson break; 398eba688b1SShannon Nelson } 3991d4ddc4aSShannon Nelson 400ff542fbeSShannon Nelson f = ionic_rx_filter_find(lif, &ctx.cmd.rx_filter_add); 4011d4ddc4aSShannon Nelson if (f && f->state == IONIC_FILTER_STATE_OLD) { 4021d4ddc4aSShannon Nelson /* Someone requested a delete while we were adding 4031d4ddc4aSShannon Nelson * so update the filter info with the results from the add 4041d4ddc4aSShannon Nelson * and the data will be there for the delete on the next 4051d4ddc4aSShannon Nelson * sync cycle. 4061d4ddc4aSShannon Nelson */ 4071d4ddc4aSShannon Nelson err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx, 4081d4ddc4aSShannon Nelson IONIC_FILTER_STATE_OLD); 4091d4ddc4aSShannon Nelson } else { 4101d4ddc4aSShannon Nelson err = ionic_rx_filter_save(lif, 0, IONIC_RXQ_INDEX_ANY, 0, &ctx, 4111d4ddc4aSShannon Nelson IONIC_FILTER_STATE_SYNCED); 4121d4ddc4aSShannon Nelson } 4131d4ddc4aSShannon Nelson 4141d4ddc4aSShannon Nelson spin_unlock_bh(&lif->rx_filters.lock); 4151d4ddc4aSShannon Nelson 4161d4ddc4aSShannon Nelson return err; 4171d4ddc4aSShannon Nelson } 4181d4ddc4aSShannon Nelson 419eba688b1SShannon Nelson int ionic_lif_addr_add(struct ionic_lif *lif, const u8 *addr) 420eba688b1SShannon Nelson { 421eba688b1SShannon Nelson struct ionic_rx_filter_add_cmd ac = { 422eba688b1SShannon Nelson .match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC), 423eba688b1SShannon Nelson }; 424eba688b1SShannon Nelson 425eba688b1SShannon Nelson memcpy(&ac.mac.addr, addr, ETH_ALEN); 426eba688b1SShannon Nelson 427eba688b1SShannon Nelson return ionic_lif_filter_add(lif, &ac); 428eba688b1SShannon Nelson } 429eba688b1SShannon Nelson 430*9b0b6ba6SShannon Nelson int ionic_lif_vlan_add(struct ionic_lif *lif, const u16 vid) 431*9b0b6ba6SShannon Nelson { 432*9b0b6ba6SShannon Nelson struct ionic_rx_filter_add_cmd ac = { 433*9b0b6ba6SShannon Nelson .match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN), 434*9b0b6ba6SShannon Nelson .vlan.vlan = cpu_to_le16(vid), 435*9b0b6ba6SShannon Nelson }; 436*9b0b6ba6SShannon Nelson 437*9b0b6ba6SShannon Nelson return ionic_lif_filter_add(lif, &ac); 438*9b0b6ba6SShannon Nelson } 439*9b0b6ba6SShannon Nelson 440c2b63d34SShannon Nelson static int ionic_lif_filter_del(struct ionic_lif *lif, 441c2b63d34SShannon Nelson struct ionic_rx_filter_add_cmd *ac) 4421d4ddc4aSShannon Nelson { 4431d4ddc4aSShannon Nelson struct ionic_admin_ctx ctx = { 4441d4ddc4aSShannon Nelson .work = COMPLETION_INITIALIZER_ONSTACK(ctx.work), 4451d4ddc4aSShannon Nelson .cmd.rx_filter_del = { 4461d4ddc4aSShannon Nelson .opcode = IONIC_CMD_RX_FILTER_DEL, 4471d4ddc4aSShannon Nelson .lif_index = cpu_to_le16(lif->index), 4481d4ddc4aSShannon Nelson }, 4491d4ddc4aSShannon Nelson }; 4501d4ddc4aSShannon Nelson struct ionic_rx_filter *f; 4511d4ddc4aSShannon Nelson int state; 4521d4ddc4aSShannon Nelson int err; 4531d4ddc4aSShannon Nelson 4541d4ddc4aSShannon Nelson spin_lock_bh(&lif->rx_filters.lock); 455c2b63d34SShannon Nelson f = ionic_rx_filter_find(lif, ac); 4561d4ddc4aSShannon Nelson if (!f) { 4571d4ddc4aSShannon Nelson spin_unlock_bh(&lif->rx_filters.lock); 4581d4ddc4aSShannon Nelson return -ENOENT; 4591d4ddc4aSShannon Nelson } 4601d4ddc4aSShannon Nelson 461c2b63d34SShannon Nelson switch (le16_to_cpu(ac->match)) { 462*9b0b6ba6SShannon Nelson case IONIC_RX_FILTER_MATCH_VLAN: 463*9b0b6ba6SShannon Nelson netdev_dbg(lif->netdev, "%s: rx_filter del VLAN %d id %d\n", 464*9b0b6ba6SShannon Nelson __func__, ac->vlan.vlan, f->filter_id); 465*9b0b6ba6SShannon Nelson lif->nvlans--; 466*9b0b6ba6SShannon Nelson break; 467c2b63d34SShannon Nelson case IONIC_RX_FILTER_MATCH_MAC: 468c2b63d34SShannon Nelson netdev_dbg(lif->netdev, "%s: rx_filter del ADDR %pM id %d\n", 469c2b63d34SShannon Nelson __func__, ac->mac.addr, f->filter_id); 470c2b63d34SShannon Nelson if (is_multicast_ether_addr(ac->mac.addr) && lif->nmcast) 471c2b63d34SShannon Nelson lif->nmcast--; 472c2b63d34SShannon Nelson else if (!is_multicast_ether_addr(ac->mac.addr) && lif->nucast) 473c2b63d34SShannon Nelson lif->nucast--; 474c2b63d34SShannon Nelson break; 475c2b63d34SShannon Nelson } 4761d4ddc4aSShannon Nelson 4771d4ddc4aSShannon Nelson state = f->state; 4781d4ddc4aSShannon Nelson ctx.cmd.rx_filter_del.filter_id = cpu_to_le32(f->filter_id); 4791d4ddc4aSShannon Nelson ionic_rx_filter_free(lif, f); 4801d4ddc4aSShannon Nelson 4811d4ddc4aSShannon Nelson spin_unlock_bh(&lif->rx_filters.lock); 4821d4ddc4aSShannon Nelson 4831d4ddc4aSShannon Nelson if (state != IONIC_FILTER_STATE_NEW) { 4841d4ddc4aSShannon Nelson err = ionic_adminq_post_wait(lif, &ctx); 4851d4ddc4aSShannon Nelson if (err && err != -EEXIST) 4861d4ddc4aSShannon Nelson return err; 4871d4ddc4aSShannon Nelson } 4881d4ddc4aSShannon Nelson 4891d4ddc4aSShannon Nelson return 0; 4901d4ddc4aSShannon Nelson } 4911d4ddc4aSShannon Nelson 492c2b63d34SShannon Nelson int ionic_lif_addr_del(struct ionic_lif *lif, const u8 *addr) 493c2b63d34SShannon Nelson { 494c2b63d34SShannon Nelson struct ionic_rx_filter_add_cmd ac = { 495c2b63d34SShannon Nelson .match = cpu_to_le16(IONIC_RX_FILTER_MATCH_MAC), 496c2b63d34SShannon Nelson }; 497c2b63d34SShannon Nelson 498c2b63d34SShannon Nelson memcpy(&ac.mac.addr, addr, ETH_ALEN); 499c2b63d34SShannon Nelson 500c2b63d34SShannon Nelson return ionic_lif_filter_del(lif, &ac); 501c2b63d34SShannon Nelson } 502c2b63d34SShannon Nelson 503*9b0b6ba6SShannon Nelson int ionic_lif_vlan_del(struct ionic_lif *lif, const u16 vid) 504*9b0b6ba6SShannon Nelson { 505*9b0b6ba6SShannon Nelson struct ionic_rx_filter_add_cmd ac = { 506*9b0b6ba6SShannon Nelson .match = cpu_to_le16(IONIC_RX_FILTER_MATCH_VLAN), 507*9b0b6ba6SShannon Nelson .vlan.vlan = cpu_to_le16(vid), 508*9b0b6ba6SShannon Nelson }; 509*9b0b6ba6SShannon Nelson 510*9b0b6ba6SShannon Nelson return ionic_lif_filter_del(lif, &ac); 511*9b0b6ba6SShannon Nelson } 512*9b0b6ba6SShannon Nelson 513969f8439SShannon Nelson struct sync_item { 514969f8439SShannon Nelson struct list_head list; 515969f8439SShannon Nelson struct ionic_rx_filter f; 516969f8439SShannon Nelson }; 517969f8439SShannon Nelson 518969f8439SShannon Nelson void ionic_rx_filter_sync(struct ionic_lif *lif) 519969f8439SShannon Nelson { 520969f8439SShannon Nelson struct device *dev = lif->ionic->dev; 521969f8439SShannon Nelson struct list_head sync_add_list; 522969f8439SShannon Nelson struct list_head sync_del_list; 523969f8439SShannon Nelson struct sync_item *sync_item; 524969f8439SShannon Nelson struct ionic_rx_filter *f; 525969f8439SShannon Nelson struct hlist_head *head; 526969f8439SShannon Nelson struct hlist_node *tmp; 527969f8439SShannon Nelson struct sync_item *spos; 528969f8439SShannon Nelson unsigned int i; 529969f8439SShannon Nelson 530969f8439SShannon Nelson INIT_LIST_HEAD(&sync_add_list); 531969f8439SShannon Nelson INIT_LIST_HEAD(&sync_del_list); 532969f8439SShannon Nelson 533969f8439SShannon Nelson clear_bit(IONIC_LIF_F_FILTER_SYNC_NEEDED, lif->state); 534969f8439SShannon Nelson 535969f8439SShannon Nelson /* Copy the filters to be added and deleted 536969f8439SShannon Nelson * into a separate local list that needs no locking. 537969f8439SShannon Nelson */ 538969f8439SShannon Nelson spin_lock_bh(&lif->rx_filters.lock); 539969f8439SShannon Nelson for (i = 0; i < IONIC_RX_FILTER_HLISTS; i++) { 540969f8439SShannon Nelson head = &lif->rx_filters.by_id[i]; 541969f8439SShannon Nelson hlist_for_each_entry_safe(f, tmp, head, by_id) { 542969f8439SShannon Nelson if (f->state == IONIC_FILTER_STATE_NEW || 543969f8439SShannon Nelson f->state == IONIC_FILTER_STATE_OLD) { 544969f8439SShannon Nelson sync_item = devm_kzalloc(dev, sizeof(*sync_item), 54552a67fbfSDan Carpenter GFP_ATOMIC); 546969f8439SShannon Nelson if (!sync_item) 547969f8439SShannon Nelson goto loop_out; 548969f8439SShannon Nelson 549969f8439SShannon Nelson sync_item->f = *f; 550969f8439SShannon Nelson 551969f8439SShannon Nelson if (f->state == IONIC_FILTER_STATE_NEW) 552969f8439SShannon Nelson list_add(&sync_item->list, &sync_add_list); 553969f8439SShannon Nelson else 554969f8439SShannon Nelson list_add(&sync_item->list, &sync_del_list); 555969f8439SShannon Nelson } 556969f8439SShannon Nelson } 557969f8439SShannon Nelson } 558969f8439SShannon Nelson loop_out: 559969f8439SShannon Nelson spin_unlock_bh(&lif->rx_filters.lock); 560969f8439SShannon Nelson 561969f8439SShannon Nelson /* If the add or delete fails, it won't get marked as sync'd 562969f8439SShannon Nelson * and will be tried again in the next sync action. 563969f8439SShannon Nelson * Do the deletes first in case we're in an overflow state and 564969f8439SShannon Nelson * they can clear room for some new filters 565969f8439SShannon Nelson */ 566969f8439SShannon Nelson list_for_each_entry_safe(sync_item, spos, &sync_del_list, list) { 567c2b63d34SShannon Nelson (void)ionic_lif_filter_del(lif, &sync_item->f.cmd); 568969f8439SShannon Nelson 569969f8439SShannon Nelson list_del(&sync_item->list); 570969f8439SShannon Nelson devm_kfree(dev, sync_item); 571969f8439SShannon Nelson } 572969f8439SShannon Nelson 573969f8439SShannon Nelson list_for_each_entry_safe(sync_item, spos, &sync_add_list, list) { 574eba688b1SShannon Nelson (void)ionic_lif_filter_add(lif, &sync_item->f.cmd); 575969f8439SShannon Nelson 576969f8439SShannon Nelson list_del(&sync_item->list); 577969f8439SShannon Nelson devm_kfree(dev, sync_item); 578969f8439SShannon Nelson } 579969f8439SShannon Nelson } 580