| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (c) Meta Platforms, Inc. and affiliates. */
- #include <linux/etherdevice.h>
- #include <linux/ethtool.h>
- #include <net/ipv6.h>
- #include "fbnic.h"
- #include "fbnic_fw.h"
- #include "fbnic_netdev.h"
- #include "fbnic_rpc.h"
- void fbnic_reset_indir_tbl(struct fbnic_net *fbn)
- {
- unsigned int num_rx = fbn->num_rx_queues;
- unsigned int i;
- if (netif_is_rxfh_configured(fbn->netdev))
- return;
- for (i = 0; i < FBNIC_RPC_RSS_TBL_SIZE; i++)
- fbn->indir_tbl[0][i] = ethtool_rxfh_indir_default(i, num_rx);
- }
- void fbnic_rss_key_fill(u32 *buffer)
- {
- static u32 rss_key[FBNIC_RPC_RSS_KEY_DWORD_LEN];
- net_get_random_once(rss_key, sizeof(rss_key));
- rss_key[FBNIC_RPC_RSS_KEY_LAST_IDX] &= FBNIC_RPC_RSS_KEY_LAST_MASK;
- memcpy(buffer, rss_key, sizeof(rss_key));
- }
- #define RX_HASH_OPT_L4 \
- (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)
- #define RX_HASH_OPT_L3 \
- (RXH_IP_SRC | RXH_IP_DST)
- #define RX_HASH_OPT_L2 RXH_L2DA
- void fbnic_rss_init_en_mask(struct fbnic_net *fbn)
- {
- fbn->rss_flow_hash[FBNIC_TCP4_HASH_OPT] = RX_HASH_OPT_L4;
- fbn->rss_flow_hash[FBNIC_TCP6_HASH_OPT] = RX_HASH_OPT_L4;
- fbn->rss_flow_hash[FBNIC_UDP4_HASH_OPT] = RX_HASH_OPT_L3;
- fbn->rss_flow_hash[FBNIC_UDP6_HASH_OPT] = RX_HASH_OPT_L3;
- fbn->rss_flow_hash[FBNIC_IPV4_HASH_OPT] = RX_HASH_OPT_L3;
- fbn->rss_flow_hash[FBNIC_IPV6_HASH_OPT] = RX_HASH_OPT_L3;
- fbn->rss_flow_hash[FBNIC_ETHER_HASH_OPT] = RX_HASH_OPT_L2;
- }
- void fbnic_rss_disable_hw(struct fbnic_dev *fbd)
- {
- /* Disable RPC by clearing enable bit and configuration */
- if (!fbnic_bmc_present(fbd))
- wr32(fbd, FBNIC_RPC_RMI_CONFIG,
- FIELD_PREP(FBNIC_RPC_RMI_CONFIG_OH_BYTES, 20));
- }
- #define FBNIC_FH_2_RSSEM_BIT(_fh, _rssem, _val) \
- FIELD_PREP(FBNIC_RPC_ACT_TBL1_RSS_ENA_##_rssem, \
- FIELD_GET(RXH_##_fh, _val))
- u16 fbnic_flow_hash_2_rss_en_mask(struct fbnic_net *fbn, int flow_type)
- {
- u32 flow_hash = fbn->rss_flow_hash[flow_type];
- u32 rss_en_mask = 0;
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(L2DA, L2_DA, flow_hash);
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(IP_SRC, IP_SRC, flow_hash);
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(IP_DST, IP_DST, flow_hash);
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(L4_B_0_1, L4_SRC, flow_hash);
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(L4_B_2_3, L4_DST, flow_hash);
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(IP6_FL, OV6_FL_LBL, flow_hash);
- rss_en_mask |= FBNIC_FH_2_RSSEM_BIT(IP6_FL, IV6_FL_LBL, flow_hash);
- return rss_en_mask;
- }
- void fbnic_rss_reinit_hw(struct fbnic_dev *fbd, struct fbnic_net *fbn)
- {
- unsigned int i;
- for (i = 0; i < FBNIC_RPC_RSS_TBL_SIZE; i++) {
- wr32(fbd, FBNIC_RPC_RSS_TBL(0, i), fbn->indir_tbl[0][i]);
- wr32(fbd, FBNIC_RPC_RSS_TBL(1, i), fbn->indir_tbl[1][i]);
- }
- for (i = 0; i < FBNIC_RPC_RSS_KEY_DWORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_RSS_KEY(i), fbn->rss_key[i]);
- /* Default action for this to drop w/ no destination */
- wr32(fbd, FBNIC_RPC_ACT_TBL0_DEFAULT, FBNIC_RPC_ACT_TBL0_DROP);
- wrfl(fbd);
- wr32(fbd, FBNIC_RPC_ACT_TBL1_DEFAULT, 0);
- /* If it isn't already enabled set the RMI Config value to enable RPC */
- wr32(fbd, FBNIC_RPC_RMI_CONFIG,
- FIELD_PREP(FBNIC_RPC_RMI_CONFIG_MTU, FBNIC_MAX_JUMBO_FRAME_SIZE) |
- FIELD_PREP(FBNIC_RPC_RMI_CONFIG_OH_BYTES, 20) |
- FBNIC_RPC_RMI_CONFIG_ENABLE);
- }
- void fbnic_bmc_rpc_all_multi_config(struct fbnic_dev *fbd,
- bool enable_host)
- {
- struct fbnic_act_tcam *act_tcam;
- struct fbnic_mac_addr *mac_addr;
- int j;
- /* We need to add the all multicast filter at the end of the
- * multicast address list. This way if there are any that are
- * shared between the host and the BMC they can be directed to
- * both. Otherwise the remainder just get sent directly to the
- * BMC.
- */
- mac_addr = &fbd->mac_addr[fbd->mac_addr_boundary - 1];
- if (fbnic_bmc_present(fbd) && fbd->fw_cap.all_multi) {
- if (mac_addr->state != FBNIC_TCAM_S_VALID) {
- eth_zero_addr(mac_addr->value.addr8);
- eth_broadcast_addr(mac_addr->mask.addr8);
- mac_addr->value.addr8[0] ^= 1;
- mac_addr->mask.addr8[0] ^= 1;
- set_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);
- mac_addr->state = FBNIC_TCAM_S_ADD;
- }
- if (enable_host)
- set_bit(FBNIC_MAC_ADDR_T_ALLMULTI,
- mac_addr->act_tcam);
- else
- clear_bit(FBNIC_MAC_ADDR_T_ALLMULTI,
- mac_addr->act_tcam);
- } else {
- __fbnic_xc_unsync(mac_addr, FBNIC_MAC_ADDR_T_BMC);
- __fbnic_xc_unsync(mac_addr, FBNIC_MAC_ADDR_T_ALLMULTI);
- }
- /* We have to add a special handler for multicast as the
- * BMC may have an all-multi rule already in place. As such
- * adding a rule ourselves won't do any good so we will have
- * to modify the rules for the ALL MULTI below if the BMC
- * already has the rule in place.
- */
- act_tcam = &fbd->act_tcam[FBNIC_RPC_ACT_TBL_BMC_ALL_MULTI_OFFSET];
- /* If we are not enabling the rule just delete it. We will fall
- * back to the RSS rules that support the multicast addresses.
- */
- if (!fbnic_bmc_present(fbd) || !fbd->fw_cap.all_multi || enable_host) {
- if (act_tcam->state == FBNIC_TCAM_S_VALID)
- act_tcam->state = FBNIC_TCAM_S_DELETE;
- return;
- }
- /* Rewrite TCAM rule 23 to handle BMC all-multi traffic */
- act_tcam->dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
- FBNIC_RPC_ACT_TBL0_DEST_BMC);
- act_tcam->mask.tcam[0] = 0xffff;
- /* MACDA 0 - 3 is reserved for the BMC MAC address */
- act_tcam->value.tcam[1] =
- FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,
- fbd->mac_addr_boundary - 1) |
- FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
- act_tcam->mask.tcam[1] = 0xffff &
- ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX &
- ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
- for (j = 2; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)
- act_tcam->mask.tcam[j] = 0xffff;
- act_tcam->state = FBNIC_TCAM_S_UPDATE;
- }
- void fbnic_bmc_rpc_init(struct fbnic_dev *fbd)
- {
- int i = FBNIC_RPC_TCAM_MACDA_BMC_ADDR_IDX;
- struct fbnic_act_tcam *act_tcam;
- struct fbnic_mac_addr *mac_addr;
- int j;
- /* Check if BMC is present */
- if (!fbnic_bmc_present(fbd))
- return;
- /* Fetch BMC MAC addresses from firmware capabilities */
- for (j = 0; j < 4; j++) {
- u8 *bmc_mac = fbd->fw_cap.bmc_mac_addr[j];
- /* Validate BMC MAC addresses */
- if (is_zero_ether_addr(bmc_mac))
- continue;
- if (is_multicast_ether_addr(bmc_mac))
- mac_addr = __fbnic_mc_sync(fbd, bmc_mac);
- else
- mac_addr = &fbd->mac_addr[i++];
- if (!mac_addr) {
- netdev_err(fbd->netdev,
- "No slot for BMC MAC address[%d]\n", j);
- continue;
- }
- ether_addr_copy(mac_addr->value.addr8, bmc_mac);
- eth_zero_addr(mac_addr->mask.addr8);
- set_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);
- mac_addr->state = FBNIC_TCAM_S_ADD;
- }
- /* Validate Broadcast is also present, record it and tag it */
- mac_addr = &fbd->mac_addr[FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX];
- eth_broadcast_addr(mac_addr->value.addr8);
- set_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam);
- mac_addr->state = FBNIC_TCAM_S_ADD;
- /* Rewrite TCAM rule 0 if it isn't present to relocate BMC rules */
- act_tcam = &fbd->act_tcam[FBNIC_RPC_ACT_TBL_BMC_OFFSET];
- act_tcam->dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
- FBNIC_RPC_ACT_TBL0_DEST_BMC);
- act_tcam->mask.tcam[0] = 0xffff;
- /* MACDA 0 - 3 is reserved for the BMC MAC address
- * to account for that we have to mask out the lower 2 bits
- * of the macda by performing an &= with 0x1c.
- */
- act_tcam->value.tcam[1] = FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
- act_tcam->mask.tcam[1] = 0xffff &
- ~FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX, 0x1c) &
- ~FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
- for (j = 2; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)
- act_tcam->mask.tcam[j] = 0xffff;
- act_tcam->state = FBNIC_TCAM_S_UPDATE;
- }
- void fbnic_bmc_rpc_check(struct fbnic_dev *fbd)
- {
- int err;
- if (fbd->fw_cap.need_bmc_tcam_reinit) {
- fbnic_bmc_rpc_init(fbd);
- __fbnic_set_rx_mode(fbd);
- fbd->fw_cap.need_bmc_tcam_reinit = false;
- }
- if (fbd->fw_cap.need_bmc_macda_sync) {
- err = fbnic_fw_xmit_rpc_macda_sync(fbd);
- if (err)
- dev_warn(fbd->dev,
- "Writing MACDA table to FW failed, err: %d\n", err);
- fbd->fw_cap.need_bmc_macda_sync = false;
- }
- }
- #define FBNIC_ACT1_INIT(_l4, _udp, _ip, _v6) \
- (((_l4) ? FBNIC_RPC_TCAM_ACT1_L4_VALID : 0) | \
- ((_udp) ? FBNIC_RPC_TCAM_ACT1_L4_IS_UDP : 0) | \
- ((_ip) ? FBNIC_RPC_TCAM_ACT1_IP_VALID : 0) | \
- ((_v6) ? FBNIC_RPC_TCAM_ACT1_IP_IS_V6 : 0))
- #define FBNIC_TSTAMP_MASK(_all, _udp, _ether) \
- (((_all) ? ((1u << FBNIC_NUM_HASH_OPT) - 1) : 0) | \
- ((_udp) ? (1u << FBNIC_UDP6_HASH_OPT) | \
- (1u << FBNIC_UDP4_HASH_OPT) : 0) | \
- ((_ether) ? (1u << FBNIC_ETHER_HASH_OPT) : 0))
- void fbnic_rss_reinit(struct fbnic_dev *fbd, struct fbnic_net *fbn)
- {
- static const u32 act1_value[FBNIC_NUM_HASH_OPT] = {
- FBNIC_ACT1_INIT(1, 1, 1, 1), /* UDP6 */
- FBNIC_ACT1_INIT(1, 1, 1, 0), /* UDP4 */
- FBNIC_ACT1_INIT(1, 0, 1, 1), /* TCP6 */
- FBNIC_ACT1_INIT(1, 0, 1, 0), /* TCP4 */
- FBNIC_ACT1_INIT(0, 0, 1, 1), /* IP6 */
- FBNIC_ACT1_INIT(0, 0, 1, 0), /* IP4 */
- 0 /* Ether */
- };
- u32 tstamp_mask = 0;
- unsigned int i;
- /* To support scenarios where a BMC is present we must write the
- * rules twice, once for the unicast cases, and once again for
- * the broadcast/multicast cases as we have to support 2 destinations.
- */
- BUILD_BUG_ON(FBNIC_RSS_EN_NUM_UNICAST * 2 != FBNIC_RSS_EN_NUM_ENTRIES);
- BUILD_BUG_ON(ARRAY_SIZE(act1_value) != FBNIC_NUM_HASH_OPT);
- /* Set timestamp mask with 1b per flow type */
- if (fbn->hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE) {
- switch (fbn->hwtstamp_config.rx_filter) {
- case HWTSTAMP_FILTER_ALL:
- tstamp_mask = FBNIC_TSTAMP_MASK(1, 1, 1);
- break;
- case HWTSTAMP_FILTER_PTP_V2_EVENT:
- tstamp_mask = FBNIC_TSTAMP_MASK(0, 1, 1);
- break;
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- tstamp_mask = FBNIC_TSTAMP_MASK(0, 1, 0);
- break;
- case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- tstamp_mask = FBNIC_TSTAMP_MASK(0, 0, 1);
- break;
- default:
- netdev_warn(fbn->netdev, "Unsupported hwtstamp_rx_filter\n");
- break;
- }
- }
- /* Program RSS hash enable mask for host in action TCAM/table. */
- for (i = fbnic_bmc_present(fbd) ? 0 : FBNIC_RSS_EN_NUM_UNICAST;
- i < FBNIC_RSS_EN_NUM_ENTRIES; i++) {
- unsigned int idx = i + FBNIC_RPC_ACT_TBL_RSS_OFFSET;
- struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[idx];
- u32 flow_hash, dest, rss_en_mask;
- int flow_type, j;
- u16 value = 0;
- flow_type = i % FBNIC_RSS_EN_NUM_UNICAST;
- flow_hash = fbn->rss_flow_hash[flow_type];
- /* Set DEST_HOST based on absence of RXH_DISCARD */
- dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
- !(RXH_DISCARD & flow_hash) ?
- FBNIC_RPC_ACT_TBL0_DEST_HOST : 0);
- if (i >= FBNIC_RSS_EN_NUM_UNICAST && fbnic_bmc_present(fbd))
- dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
- FBNIC_RPC_ACT_TBL0_DEST_BMC);
- if (!dest)
- dest = FBNIC_RPC_ACT_TBL0_DROP;
- else if (tstamp_mask & (1u << flow_type))
- dest |= FBNIC_RPC_ACT_TBL0_TS_ENA;
- dest |= FIELD_PREP(FBNIC_RPC_ACT_TBL0_DMA_HINT,
- FBNIC_RCD_HDR_AL_DMA_HINT_L4);
- rss_en_mask = fbnic_flow_hash_2_rss_en_mask(fbn, flow_type);
- act_tcam->dest = dest;
- act_tcam->rss_en_mask = rss_en_mask;
- act_tcam->state = FBNIC_TCAM_S_UPDATE;
- act_tcam->mask.tcam[0] = 0xffff;
- /* We reserve the upper 8 MACDA TCAM entries for host
- * unicast. So we set the value to 24, and the mask the
- * lower bits so that the lower entries can be used as
- * multicast or BMC addresses.
- */
- if (i < FBNIC_RSS_EN_NUM_UNICAST)
- value = FIELD_PREP(FBNIC_RPC_TCAM_ACT1_L2_MACDA_IDX,
- fbd->mac_addr_boundary);
- value |= FBNIC_RPC_TCAM_ACT1_L2_MACDA_VALID;
- flow_type = i % FBNIC_RSS_EN_NUM_UNICAST;
- value |= act1_value[flow_type];
- act_tcam->value.tcam[1] = value;
- act_tcam->mask.tcam[1] = ~value;
- for (j = 2; j < FBNIC_RPC_TCAM_ACT_WORD_LEN; j++)
- act_tcam->mask.tcam[j] = 0xffff;
- act_tcam->state = FBNIC_TCAM_S_UPDATE;
- }
- }
- struct fbnic_mac_addr *__fbnic_uc_sync(struct fbnic_dev *fbd,
- const unsigned char *addr)
- {
- struct fbnic_mac_addr *avail_addr = NULL;
- unsigned int i;
- /* Scan from middle of list to bottom, filling bottom up.
- * Skip the first entry which is reserved for dev_addr and
- * leave the last entry to use for promiscuous filtering.
- */
- for (i = fbd->mac_addr_boundary - 1;
- i < FBNIC_RPC_TCAM_MACDA_HOST_ADDR_IDX; i++) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];
- if (mac_addr->state == FBNIC_TCAM_S_DISABLED) {
- avail_addr = mac_addr;
- } else if (ether_addr_equal(mac_addr->value.addr8, addr)) {
- avail_addr = mac_addr;
- break;
- }
- }
- if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {
- ether_addr_copy(avail_addr->value.addr8, addr);
- eth_zero_addr(avail_addr->mask.addr8);
- avail_addr->state = FBNIC_TCAM_S_ADD;
- }
- return avail_addr;
- }
- struct fbnic_mac_addr *__fbnic_mc_sync(struct fbnic_dev *fbd,
- const unsigned char *addr)
- {
- struct fbnic_mac_addr *avail_addr = NULL;
- unsigned int i;
- /* Scan from middle of list to top, filling top down.
- * Skip over the address reserved for the BMC MAC and
- * exclude index 0 as that belongs to the broadcast address
- */
- for (i = fbd->mac_addr_boundary;
- --i > FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX;) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];
- if (mac_addr->state == FBNIC_TCAM_S_DISABLED) {
- avail_addr = mac_addr;
- } else if (ether_addr_equal(mac_addr->value.addr8, addr)) {
- avail_addr = mac_addr;
- break;
- }
- }
- /* Scan the BMC addresses to see if it may have already
- * reserved the address.
- */
- while (--i) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[i];
- if (!is_zero_ether_addr(mac_addr->mask.addr8))
- continue;
- /* Only move on if we find a match */
- if (!ether_addr_equal(mac_addr->value.addr8, addr))
- continue;
- /* We need to pull this address to the shared area */
- if (avail_addr) {
- memcpy(avail_addr, mac_addr, sizeof(*mac_addr));
- mac_addr->state = FBNIC_TCAM_S_DELETE;
- avail_addr->state = FBNIC_TCAM_S_ADD;
- }
- break;
- }
- if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {
- ether_addr_copy(avail_addr->value.addr8, addr);
- eth_zero_addr(avail_addr->mask.addr8);
- avail_addr->state = FBNIC_TCAM_S_ADD;
- }
- return avail_addr;
- }
- int __fbnic_xc_unsync(struct fbnic_mac_addr *mac_addr, unsigned int tcam_idx)
- {
- if (!test_and_clear_bit(tcam_idx, mac_addr->act_tcam))
- return -ENOENT;
- if (bitmap_empty(mac_addr->act_tcam, FBNIC_RPC_TCAM_ACT_NUM_ENTRIES))
- mac_addr->state = FBNIC_TCAM_S_DELETE;
- return 0;
- }
- void fbnic_promisc_sync(struct fbnic_dev *fbd,
- bool uc_promisc, bool mc_promisc)
- {
- struct fbnic_mac_addr *mac_addr;
- /* Populate last TCAM entry with promiscuous entry and 0/1 bit mask */
- mac_addr = &fbd->mac_addr[FBNIC_RPC_TCAM_MACDA_PROMISC_IDX];
- if (uc_promisc) {
- if (!is_zero_ether_addr(mac_addr->value.addr8) ||
- mac_addr->state != FBNIC_TCAM_S_VALID) {
- eth_zero_addr(mac_addr->value.addr8);
- eth_broadcast_addr(mac_addr->mask.addr8);
- clear_bit(FBNIC_MAC_ADDR_T_ALLMULTI,
- mac_addr->act_tcam);
- set_bit(FBNIC_MAC_ADDR_T_PROMISC,
- mac_addr->act_tcam);
- mac_addr->state = FBNIC_TCAM_S_ADD;
- }
- } else if (mc_promisc &&
- (!fbnic_bmc_present(fbd) || !fbd->fw_cap.all_multi)) {
- /* We have to add a special handler for multicast as the
- * BMC may have an all-multi rule already in place. As such
- * adding a rule ourselves won't do any good so we will have
- * to modify the rules for the ALL MULTI below if the BMC
- * already has the rule in place.
- */
- if (!is_multicast_ether_addr(mac_addr->value.addr8) ||
- mac_addr->state != FBNIC_TCAM_S_VALID) {
- eth_zero_addr(mac_addr->value.addr8);
- eth_broadcast_addr(mac_addr->mask.addr8);
- mac_addr->value.addr8[0] ^= 1;
- mac_addr->mask.addr8[0] ^= 1;
- set_bit(FBNIC_MAC_ADDR_T_ALLMULTI,
- mac_addr->act_tcam);
- clear_bit(FBNIC_MAC_ADDR_T_PROMISC,
- mac_addr->act_tcam);
- mac_addr->state = FBNIC_TCAM_S_ADD;
- }
- } else if (mac_addr->state == FBNIC_TCAM_S_VALID) {
- __fbnic_xc_unsync(mac_addr, FBNIC_MAC_ADDR_T_ALLMULTI);
- __fbnic_xc_unsync(mac_addr, FBNIC_MAC_ADDR_T_PROMISC);
- }
- }
- void fbnic_sift_macda(struct fbnic_dev *fbd)
- {
- int dest, src;
- /* Move BMC only addresses back into BMC region */
- for (dest = FBNIC_RPC_TCAM_MACDA_BMC_ADDR_IDX,
- src = FBNIC_RPC_TCAM_MACDA_MULTICAST_IDX;
- ++dest < FBNIC_RPC_TCAM_MACDA_BROADCAST_IDX &&
- src < fbd->mac_addr_boundary;) {
- struct fbnic_mac_addr *dest_addr = &fbd->mac_addr[dest];
- if (dest_addr->state != FBNIC_TCAM_S_DISABLED)
- continue;
- while (src < fbd->mac_addr_boundary) {
- struct fbnic_mac_addr *src_addr = &fbd->mac_addr[src++];
- /* Verify BMC bit is set */
- if (!test_bit(FBNIC_MAC_ADDR_T_BMC, src_addr->act_tcam))
- continue;
- /* Verify filter isn't already disabled */
- if (src_addr->state == FBNIC_TCAM_S_DISABLED ||
- src_addr->state == FBNIC_TCAM_S_DELETE)
- continue;
- /* Verify only BMC bit is set */
- if (bitmap_weight(src_addr->act_tcam,
- FBNIC_RPC_TCAM_ACT_NUM_ENTRIES) != 1)
- continue;
- /* Verify we are not moving wildcard address */
- if (!is_zero_ether_addr(src_addr->mask.addr8))
- continue;
- memcpy(dest_addr, src_addr, sizeof(*src_addr));
- src_addr->state = FBNIC_TCAM_S_DELETE;
- dest_addr->state = FBNIC_TCAM_S_ADD;
- }
- }
- }
- static void fbnic_clear_macda_entry(struct fbnic_dev *fbd, unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_RPC_TCAM_MACDA_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_MACDA(idx, i), 0);
- }
- static void fbnic_clear_macda(struct fbnic_dev *fbd)
- {
- int idx;
- for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];
- if (mac_addr->state == FBNIC_TCAM_S_DISABLED)
- continue;
- if (test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam)) {
- if (fbnic_bmc_present(fbd))
- continue;
- dev_warn_once(fbd->dev,
- "Found BMC MAC address w/ BMC not present\n");
- }
- fbnic_clear_macda_entry(fbd, idx);
- /* If rule was already destined for deletion just wipe it now */
- if (mac_addr->state == FBNIC_TCAM_S_DELETE) {
- memset(mac_addr, 0, sizeof(*mac_addr));
- continue;
- }
- /* Change state to update so that we will rewrite
- * this tcam the next time fbnic_write_macda is called.
- */
- mac_addr->state = FBNIC_TCAM_S_UPDATE;
- }
- }
- static void fbnic_clear_valid_macda(struct fbnic_dev *fbd)
- {
- int idx;
- for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];
- if (mac_addr->state == FBNIC_TCAM_S_VALID) {
- fbnic_clear_macda_entry(fbd, idx);
- mac_addr->state = FBNIC_TCAM_S_UPDATE;
- }
- }
- }
- static void fbnic_write_macda_entry(struct fbnic_dev *fbd, unsigned int idx,
- struct fbnic_mac_addr *mac_addr)
- {
- __be16 *mask, *value;
- int i;
- mask = &mac_addr->mask.addr16[FBNIC_RPC_TCAM_MACDA_WORD_LEN - 1];
- value = &mac_addr->value.addr16[FBNIC_RPC_TCAM_MACDA_WORD_LEN - 1];
- for (i = 0; i < FBNIC_RPC_TCAM_MACDA_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_MACDA(idx, i),
- FIELD_PREP(FBNIC_RPC_TCAM_MACDA_MASK, ntohs(*mask--)) |
- FIELD_PREP(FBNIC_RPC_TCAM_MACDA_VALUE, ntohs(*value--)));
- wrfl(fbd);
- wr32(fbd, FBNIC_RPC_TCAM_MACDA(idx, i), FBNIC_RPC_TCAM_VALIDATE);
- }
- void fbnic_write_macda(struct fbnic_dev *fbd)
- {
- int idx, updates = 0;
- for (idx = ARRAY_SIZE(fbd->mac_addr); idx--;) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[idx];
- /* Check if update flag is set else exit. */
- if (!(mac_addr->state & FBNIC_TCAM_S_UPDATE))
- continue;
- /* Record update count */
- updates++;
- /* Clear by writing 0s. */
- if (mac_addr->state == FBNIC_TCAM_S_DELETE) {
- /* Invalidate entry and clear addr state info */
- fbnic_clear_macda_entry(fbd, idx);
- memset(mac_addr, 0, sizeof(*mac_addr));
- continue;
- }
- fbnic_write_macda_entry(fbd, idx, mac_addr);
- mac_addr->state = FBNIC_TCAM_S_VALID;
- }
- /* If reinitializing the BMC TCAM we are doing an initial update */
- if (fbd->fw_cap.need_bmc_tcam_reinit)
- updates++;
- /* If needed notify firmware of changes to MACDA TCAM */
- if (updates != 0 && fbnic_bmc_present(fbd))
- fbd->fw_cap.need_bmc_macda_sync = true;
- }
- static void fbnic_clear_act_tcam(struct fbnic_dev *fbd, unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_RPC_TCAM_ACT_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_ACT(idx, i), 0);
- }
- static void fbnic_clear_tce_tcam_entry(struct fbnic_dev *fbd, unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_TCE_TCAM_WORD_LEN; i++)
- wr32(fbd, FBNIC_TCE_RAM_TCAM(idx, i), 0);
- }
- static void fbnic_write_tce_tcam_dest(struct fbnic_dev *fbd, unsigned int idx,
- struct fbnic_mac_addr *mac_addr)
- {
- u32 dest = FBNIC_TCE_TCAM_DEST_BMC;
- u32 idx2dest_map;
- if (is_multicast_ether_addr(mac_addr->value.addr8))
- dest |= FBNIC_TCE_TCAM_DEST_MAC;
- idx2dest_map = rd32(fbd, FBNIC_TCE_TCAM_IDX2DEST_MAP);
- idx2dest_map &= ~(FBNIC_TCE_TCAM_IDX2DEST_MAP_DEST_ID_0 << (4 * idx));
- idx2dest_map |= dest << (4 * idx);
- wr32(fbd, FBNIC_TCE_TCAM_IDX2DEST_MAP, idx2dest_map);
- }
- static void fbnic_write_tce_tcam_entry(struct fbnic_dev *fbd, unsigned int idx,
- struct fbnic_mac_addr *mac_addr)
- {
- __be16 *mask, *value;
- int i;
- mask = &mac_addr->mask.addr16[FBNIC_TCE_TCAM_WORD_LEN - 1];
- value = &mac_addr->value.addr16[FBNIC_TCE_TCAM_WORD_LEN - 1];
- for (i = 0; i < FBNIC_TCE_TCAM_WORD_LEN; i++)
- wr32(fbd, FBNIC_TCE_RAM_TCAM(idx, i),
- FIELD_PREP(FBNIC_TCE_RAM_TCAM_MASK, ntohs(*mask--)) |
- FIELD_PREP(FBNIC_TCE_RAM_TCAM_VALUE, ntohs(*value--)));
- wrfl(fbd);
- wr32(fbd, FBNIC_TCE_RAM_TCAM3(idx), FBNIC_TCE_RAM_TCAM3_MCQ_MASK |
- FBNIC_TCE_RAM_TCAM3_DEST_MASK |
- FBNIC_TCE_RAM_TCAM3_VALIDATE);
- }
- static void __fbnic_write_tce_tcam_rev(struct fbnic_dev *fbd)
- {
- int tcam_idx = FBNIC_TCE_TCAM_NUM_ENTRIES;
- int mac_idx;
- for (mac_idx = ARRAY_SIZE(fbd->mac_addr); mac_idx--;) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[mac_idx];
- /* Verify BMC bit is set */
- if (!test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam))
- continue;
- if (!tcam_idx) {
- dev_err(fbd->dev, "TCE TCAM overflow\n");
- return;
- }
- tcam_idx--;
- fbnic_write_tce_tcam_dest(fbd, tcam_idx, mac_addr);
- fbnic_write_tce_tcam_entry(fbd, tcam_idx, mac_addr);
- }
- while (tcam_idx)
- fbnic_clear_tce_tcam_entry(fbd, --tcam_idx);
- fbd->tce_tcam_last = tcam_idx;
- }
- static void __fbnic_write_tce_tcam(struct fbnic_dev *fbd)
- {
- int tcam_idx = 0;
- int mac_idx;
- for (mac_idx = 0; mac_idx < ARRAY_SIZE(fbd->mac_addr); mac_idx++) {
- struct fbnic_mac_addr *mac_addr = &fbd->mac_addr[mac_idx];
- /* Verify BMC bit is set */
- if (!test_bit(FBNIC_MAC_ADDR_T_BMC, mac_addr->act_tcam))
- continue;
- if (tcam_idx == FBNIC_TCE_TCAM_NUM_ENTRIES) {
- dev_err(fbd->dev, "TCE TCAM overflow\n");
- return;
- }
- fbnic_write_tce_tcam_dest(fbd, tcam_idx, mac_addr);
- fbnic_write_tce_tcam_entry(fbd, tcam_idx, mac_addr);
- tcam_idx++;
- }
- while (tcam_idx < FBNIC_TCE_TCAM_NUM_ENTRIES)
- fbnic_clear_tce_tcam_entry(fbd, tcam_idx++);
- fbd->tce_tcam_last = tcam_idx;
- }
- void fbnic_write_tce_tcam(struct fbnic_dev *fbd)
- {
- if (fbd->tce_tcam_last)
- __fbnic_write_tce_tcam_rev(fbd);
- else
- __fbnic_write_tce_tcam(fbd);
- }
- struct fbnic_ip_addr *__fbnic_ip4_sync(struct fbnic_dev *fbd,
- struct fbnic_ip_addr *ip_addr,
- const struct in_addr *addr,
- const struct in_addr *mask)
- {
- struct fbnic_ip_addr *avail_addr = NULL;
- unsigned int i;
- /* Scan from top of list to bottom, filling bottom up. */
- for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES; i++, ip_addr++) {
- struct in6_addr *m = &ip_addr->mask;
- if (ip_addr->state == FBNIC_TCAM_S_DISABLED) {
- avail_addr = ip_addr;
- continue;
- }
- if (ip_addr->version != 4)
- continue;
- /* Drop avail_addr if mask is a subset of our current mask,
- * This prevents us from inserting a longer prefix behind a
- * shorter one.
- *
- * The mask is stored inverted value so as an example:
- * m ffff ffff ffff ffff ffff ffff ffff 0000 0000
- * mask 0000 0000 0000 0000 0000 0000 0000 ffff ffff
- *
- * "m" and "mask" represent typical IPv4 mask stored in
- * the TCAM and those provided by the stack. The code below
- * should return a non-zero result if there is a 0 stored
- * anywhere in "m" where "mask" has a 0.
- */
- if (~m->s6_addr32[3] & ~mask->s_addr) {
- avail_addr = NULL;
- continue;
- }
- /* Check to see if the mask actually contains fewer bits than
- * our new mask "m". The XOR below should only result in 0 if
- * "m" is masking a bit that we are looking for in our new
- * "mask", we eliminated the 0^0 case with the check above.
- *
- * If it contains fewer bits we need to stop here, otherwise
- * we might be adding an unreachable rule.
- */
- if (~(m->s6_addr32[3] ^ mask->s_addr))
- break;
- if (ip_addr->value.s6_addr32[3] == addr->s_addr) {
- avail_addr = ip_addr;
- break;
- }
- }
- if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {
- ipv6_addr_set(&avail_addr->value, 0, 0, 0, addr->s_addr);
- ipv6_addr_set(&avail_addr->mask, htonl(~0), htonl(~0),
- htonl(~0), ~mask->s_addr);
- avail_addr->version = 4;
- avail_addr->state = FBNIC_TCAM_S_ADD;
- }
- return avail_addr;
- }
- struct fbnic_ip_addr *__fbnic_ip6_sync(struct fbnic_dev *fbd,
- struct fbnic_ip_addr *ip_addr,
- const struct in6_addr *addr,
- const struct in6_addr *mask)
- {
- struct fbnic_ip_addr *avail_addr = NULL;
- unsigned int i;
- ip_addr = &ip_addr[FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES - 1];
- /* Scan from bottom of list to top, filling top down. */
- for (i = FBNIC_RPC_TCAM_IP_ADDR_NUM_ENTRIES; i--; ip_addr--) {
- struct in6_addr *m = &ip_addr->mask;
- if (ip_addr->state == FBNIC_TCAM_S_DISABLED) {
- avail_addr = ip_addr;
- continue;
- }
- if (ip_addr->version != 6)
- continue;
- /* Drop avail_addr if mask is a superset of our current mask.
- * This prevents us from inserting a longer prefix behind a
- * shorter one.
- *
- * The mask is stored inverted value so as an example:
- * m 0000 0000 0000 0000 0000 0000 0000 0000 0000
- * mask ffff ffff ffff ffff ffff ffff ffff ffff ffff
- *
- * "m" and "mask" represent typical IPv6 mask stored in
- * the TCAM and those provided by the stack. The code below
- * should return a non-zero result which will cause us
- * to drop the avail_addr value that might be cached
- * to prevent us from dropping a v6 address behind it.
- */
- if ((m->s6_addr32[0] & mask->s6_addr32[0]) |
- (m->s6_addr32[1] & mask->s6_addr32[1]) |
- (m->s6_addr32[2] & mask->s6_addr32[2]) |
- (m->s6_addr32[3] & mask->s6_addr32[3])) {
- avail_addr = NULL;
- continue;
- }
- /* The previous test eliminated any overlap between the
- * two values so now we need to check for gaps.
- *
- * If the mask is equal to our current mask then it should
- * result with m ^ mask = ffff ffff, if however the value
- * stored in m is bigger then we should see a 0 appear
- * somewhere in the mask.
- */
- if (~(m->s6_addr32[0] ^ mask->s6_addr32[0]) |
- ~(m->s6_addr32[1] ^ mask->s6_addr32[1]) |
- ~(m->s6_addr32[2] ^ mask->s6_addr32[2]) |
- ~(m->s6_addr32[3] ^ mask->s6_addr32[3]))
- break;
- if (ipv6_addr_cmp(&ip_addr->value, addr))
- continue;
- avail_addr = ip_addr;
- break;
- }
- if (avail_addr && avail_addr->state == FBNIC_TCAM_S_DISABLED) {
- memcpy(&avail_addr->value, addr, sizeof(*addr));
- ipv6_addr_set(&avail_addr->mask,
- ~mask->s6_addr32[0], ~mask->s6_addr32[1],
- ~mask->s6_addr32[2], ~mask->s6_addr32[3]);
- avail_addr->version = 6;
- avail_addr->state = FBNIC_TCAM_S_ADD;
- }
- return avail_addr;
- }
- int __fbnic_ip_unsync(struct fbnic_ip_addr *ip_addr, unsigned int tcam_idx)
- {
- if (!test_and_clear_bit(tcam_idx, ip_addr->act_tcam))
- return -ENOENT;
- if (bitmap_empty(ip_addr->act_tcam, FBNIC_RPC_TCAM_ACT_NUM_ENTRIES))
- ip_addr->state = FBNIC_TCAM_S_DELETE;
- return 0;
- }
- static void fbnic_clear_ip_src_entry(struct fbnic_dev *fbd, unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_IPSRC(idx, i), 0);
- }
- static void fbnic_clear_ip_dst_entry(struct fbnic_dev *fbd, unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_IPDST(idx, i), 0);
- }
- static void fbnic_clear_ip_outer_src_entry(struct fbnic_dev *fbd,
- unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPSRC(idx, i), 0);
- }
- static void fbnic_clear_ip_outer_dst_entry(struct fbnic_dev *fbd,
- unsigned int idx)
- {
- int i;
- /* Invalidate entry and clear addr state info */
- for (i = 0; i <= FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPDST(idx, i), 0);
- }
- static void fbnic_write_ip_src_entry(struct fbnic_dev *fbd, unsigned int idx,
- struct fbnic_ip_addr *ip_addr)
- {
- __be16 *mask, *value;
- int i;
- mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_IPSRC(idx, i),
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
- wrfl(fbd);
- /* Bit 129 is used to flag for v4/v6 */
- wr32(fbd, FBNIC_RPC_TCAM_IPSRC(idx, i),
- (ip_addr->version == 6) | FBNIC_RPC_TCAM_VALIDATE);
- }
- static void fbnic_write_ip_dst_entry(struct fbnic_dev *fbd, unsigned int idx,
- struct fbnic_ip_addr *ip_addr)
- {
- __be16 *mask, *value;
- int i;
- mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_IPDST(idx, i),
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
- wrfl(fbd);
- /* Bit 129 is used to flag for v4/v6 */
- wr32(fbd, FBNIC_RPC_TCAM_IPDST(idx, i),
- (ip_addr->version == 6) | FBNIC_RPC_TCAM_VALIDATE);
- }
- static void fbnic_write_ip_outer_src_entry(struct fbnic_dev *fbd,
- unsigned int idx,
- struct fbnic_ip_addr *ip_addr)
- {
- __be16 *mask, *value;
- int i;
- mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPSRC(idx, i),
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
- wrfl(fbd);
- wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPSRC(idx, i), FBNIC_RPC_TCAM_VALIDATE);
- }
- static void fbnic_write_ip_outer_dst_entry(struct fbnic_dev *fbd,
- unsigned int idx,
- struct fbnic_ip_addr *ip_addr)
- {
- __be16 *mask, *value;
- int i;
- mask = &ip_addr->mask.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- value = &ip_addr->value.s6_addr16[FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN - 1];
- for (i = 0; i < FBNIC_RPC_TCAM_IP_ADDR_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPDST(idx, i),
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_MASK, ntohs(*mask--)) |
- FIELD_PREP(FBNIC_RPC_TCAM_IP_ADDR_VALUE, ntohs(*value--)));
- wrfl(fbd);
- wr32(fbd, FBNIC_RPC_TCAM_OUTER_IPDST(idx, i), FBNIC_RPC_TCAM_VALIDATE);
- }
- void fbnic_write_ip_addr(struct fbnic_dev *fbd)
- {
- int idx;
- for (idx = ARRAY_SIZE(fbd->ip_src); idx--;) {
- struct fbnic_ip_addr *ip_addr = &fbd->ip_src[idx];
- /* Check if update flag is set else skip. */
- if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
- continue;
- /* Clear by writing 0s. */
- if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
- /* Invalidate entry and clear addr state info */
- fbnic_clear_ip_src_entry(fbd, idx);
- memset(ip_addr, 0, sizeof(*ip_addr));
- continue;
- }
- fbnic_write_ip_src_entry(fbd, idx, ip_addr);
- ip_addr->state = FBNIC_TCAM_S_VALID;
- }
- /* Repeat process for other IP TCAMs */
- for (idx = ARRAY_SIZE(fbd->ip_dst); idx--;) {
- struct fbnic_ip_addr *ip_addr = &fbd->ip_dst[idx];
- if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
- continue;
- if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
- fbnic_clear_ip_dst_entry(fbd, idx);
- memset(ip_addr, 0, sizeof(*ip_addr));
- continue;
- }
- fbnic_write_ip_dst_entry(fbd, idx, ip_addr);
- ip_addr->state = FBNIC_TCAM_S_VALID;
- }
- for (idx = ARRAY_SIZE(fbd->ipo_src); idx--;) {
- struct fbnic_ip_addr *ip_addr = &fbd->ipo_src[idx];
- if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
- continue;
- if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
- fbnic_clear_ip_outer_src_entry(fbd, idx);
- memset(ip_addr, 0, sizeof(*ip_addr));
- continue;
- }
- fbnic_write_ip_outer_src_entry(fbd, idx, ip_addr);
- ip_addr->state = FBNIC_TCAM_S_VALID;
- }
- for (idx = ARRAY_SIZE(fbd->ipo_dst); idx--;) {
- struct fbnic_ip_addr *ip_addr = &fbd->ipo_dst[idx];
- if (!(ip_addr->state & FBNIC_TCAM_S_UPDATE))
- continue;
- if (ip_addr->state == FBNIC_TCAM_S_DELETE) {
- fbnic_clear_ip_outer_dst_entry(fbd, idx);
- memset(ip_addr, 0, sizeof(*ip_addr));
- continue;
- }
- fbnic_write_ip_outer_dst_entry(fbd, idx, ip_addr);
- ip_addr->state = FBNIC_TCAM_S_VALID;
- }
- }
- static void fbnic_clear_valid_act_tcam(struct fbnic_dev *fbd)
- {
- int i = FBNIC_RPC_TCAM_ACT_NUM_ENTRIES - 1;
- struct fbnic_act_tcam *act_tcam;
- /* Work from the bottom up deleting all other rules from hardware */
- do {
- act_tcam = &fbd->act_tcam[i];
- if (act_tcam->state != FBNIC_TCAM_S_VALID)
- continue;
- fbnic_clear_act_tcam(fbd, i);
- act_tcam->state = FBNIC_TCAM_S_UPDATE;
- } while (i--);
- }
- void fbnic_clear_rules(struct fbnic_dev *fbd)
- {
- /* Clear MAC rules */
- fbnic_clear_macda(fbd);
- /* If BMC is present we need to preserve the last rule which
- * will be used to route traffic to the BMC if it is received.
- *
- * At this point it should be the only MAC address in the MACDA
- * so any unicast or multicast traffic received should be routed
- * to it. So leave the last rule in place.
- *
- * It will be rewritten to add the host again when we bring
- * the interface back up.
- */
- if (fbnic_bmc_present(fbd)) {
- u32 dest = FIELD_PREP(FBNIC_RPC_ACT_TBL0_DEST_MASK,
- FBNIC_RPC_ACT_TBL0_DEST_BMC);
- int i = FBNIC_RPC_TCAM_ACT_NUM_ENTRIES - 1;
- struct fbnic_act_tcam *act_tcam;
- act_tcam = &fbd->act_tcam[i];
- if (act_tcam->state == FBNIC_TCAM_S_VALID &&
- (act_tcam->dest & dest)) {
- wr32(fbd, FBNIC_RPC_ACT_TBL0(i), dest);
- wr32(fbd, FBNIC_RPC_ACT_TBL1(i), 0);
- act_tcam->state = FBNIC_TCAM_S_UPDATE;
- }
- }
- fbnic_clear_valid_act_tcam(fbd);
- }
- static void fbnic_delete_act_tcam(struct fbnic_dev *fbd, unsigned int idx)
- {
- fbnic_clear_act_tcam(fbd, idx);
- memset(&fbd->act_tcam[idx], 0, sizeof(struct fbnic_act_tcam));
- }
- static void fbnic_update_act_tcam(struct fbnic_dev *fbd, unsigned int idx)
- {
- struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[idx];
- int i;
- /* Update entry by writing the destination and RSS mask */
- wr32(fbd, FBNIC_RPC_ACT_TBL0(idx), act_tcam->dest);
- wr32(fbd, FBNIC_RPC_ACT_TBL1(idx), act_tcam->rss_en_mask);
- /* Write new TCAM rule to hardware */
- for (i = 0; i < FBNIC_RPC_TCAM_ACT_WORD_LEN; i++)
- wr32(fbd, FBNIC_RPC_TCAM_ACT(idx, i),
- FIELD_PREP(FBNIC_RPC_TCAM_ACT_MASK,
- act_tcam->mask.tcam[i]) |
- FIELD_PREP(FBNIC_RPC_TCAM_ACT_VALUE,
- act_tcam->value.tcam[i]));
- wrfl(fbd);
- wr32(fbd, FBNIC_RPC_TCAM_ACT(idx, i), FBNIC_RPC_TCAM_VALIDATE);
- act_tcam->state = FBNIC_TCAM_S_VALID;
- }
- void fbnic_write_rules(struct fbnic_dev *fbd)
- {
- int i;
- /* Flush any pending action table rules */
- for (i = 0; i < FBNIC_RPC_ACT_TBL_NUM_ENTRIES; i++) {
- struct fbnic_act_tcam *act_tcam = &fbd->act_tcam[i];
- /* Check if update flag is set else exit. */
- if (!(act_tcam->state & FBNIC_TCAM_S_UPDATE))
- continue;
- if (act_tcam->state == FBNIC_TCAM_S_DELETE)
- fbnic_delete_act_tcam(fbd, i);
- else
- fbnic_update_act_tcam(fbd, i);
- }
- }
- void fbnic_rpc_reset_valid_entries(struct fbnic_dev *fbd)
- {
- fbnic_clear_valid_act_tcam(fbd);
- fbnic_clear_valid_macda(fbd);
- }
|