Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/flowring.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2014 Broadcom Corporation3*/456#include <linux/types.h>7#include <linux/netdevice.h>8#include <linux/etherdevice.h>9#include <brcmu_utils.h>1011#include "core.h"12#include "debug.h"13#include "bus.h"14#include "proto.h"15#include "flowring.h"16#include "msgbuf.h"17#include "common.h"181920#define BRCMF_FLOWRING_HIGH 102421#define BRCMF_FLOWRING_LOW (BRCMF_FLOWRING_HIGH - 256)22#define BRCMF_FLOWRING_INVALID_IFIDX 0xff2324#define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] * 2 + fifo + ifidx * 16)25#define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16)2627static const u8 brcmf_flowring_prio2fifo[] = {280,291,301,310,322,332,343,35336};3738static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };394041static bool42brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN])43{44struct brcmf_flowring_tdls_entry *search;4546search = flow->tdls_entry;4748while (search) {49if (memcmp(search->mac, mac, ETH_ALEN) == 0)50return true;51search = search->next;52}5354return false;55}565758u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN],59u8 prio, u8 ifidx)60{61struct brcmf_flowring_hash *hash;62u16 hash_idx;63u32 i;64bool found;65bool sta;66u8 fifo;67#if defined(__linux__)68u8 *mac;69#elif defined(__FreeBSD__)70const u8 *mac;71#endif7273fifo = brcmf_flowring_prio2fifo[prio];74sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);75mac = da;76if ((!sta) && (is_multicast_ether_addr(da))) {77#if defined(__linux__)78mac = (u8 *)ALLFFMAC;79#elif defined(__FreeBSD__)80mac = ALLFFMAC;81#endif82fifo = 0;83}84if ((sta) && (flow->tdls_active) &&85(brcmf_flowring_is_tdls_mac(flow, da))) {86sta = false;87}88hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :89BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);90hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);91found = false;92hash = flow->hash;93for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {94if ((sta || (memcmp(hash[hash_idx].mac, mac, ETH_ALEN) == 0)) &&95(hash[hash_idx].fifo == fifo) &&96(hash[hash_idx].ifidx == ifidx)) {97found = true;98break;99}100hash_idx++;101hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);102}103if (found)104return hash[hash_idx].flowid;105106return BRCMF_FLOWRING_INVALID_ID;107}108109110u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],111u8 prio, u8 ifidx)112{113struct brcmf_flowring_ring *ring;114struct brcmf_flowring_hash *hash;115u16 hash_idx;116u32 i;117bool found;118u8 fifo;119bool sta;120#if defined(__linux__)121u8 *mac;122#elif defined(__FreeBSD__)123const u8 *mac;124#endif125126fifo = brcmf_flowring_prio2fifo[prio];127sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);128mac = da;129if ((!sta) && (is_multicast_ether_addr(da))) {130#if defined(__linux__)131mac = (u8 *)ALLFFMAC;132#elif defined(__FreeBSD__)133mac = ALLFFMAC;134#endif135fifo = 0;136}137if ((sta) && (flow->tdls_active) &&138(brcmf_flowring_is_tdls_mac(flow, da))) {139sta = false;140}141hash_idx = sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :142BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);143hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);144found = false;145hash = flow->hash;146for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {147if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) &&148(is_zero_ether_addr(hash[hash_idx].mac))) {149found = true;150break;151}152hash_idx++;153hash_idx &= (BRCMF_FLOWRING_HASHSIZE - 1);154}155if (found) {156for (i = 0; i < flow->nrofrings; i++) {157if (flow->rings[i] == NULL)158break;159}160if (i == flow->nrofrings)161return -ENOMEM;162163ring = kzalloc(sizeof(*ring), GFP_ATOMIC);164if (!ring)165return -ENOMEM;166167memcpy(hash[hash_idx].mac, mac, ETH_ALEN);168hash[hash_idx].fifo = fifo;169hash[hash_idx].ifidx = ifidx;170hash[hash_idx].flowid = i;171172ring->hash_id = hash_idx;173ring->status = RING_CLOSED;174skb_queue_head_init(&ring->skblist);175flow->rings[i] = ring;176177return i;178}179return BRCMF_FLOWRING_INVALID_ID;180}181182183u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u16 flowid)184{185struct brcmf_flowring_ring *ring;186187ring = flow->rings[flowid];188189return flow->hash[ring->hash_id].fifo;190}191192193static void brcmf_flowring_block(struct brcmf_flowring *flow, u16 flowid,194bool blocked)195{196struct brcmf_flowring_ring *ring;197struct brcmf_bus *bus_if;198struct brcmf_pub *drvr;199struct brcmf_if *ifp;200bool currently_blocked;201int i;202u8 ifidx;203unsigned long flags;204205spin_lock_irqsave(&flow->block_lock, flags);206207ring = flow->rings[flowid];208if (ring->blocked == blocked) {209spin_unlock_irqrestore(&flow->block_lock, flags);210return;211}212ifidx = brcmf_flowring_ifidx_get(flow, flowid);213214currently_blocked = false;215for (i = 0; i < flow->nrofrings; i++) {216if ((flow->rings[i]) && (i != flowid)) {217ring = flow->rings[i];218if ((ring->status == RING_OPEN) &&219(brcmf_flowring_ifidx_get(flow, i) == ifidx)) {220if (ring->blocked) {221currently_blocked = true;222break;223}224}225}226}227flow->rings[flowid]->blocked = blocked;228if (currently_blocked) {229spin_unlock_irqrestore(&flow->block_lock, flags);230return;231}232233bus_if = dev_get_drvdata(flow->dev);234drvr = bus_if->drvr;235ifp = brcmf_get_ifp(drvr, ifidx);236brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked);237238spin_unlock_irqrestore(&flow->block_lock, flags);239}240241242void brcmf_flowring_delete(struct brcmf_flowring *flow, u16 flowid)243{244struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);245struct brcmf_flowring_ring *ring;246struct brcmf_if *ifp;247u16 hash_idx;248u8 ifidx;249struct sk_buff *skb;250251ring = flow->rings[flowid];252if (!ring)253return;254255ifidx = brcmf_flowring_ifidx_get(flow, flowid);256ifp = brcmf_get_ifp(bus_if->drvr, ifidx);257258brcmf_flowring_block(flow, flowid, false);259hash_idx = ring->hash_id;260flow->hash[hash_idx].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;261eth_zero_addr(flow->hash[hash_idx].mac);262flow->rings[flowid] = NULL;263264skb = skb_dequeue(&ring->skblist);265while (skb) {266brcmf_txfinalize(ifp, skb, false);267skb = skb_dequeue(&ring->skblist);268}269270kfree(ring);271}272273274u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u16 flowid,275struct sk_buff *skb)276{277struct brcmf_flowring_ring *ring;278279ring = flow->rings[flowid];280281skb_queue_tail(&ring->skblist, skb);282283if (!ring->blocked &&284(skb_queue_len(&ring->skblist) > BRCMF_FLOWRING_HIGH)) {285brcmf_flowring_block(flow, flowid, true);286brcmf_dbg(MSGBUF, "Flowcontrol: BLOCK for ring %d\n", flowid);287/* To prevent (work around) possible race condition, check288* queue len again. It is also possible to use locking to289* protect, but that is undesirable for every enqueue and290* dequeue. This simple check will solve a possible race291* condition if it occurs.292*/293if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)294brcmf_flowring_block(flow, flowid, false);295}296return skb_queue_len(&ring->skblist);297}298299300struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u16 flowid)301{302struct brcmf_flowring_ring *ring;303struct sk_buff *skb;304305ring = flow->rings[flowid];306if (ring->status != RING_OPEN)307return NULL;308309skb = skb_dequeue(&ring->skblist);310311if (ring->blocked &&312(skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW)) {313brcmf_flowring_block(flow, flowid, false);314brcmf_dbg(MSGBUF, "Flowcontrol: OPEN for ring %d\n", flowid);315}316317return skb;318}319320321void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u16 flowid,322struct sk_buff *skb)323{324struct brcmf_flowring_ring *ring;325326ring = flow->rings[flowid];327328skb_queue_head(&ring->skblist, skb);329}330331332u32 brcmf_flowring_qlen(struct brcmf_flowring *flow, u16 flowid)333{334struct brcmf_flowring_ring *ring;335336ring = flow->rings[flowid];337if (!ring)338return 0;339340if (ring->status != RING_OPEN)341return 0;342343return skb_queue_len(&ring->skblist);344}345346347void brcmf_flowring_open(struct brcmf_flowring *flow, u16 flowid)348{349struct brcmf_flowring_ring *ring;350351ring = flow->rings[flowid];352if (!ring) {353brcmf_err("Ring NULL, for flowid %d\n", flowid);354return;355}356357ring->status = RING_OPEN;358}359360361u8 brcmf_flowring_ifidx_get(struct brcmf_flowring *flow, u16 flowid)362{363struct brcmf_flowring_ring *ring;364u16 hash_idx;365366ring = flow->rings[flowid];367hash_idx = ring->hash_id;368369return flow->hash[hash_idx].ifidx;370}371372373struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings)374{375struct brcmf_flowring *flow;376u32 i;377378flow = kzalloc(sizeof(*flow), GFP_KERNEL);379if (flow) {380flow->dev = dev;381flow->nrofrings = nrofrings;382spin_lock_init(&flow->block_lock);383for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++)384flow->addr_mode[i] = ADDR_INDIRECT;385for (i = 0; i < ARRAY_SIZE(flow->hash); i++)386flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;387flow->rings = kcalloc(nrofrings, sizeof(*flow->rings),388GFP_KERNEL);389if (!flow->rings) {390kfree(flow);391flow = NULL;392}393}394395return flow;396}397398399void brcmf_flowring_detach(struct brcmf_flowring *flow)400{401struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);402struct brcmf_pub *drvr = bus_if->drvr;403struct brcmf_flowring_tdls_entry *search;404struct brcmf_flowring_tdls_entry *remove;405u16 flowid;406407for (flowid = 0; flowid < flow->nrofrings; flowid++) {408if (flow->rings[flowid])409brcmf_msgbuf_delete_flowring(drvr, flowid);410}411412search = flow->tdls_entry;413while (search) {414remove = search;415search = search->next;416kfree(remove);417}418kfree(flow->rings);419kfree(flow);420}421422423void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,424enum proto_addr_mode addr_mode)425{426struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);427struct brcmf_pub *drvr = bus_if->drvr;428u32 i;429u16 flowid;430431if (flow->addr_mode[ifidx] != addr_mode) {432for (i = 0; i < ARRAY_SIZE(flow->hash); i++) {433if (flow->hash[i].ifidx == ifidx) {434flowid = flow->hash[i].flowid;435if (flow->rings[flowid]->status != RING_OPEN)436continue;437brcmf_msgbuf_delete_flowring(drvr, flowid);438}439}440flow->addr_mode[ifidx] = addr_mode;441}442}443444445void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx,446#if defined(__linux__)447u8 peer[ETH_ALEN])448#elif defined(__FreeBSD__)449const u8 peer[ETH_ALEN])450#endif451{452struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);453struct brcmf_pub *drvr = bus_if->drvr;454struct brcmf_flowring_hash *hash;455struct brcmf_flowring_tdls_entry *prev;456struct brcmf_flowring_tdls_entry *search;457u32 i;458u16 flowid;459bool sta;460461sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);462463search = flow->tdls_entry;464prev = NULL;465while (search) {466if (memcmp(search->mac, peer, ETH_ALEN) == 0) {467sta = false;468break;469}470prev = search;471search = search->next;472}473474hash = flow->hash;475for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {476if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) &&477(hash[i].ifidx == ifidx)) {478flowid = flow->hash[i].flowid;479if (flow->rings[flowid]->status == RING_OPEN)480brcmf_msgbuf_delete_flowring(drvr, flowid);481}482}483484if (search) {485if (prev)486prev->next = search->next;487else488flow->tdls_entry = search->next;489kfree(search);490if (flow->tdls_entry == NULL)491flow->tdls_active = false;492}493}494495496void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx,497#if defined(__linux__)498u8 peer[ETH_ALEN])499#elif defined(__FreeBSD__)500const u8 peer[ETH_ALEN])501#endif502{503struct brcmf_flowring_tdls_entry *tdls_entry;504struct brcmf_flowring_tdls_entry *search;505506tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC);507if (tdls_entry == NULL)508return;509510memcpy(tdls_entry->mac, peer, ETH_ALEN);511tdls_entry->next = NULL;512if (flow->tdls_entry == NULL) {513flow->tdls_entry = tdls_entry;514} else {515search = flow->tdls_entry;516if (memcmp(search->mac, peer, ETH_ALEN) == 0)517goto free_entry;518while (search->next) {519search = search->next;520if (memcmp(search->mac, peer, ETH_ALEN) == 0)521goto free_entry;522}523search->next = tdls_entry;524}525526flow->tdls_active = true;527return;528529free_entry:530kfree(tdls_entry);531}532533534