linux/drivers/net/ethernet/pensando/ionic/ionic_rx_filter.c
<<
>>
Prefs
   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
  12void 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
  21void 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
  90int 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
 106void 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
 122int 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        case IONIC_RX_FILTER_STEER_PKTCLASS:
 144                key = 0;
 145                break;
 146        default:
 147                return -EINVAL;
 148        }
 149
 150        f = devm_kzalloc(dev, sizeof(*f), GFP_KERNEL);
 151        if (!f)
 152                return -ENOMEM;
 153
 154        f->flow_id = flow_id;
 155        f->filter_id = le32_to_cpu(ctx->comp.rx_filter_add.filter_id);
 156        f->rxq_index = rxq_index;
 157        memcpy(&f->cmd, ac, sizeof(f->cmd));
 158        netdev_dbg(lif->netdev, "rx_filter add filter_id %d\n", f->filter_id);
 159
 160        INIT_HLIST_NODE(&f->by_hash);
 161        INIT_HLIST_NODE(&f->by_id);
 162
 163        spin_lock_bh(&lif->rx_filters.lock);
 164
 165        key = hash_32(key, IONIC_RX_FILTER_HASH_BITS);
 166        head = &lif->rx_filters.by_hash[key];
 167        hlist_add_head(&f->by_hash, head);
 168
 169        key = f->filter_id & IONIC_RX_FILTER_HLISTS_MASK;
 170        head = &lif->rx_filters.by_id[key];
 171        hlist_add_head(&f->by_id, head);
 172
 173        spin_unlock_bh(&lif->rx_filters.lock);
 174
 175        return 0;
 176}
 177
 178struct ionic_rx_filter *ionic_rx_filter_by_vlan(struct ionic_lif *lif, u16 vid)
 179{
 180        struct ionic_rx_filter *f;
 181        struct hlist_head *head;
 182        unsigned int key;
 183
 184        key = hash_32(vid, IONIC_RX_FILTER_HASH_BITS);
 185        head = &lif->rx_filters.by_hash[key];
 186
 187        hlist_for_each_entry(f, head, by_hash) {
 188                if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_VLAN)
 189                        continue;
 190                if (le16_to_cpu(f->cmd.vlan.vlan) == vid)
 191                        return f;
 192        }
 193
 194        return NULL;
 195}
 196
 197struct ionic_rx_filter *ionic_rx_filter_by_addr(struct ionic_lif *lif,
 198                                                const u8 *addr)
 199{
 200        struct ionic_rx_filter *f;
 201        struct hlist_head *head;
 202        unsigned int key;
 203
 204        key = hash_32(*(u32 *)addr, IONIC_RX_FILTER_HASH_BITS);
 205        head = &lif->rx_filters.by_hash[key];
 206
 207        hlist_for_each_entry(f, head, by_hash) {
 208                if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_MATCH_MAC)
 209                        continue;
 210                if (memcmp(addr, f->cmd.mac.addr, ETH_ALEN) == 0)
 211                        return f;
 212        }
 213
 214        return NULL;
 215}
 216
 217struct ionic_rx_filter *ionic_rx_filter_rxsteer(struct ionic_lif *lif)
 218{
 219        struct ionic_rx_filter *f;
 220        struct hlist_head *head;
 221        unsigned int key;
 222
 223        key = hash_32(0, IONIC_RX_FILTER_HASH_BITS);
 224        head = &lif->rx_filters.by_hash[key];
 225
 226        hlist_for_each_entry(f, head, by_hash) {
 227                if (le16_to_cpu(f->cmd.match) != IONIC_RX_FILTER_STEER_PKTCLASS)
 228                        continue;
 229                return f;
 230        }
 231
 232        return NULL;
 233}
 234