Introduce XDP in mt76 driver. Add mt76_xdp and mt76x02_xdp_setup routines in order to initialize xdp rx driver hook with the bpf program received from mac80211. Introduce the mt76_dma_rx_xdp routine in order to run the attached bpf program and parse the action result. Currently supported actions are: - XDP_PASS - XDP_ABORTED - XDP_DROP Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@xxxxxxxxxx> --- drivers/net/wireless/mediatek/mt76/dma.c | 46 ++++++++++ drivers/net/wireless/mediatek/mt76/mac80211.c | 18 ++++ drivers/net/wireless/mediatek/mt76/mt76.h | 6 ++ .../net/wireless/mediatek/mt76/mt76x0/pci.c | 3 + drivers/net/wireless/mediatek/mt76/mt76x02.h | 3 + .../net/wireless/mediatek/mt76/mt76x02_mac.h | 1 + .../net/wireless/mediatek/mt76/mt76x02_mmio.c | 90 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt76x02_util.c | 10 ++- .../wireless/mediatek/mt76/mt76x2/pci_init.c | 2 + .../wireless/mediatek/mt76/mt76x2/pci_main.c | 1 + 10 files changed, 179 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index efa0eab7cf01..b735849799e3 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -15,6 +15,7 @@ */ #include <linux/dma-mapping.h> +#include <linux/bpf_trace.h> #include "mt76.h" #include "dma.h" @@ -421,6 +422,44 @@ mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data, dev->drv->rx_skb(dev, q - dev->q_rx, skb); } +static bool +mt76_dma_rx_xdp(struct mt76_dev *dev, unsigned char *data, + int *len) +{ + struct page *page = virt_to_head_page(data); + struct xdp_buff xdp; + u32 action; + + if (!dev->drv->xdp_rxq_info_lookup || + dev->drv->xdp_rxq_info_lookup(dev, data, &xdp) < 0) + return false; + + xdp.data_hard_start = page_address(page); + xdp.data = (void *)data; + xdp_set_data_meta_invalid(&xdp); + xdp.data_end = xdp.data + *len; + + action = bpf_prog_run_xdp(dev->xdp_prog, &xdp); + /* bpf program chan modify the original frame */ + *len = xdp.data_end - xdp.data; + data = xdp.data; + + switch (action) { + case XDP_PASS: + return false; + default: + bpf_warn_invalid_xdp_action(action); + /* fall through */ + case XDP_ABORTED: + trace_xdp_exception(xdp.rxq->dev, dev->xdp_prog, action); + /* fall through */ + case XDP_DROP: + /* XXX: we can reuse the buffer here */ + skb_free_frag(data); + return true; + } +} + static int mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) { @@ -437,6 +476,13 @@ mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) if (!data) break; + /* xdp does not support fragmentes frames */ + if (dev->xdp_prog && !more && + mt76_dma_rx_xdp(dev, data, &len)) { + done++; + continue; + } + if (q->rx_head) { mt76_add_fragment(dev, q, data, len, more); continue; diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index b33074cf7e00..387c6bba80ca 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -707,3 +707,21 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return 0; } EXPORT_SYMBOL_GPL(mt76_sta_state); + +int mt76_xdp(struct ieee80211_hw *hw, struct netdev_bpf *xdp) +{ + struct mt76_dev *dev = hw->priv; + + switch (xdp->command) { + case XDP_SETUP_PROG: + if (!dev->drv->xdp_setup) + return -ENOTSUPP; + return dev->drv->xdp_setup(dev, xdp->prog); + case XDP_QUERY_PROG: + xdp->prog_id = dev->xdp_prog ? dev->xdp_prog->aux->id : 0; + return 0; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(mt76_xdp); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 70924792d870..b46e162dbb80 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -295,6 +295,9 @@ struct mt76_driver_ops { void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta); + int (*xdp_setup)(struct mt76_dev *dev, struct bpf_prog *prog); + int (*xdp_rxq_info_lookup)(struct mt76_dev *dev, unsigned char *data, + struct xdp_buff *xdp); }; struct mt76_channel_state { @@ -461,6 +464,8 @@ struct mt76_dev { struct mt76_mmio mmio; struct mt76_usb usb; }; + + struct bpf_prog *xdp_prog; }; enum mt76_phy_type { @@ -737,5 +742,6 @@ void mt76u_queues_deinit(struct mt76_dev *dev); void mt76u_mcu_complete_urb(struct urb *urb); int mt76u_mcu_init_rx(struct mt76_dev *dev); void mt76u_mcu_deinit(struct mt76_dev *dev); +int mt76_xdp(struct ieee80211_hw *hw, struct netdev_bpf *xdp); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c index d895b6f3dc44..38272593f839 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -105,6 +105,7 @@ static const struct ieee80211_ops mt76x0e_ops = { .release_buffered_frames = mt76_release_buffered_frames, .set_coverage_class = mt76x02_set_coverage_class, .set_rts_threshold = mt76x02_set_rts_threshold, + .xdp = mt76_xdp, }; static int mt76x0e_register_device(struct mt76x02_dev *dev) @@ -163,6 +164,8 @@ mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id) .sta_ps = mt76x02_sta_ps, .sta_add = mt76x02_sta_add, .sta_remove = mt76x02_sta_remove, + .xdp_setup = mt76x02_xdp_setup, + .xdp_rxq_info_lookup = mt76x02_xdp_rxq_info_lookup, }; struct mt76x02_dev *dev; int ret; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index 3922854ffa06..c32185f226c3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -170,6 +170,9 @@ extern const u16 mt76x02_beacon_offsets[16]; void mt76x02_init_beacon_config(struct mt76x02_dev *dev); void mt76x02_set_irq_mask(struct mt76x02_dev *dev, u32 clear, u32 set); void mt76x02_mac_start(struct mt76x02_dev *dev); +int mt76x02_xdp_setup(struct mt76_dev *mdev, struct bpf_prog *prog); +int mt76x02_xdp_rxq_info_lookup(struct mt76_dev *mdev, unsigned char *data, + struct xdp_buff *xdp); void mt76x02_init_debugfs(struct mt76x02_dev *dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h index 4e597004c445..176625d23fb6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h @@ -38,6 +38,7 @@ struct mt76x02_tx_status { struct mt76x02_vif { struct mt76_wcid group_wcid; /* must be first */ + struct xdp_rxq_info xdp_rxq; u8 idx; }; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c index 66315410aebe..76fbf136d415 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c @@ -27,6 +27,12 @@ struct beacon_bc_data { struct sk_buff *tail[8]; }; +struct xdp_iter_data { + struct ieee80211_hdr *hdr; + struct mt76x02_dev *dev; + struct xdp_rxq_info **rxq_info; +}; + static void mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { @@ -384,3 +390,87 @@ void mt76x02_mac_start(struct mt76x02_dev *dev) MT_INT_TX_STAT); } EXPORT_SYMBOL_GPL(mt76x02_mac_start); + +int mt76x02_xdp_setup(struct mt76_dev *mdev, struct bpf_prog *prog) +{ + struct bpf_prog *old_prog; + struct mt76x02_dev *dev; + bool reset; + + if (prog) { + prog = bpf_prog_add(prog, 1); + if (IS_ERR(prog)) + return -EINVAL; + } + + mutex_lock(&mdev->mutex); + + reset = (!!mdev->xdp_prog ^ !!prog); + dev = container_of(mdev, struct mt76x02_dev, mt76); + + if (reset) { + mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX); + napi_disable(&mdev->napi[MT_RXQ_MAIN]); + mt76_queue_init_rx_reset(dev, MT_RXQ_MAIN); + } + + /* attach BPF program */ + old_prog = xchg(&mdev->xdp_prog, prog); + if (old_prog) + bpf_prog_put(old_prog); + + if (reset) { + struct mt76_queue *q = &mdev->q_rx[MT_RXQ_MAIN]; + + if (mdev->xdp_prog) + q->buf_size = PAGE_SIZE + XDP_PACKET_HEADROOM + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + else + q->buf_size = MT_RX_BUF_SIZE; + + mt76_queue_complete_rx_reset(dev, MT_RXQ_MAIN); + napi_enable(&mdev->napi[MT_RXQ_MAIN]); + mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX); + } + + mutex_unlock(&mdev->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_xdp_setup); + +static void +mt76x02_xdp_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct xdp_iter_data *data = (struct xdp_iter_data *)priv; + + if (ether_addr_equal(mac, data->hdr->addr1) || + (!data->rxq_info && is_multicast_ether_addr(data->hdr->addr1))) { + struct mt76x02_vif *mvif; + + mvif = (struct mt76x02_vif *)vif->drv_priv; + *data->rxq_info = &mvif->xdp_rxq; + } +} + +int mt76x02_xdp_rxq_info_lookup(struct mt76_dev *mdev, unsigned char *data, + struct xdp_buff *xdp) +{ + struct xdp_iter_data xdp_data = {}; + struct ieee80211_hdr *hdr; + struct mt76x02_dev *dev; + + hdr = (struct ieee80211_hdr *)(data + sizeof(struct mt76x02_rxwi)); + dev = container_of(mdev, struct mt76x02_dev, mt76); + + xdp_data.rxq_info = &xdp->rxq; + xdp_data.dev = dev; + xdp_data.hdr = hdr; + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76x02_xdp_iter, &xdp_data); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_xdp_rxq_info_lookup); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c index 87195076cf62..0270f7ef3002 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c @@ -211,6 +211,7 @@ int mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif, unsigned int idx) { struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + struct net_device *ndev = ieee80211_vif_to_netdev(vif); struct mt76_txq *mtxq; mvif->idx = idx; @@ -221,7 +222,7 @@ int mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif, mt76_txq_init(&dev->mt76, vif->txq); - return 0; + return ndev ? xdp_rxq_info_reg(&mvif->xdp_rxq, ndev, 0) : 0; } EXPORT_SYMBOL_GPL(mt76x02_vif_init); @@ -257,9 +258,16 @@ EXPORT_SYMBOL_GPL(mt76x02_add_interface); void mt76x02_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { + struct net_device *ndev = ieee80211_vif_to_netdev(vif); struct mt76x02_dev *dev = hw->priv; mt76_txq_remove(&dev->mt76, vif->txq); + if (ndev) { + struct mt76x02_vif *mvif; + + mvif = (struct mt76x02_vif *)vif->drv_priv; + xdp_rxq_info_unreg(&mvif->xdp_rxq); + } } EXPORT_SYMBOL_GPL(mt76x02_remove_interface); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c index 6eaab156387a..c6a60d6809f0 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c @@ -325,6 +325,8 @@ struct mt76x02_dev *mt76x2_alloc_device(struct device *pdev) .sta_ps = mt76x02_sta_ps, .sta_add = mt76x02_sta_add, .sta_remove = mt76x02_sta_remove, + .xdp_setup = mt76x02_xdp_setup, + .xdp_rxq_info_lookup = mt76x02_xdp_rxq_info_lookup, }; struct mt76x02_dev *dev; struct mt76_dev *mdev; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c index b54a32397486..6898f134e3fe 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c @@ -199,5 +199,6 @@ const struct ieee80211_ops mt76x2_ops = { .set_antenna = mt76x2_set_antenna, .get_antenna = mt76x2_get_antenna, .set_rts_threshold = mt76x02_set_rts_threshold, + .xdp = mt76_xdp, }; -- 2.19.1